click_based_category_ranker_unittest.cc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783
  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_snippets/category_rankers/click_based_category_ranker.h"
  5. #include <utility>
  6. #include "base/strings/string_number_conversions.h"
  7. #include "base/test/metrics/histogram_tester.h"
  8. #include "base/test/simple_test_clock.h"
  9. #include "base/time/default_clock.h"
  10. #include "base/time/time.h"
  11. #include "components/ntp_snippets/category.h"
  12. #include "components/ntp_snippets/category_rankers/constant_category_ranker.h"
  13. #include "components/ntp_snippets/ntp_snippets_constants.h"
  14. #include "components/ntp_snippets/time_serialization.h"
  15. #include "components/prefs/testing_pref_service.h"
  16. #include "testing/gmock/include/gmock/gmock.h"
  17. #include "testing/gtest/include/gtest/gtest.h"
  18. using testing::ElementsAre;
  19. using testing::IsEmpty;
  20. namespace ntp_snippets {
  21. namespace {
  22. const char kHistogramMovedUpCategoryNewIndex[] =
  23. "NewTabPage.ContentSuggestions.MovedUpCategoryNewIndex";
  24. } // namespace
  25. class ClickBasedCategoryRankerTest : public testing::Test {
  26. public:
  27. ClickBasedCategoryRankerTest()
  28. : pref_service_(std::make_unique<TestingPrefServiceSimple>()),
  29. unused_remote_category_id_(
  30. static_cast<int>(KnownCategories::LAST_KNOWN_REMOTE_CATEGORY) + 1) {
  31. ClickBasedCategoryRanker::RegisterProfilePrefs(pref_service_->registry());
  32. ranker_ = std::make_unique<ClickBasedCategoryRanker>(
  33. pref_service_.get(), base::DefaultClock::GetInstance());
  34. }
  35. ClickBasedCategoryRankerTest(const ClickBasedCategoryRankerTest&) = delete;
  36. ClickBasedCategoryRankerTest& operator=(const ClickBasedCategoryRankerTest&) =
  37. delete;
  38. int GetUnusedRemoteCategoryID() { return unused_remote_category_id_++; }
  39. Category GetUnusedRemoteCategory() {
  40. return Category::FromIDValue(GetUnusedRemoteCategoryID());
  41. }
  42. bool CompareCategories(const Category& left, const Category& right) {
  43. return ranker()->Compare(left, right);
  44. }
  45. Category AddUnusedRemoteCategory() {
  46. Category category = GetUnusedRemoteCategory();
  47. ranker()->AppendCategoryIfNecessary(category);
  48. return category;
  49. }
  50. void AddUnusedRemoteCategories(int quantity) {
  51. for (int i = 0; i < quantity; ++i) {
  52. AddUnusedRemoteCategory();
  53. }
  54. }
  55. void ResetRanker(base::Clock* clock) {
  56. ranker_ =
  57. std::make_unique<ClickBasedCategoryRanker>(pref_service_.get(), clock);
  58. }
  59. void NotifyOnSuggestionOpened(int times, Category category) {
  60. for (int i = 0; i < times; ++i) {
  61. ranker()->OnSuggestionOpened(category);
  62. }
  63. }
  64. void NotifyOnCategoryDismissed(Category category) {
  65. ranker()->OnCategoryDismissed(category);
  66. }
  67. std::vector<Category> ConvertKnownCategories(
  68. std::vector<KnownCategories> known_categories) {
  69. std::vector<Category> converted;
  70. for (auto known : known_categories) {
  71. converted.push_back(Category::FromKnownCategory(known));
  72. }
  73. return converted;
  74. }
  75. ClickBasedCategoryRanker* ranker() { return ranker_.get(); }
  76. private:
  77. std::unique_ptr<TestingPrefServiceSimple> pref_service_;
  78. int unused_remote_category_id_;
  79. std::unique_ptr<ClickBasedCategoryRanker> ranker_;
  80. };
  81. TEST_F(ClickBasedCategoryRankerTest, ShouldSortRemoteCategoriesByWhenAdded) {
  82. const Category first = GetUnusedRemoteCategory();
  83. const Category second = GetUnusedRemoteCategory();
  84. // Categories are added in decreasing id order to test that they are not
  85. // compared by id.
  86. ranker()->AppendCategoryIfNecessary(second);
  87. ranker()->AppendCategoryIfNecessary(first);
  88. EXPECT_TRUE(CompareCategories(second, first));
  89. EXPECT_FALSE(CompareCategories(first, second));
  90. }
  91. TEST_F(ClickBasedCategoryRankerTest, ShouldSortLocalCategoriesBeforeRemote) {
  92. const Category remote_category = AddUnusedRemoteCategory();
  93. const Category local_category =
  94. Category::FromKnownCategory(KnownCategories::READING_LIST);
  95. EXPECT_TRUE(CompareCategories(local_category, remote_category));
  96. EXPECT_FALSE(CompareCategories(remote_category, local_category));
  97. }
  98. TEST_F(ClickBasedCategoryRankerTest,
  99. CompareShouldReturnFalseForSameCategories) {
  100. const Category remote_category = AddUnusedRemoteCategory();
  101. EXPECT_FALSE(CompareCategories(remote_category, remote_category));
  102. const Category local_category =
  103. Category::FromKnownCategory(KnownCategories::READING_LIST);
  104. EXPECT_FALSE(CompareCategories(local_category, local_category));
  105. }
  106. TEST_F(ClickBasedCategoryRankerTest,
  107. AddingMoreRemoteCategoriesShouldNotChangePreviousOrder) {
  108. AddUnusedRemoteCategories(3);
  109. Category first = AddUnusedRemoteCategory();
  110. Category second = AddUnusedRemoteCategory();
  111. ASSERT_TRUE(CompareCategories(first, second));
  112. ASSERT_FALSE(CompareCategories(second, first));
  113. AddUnusedRemoteCategories(3);
  114. EXPECT_TRUE(CompareCategories(first, second));
  115. EXPECT_FALSE(CompareCategories(second, first));
  116. }
  117. TEST_F(ClickBasedCategoryRankerTest, ShouldChangeOrderOfNonTopCategories) {
  118. // Add dummy remote categories to ensure that the following categories are not
  119. // in the top anymore.
  120. AddUnusedRemoteCategories(
  121. ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
  122. Category first = AddUnusedRemoteCategory();
  123. Category second = AddUnusedRemoteCategory();
  124. ASSERT_TRUE(CompareCategories(first, second));
  125. ASSERT_FALSE(CompareCategories(second, first));
  126. NotifyOnSuggestionOpened(
  127. /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second);
  128. EXPECT_TRUE(CompareCategories(second, first));
  129. EXPECT_FALSE(CompareCategories(first, second));
  130. }
  131. TEST_F(ClickBasedCategoryRankerTest,
  132. ShouldNotChangeOrderRightAfterOrderChange) {
  133. // Add dummy remote categories to ensure that the following categories are not
  134. // in the top anymore.
  135. AddUnusedRemoteCategories(
  136. ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
  137. // Two non-top categories are added.
  138. Category first = AddUnusedRemoteCategory();
  139. Category second = AddUnusedRemoteCategory();
  140. ASSERT_TRUE(CompareCategories(first, second));
  141. ASSERT_FALSE(CompareCategories(second, first));
  142. // Their order is changed.
  143. NotifyOnSuggestionOpened(
  144. /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second);
  145. ASSERT_TRUE(CompareCategories(second, first));
  146. ASSERT_FALSE(CompareCategories(first, second));
  147. // Click on the lower category.
  148. NotifyOnSuggestionOpened(/*times=*/1, first);
  149. // Order should not change.
  150. EXPECT_TRUE(CompareCategories(second, first));
  151. EXPECT_FALSE(CompareCategories(first, second));
  152. }
  153. TEST_F(ClickBasedCategoryRankerTest,
  154. ShouldNotMoveCategoryMoreThanOncePerClick) {
  155. // Add dummy remote categories to ensure that the following categories are not
  156. // in the top anymore.
  157. AddUnusedRemoteCategories(
  158. ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
  159. // Non-top categories are added.
  160. Category first = AddUnusedRemoteCategory();
  161. Category second = AddUnusedRemoteCategory();
  162. Category third = AddUnusedRemoteCategory();
  163. // Move the third category up.
  164. NotifyOnSuggestionOpened(
  165. /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), third);
  166. EXPECT_TRUE(CompareCategories(third, second));
  167. // But only on one position even though the first category has low counts.
  168. EXPECT_TRUE(CompareCategories(first, third));
  169. // However, another click must move it further.
  170. NotifyOnSuggestionOpened(/*times=*/1, third);
  171. EXPECT_TRUE(CompareCategories(third, first));
  172. }
  173. TEST_F(ClickBasedCategoryRankerTest,
  174. ShouldNotMoveTopCategoryRightAfterThreshold) {
  175. ASSERT_GE(ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin(), 1);
  176. // At least one top category is added from the default order.
  177. std::vector<KnownCategories> default_order =
  178. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  179. Category first = Category::FromKnownCategory(default_order[0]);
  180. Category second = Category::FromKnownCategory(default_order[1]);
  181. // Try to move the second category up as if the first category was non-top.
  182. NotifyOnSuggestionOpened(
  183. /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second);
  184. // Nothing should change, because the first category is top.
  185. EXPECT_TRUE(CompareCategories(first, second));
  186. }
  187. TEST_F(ClickBasedCategoryRankerTest, ShouldPersistOrderAndClicksWhenRestarted) {
  188. // Add dummy remote categories to ensure that the following categories are not
  189. // in the top anymore.
  190. AddUnusedRemoteCategories(
  191. ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
  192. // Non-top categories are added.
  193. Category first = AddUnusedRemoteCategory();
  194. Category second = AddUnusedRemoteCategory();
  195. Category third = AddUnusedRemoteCategory();
  196. // Change the order.
  197. NotifyOnSuggestionOpened(
  198. /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), third);
  199. ASSERT_TRUE(CompareCategories(third, second));
  200. ASSERT_TRUE(CompareCategories(first, third));
  201. // Simulate Chrome restart.
  202. ResetRanker(base::DefaultClock::GetInstance());
  203. // The old order must be preserved.
  204. EXPECT_TRUE(CompareCategories(third, second));
  205. // Clicks must be preserved as well.
  206. NotifyOnSuggestionOpened(/*times=*/1, third);
  207. EXPECT_TRUE(CompareCategories(third, first));
  208. }
  209. TEST_F(ClickBasedCategoryRankerTest, ShouldDecayClickCountsWithTime) {
  210. // Add dummy remote categories to ensure that the following categories are not
  211. // in the top anymore.
  212. AddUnusedRemoteCategories(
  213. ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
  214. // Non-top categories are added.
  215. Category first = AddUnusedRemoteCategory();
  216. Category second = AddUnusedRemoteCategory();
  217. const int first_clicks = 10 * ClickBasedCategoryRanker::GetPassingMargin();
  218. // Simulate the user using the first category for a long time (and not using
  219. // anything else).
  220. NotifyOnSuggestionOpened(/*times=*/first_clicks, first);
  221. // Let multiple years pass by.
  222. base::SimpleTestClock test_clock;
  223. test_clock.SetNow(base::Time::Now() + base::Days(1000));
  224. // Reset the ranker to pick up the new clock.
  225. ResetRanker(&test_clock);
  226. // The user behavior changes and they start using the second category instead.
  227. // According to our requirenments after such a long time it should take less
  228. // than |first_clicks| for the second category to outperform the first one.
  229. int second_clicks = 0;
  230. while (CompareCategories(first, second) && second_clicks < first_clicks) {
  231. NotifyOnSuggestionOpened(/*times=*/1, second);
  232. second_clicks++;
  233. }
  234. EXPECT_THAT(second_clicks, testing::Lt(first_clicks));
  235. }
  236. TEST_F(ClickBasedCategoryRankerTest, ShouldDecayAfterClearHistory) {
  237. std::vector<KnownCategories> default_order =
  238. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  239. Category first = Category::FromKnownCategory(default_order[0]);
  240. Category second = Category::FromKnownCategory(default_order[1]);
  241. // The user clears entire history.
  242. ranker()->ClearHistory(/*begin=*/base::Time(),
  243. /*end=*/base::Time::Max());
  244. // Check whether decay happens by clicking on the first category and
  245. // waiting.
  246. const int first_clicks = 10 * ClickBasedCategoryRanker::GetPassingMargin();
  247. NotifyOnSuggestionOpened(/*times=*/first_clicks, first);
  248. // Let multiple years pass by.
  249. base::SimpleTestClock test_clock;
  250. test_clock.SetNow(base::Time::Now() + base::Days(1000));
  251. // Reset the ranker to pick up the new clock.
  252. ResetRanker(&test_clock);
  253. // It should take less than |first_clicks| for the second category to
  254. // overtake because of decays.
  255. int second_clicks = 0;
  256. while (CompareCategories(first, second) && second_clicks < first_clicks) {
  257. NotifyOnSuggestionOpened(/*times=*/1, second);
  258. second_clicks++;
  259. }
  260. EXPECT_THAT(second_clicks, testing::Lt(first_clicks));
  261. }
  262. TEST_F(ClickBasedCategoryRankerTest, ShouldRemoveLastDecayTimeOnClearHistory) {
  263. ASSERT_NE(ranker()->GetLastDecayTime(), DeserializeTime(0));
  264. // The user clears entire history.
  265. ranker()->ClearHistory(/*begin=*/base::Time(),
  266. /*end=*/base::Time::Max());
  267. EXPECT_EQ(ranker()->GetLastDecayTime(), DeserializeTime(0));
  268. }
  269. TEST_F(ClickBasedCategoryRankerTest, ShouldPersistLastDecayTimeWhenRestarted) {
  270. base::Time before = ranker()->GetLastDecayTime();
  271. ASSERT_NE(before, DeserializeTime(0));
  272. // Ensure that |Now()| is different from |before| by injecting our clock.
  273. base::SimpleTestClock test_clock;
  274. test_clock.SetNow(base::Time::Now() + base::Seconds(10));
  275. ResetRanker(&test_clock);
  276. EXPECT_EQ(before, ranker()->GetLastDecayTime());
  277. }
  278. TEST_F(ClickBasedCategoryRankerTest, ShouldMoveCategoryDownWhenDismissed) {
  279. // Take top categories.
  280. std::vector<KnownCategories> default_order =
  281. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  282. Category first = Category::FromKnownCategory(default_order[0]);
  283. Category second = Category::FromKnownCategory(default_order[1]);
  284. ASSERT_TRUE(CompareCategories(first, second));
  285. NotifyOnCategoryDismissed(first);
  286. EXPECT_FALSE(CompareCategories(first, second));
  287. }
  288. TEST_F(ClickBasedCategoryRankerTest,
  289. ShouldMoveSecondToLastCategoryDownWhenDismissed) {
  290. // Add categories to the bottom.
  291. Category first = AddUnusedRemoteCategory();
  292. Category second = AddUnusedRemoteCategory();
  293. ASSERT_TRUE(CompareCategories(first, second));
  294. NotifyOnCategoryDismissed(first);
  295. EXPECT_FALSE(CompareCategories(first, second));
  296. }
  297. TEST_F(ClickBasedCategoryRankerTest,
  298. ShouldNotMoveCategoryTooMuchDownWhenDismissed) {
  299. // Add enough categories to the end.
  300. std::vector<Category> categories;
  301. const int penalty = ClickBasedCategoryRanker::GetDismissedCategoryPenalty();
  302. for (int i = 0; i < 2 * penalty + 10; ++i) {
  303. categories.push_back(AddUnusedRemoteCategory());
  304. }
  305. const int target = penalty + 1;
  306. Category target_category = categories[target];
  307. for (int i = 0; i < static_cast<int>(categories.size()); ++i) {
  308. ASSERT_EQ(i < target, CompareCategories(categories[i], target_category));
  309. }
  310. // This should move exactly |penalty| categories up.
  311. NotifyOnCategoryDismissed(categories[target]);
  312. // Reflect expected change in |categories|.
  313. const int expected = target + penalty;
  314. for (int i = target; i + 1 <= expected; ++i) {
  315. std::swap(categories[i], categories[i + 1]);
  316. }
  317. for (int i = 0; i < static_cast<int>(categories.size()); ++i) {
  318. EXPECT_EQ(i < expected, CompareCategories(categories[i], target_category));
  319. }
  320. }
  321. TEST_F(ClickBasedCategoryRankerTest,
  322. ShouldNotChangeOrderOfOtherCategoriesWhenDismissed) {
  323. // Add enough categories to the end.
  324. std::vector<Category> categories;
  325. const int penalty = ClickBasedCategoryRanker::GetDismissedCategoryPenalty();
  326. for (int i = 0; i < 2 * penalty + 10; ++i) {
  327. categories.push_back(AddUnusedRemoteCategory());
  328. }
  329. int target = penalty + 1;
  330. // This should not change order of all other categories.
  331. NotifyOnCategoryDismissed(categories[target]);
  332. categories.erase(categories.begin() + target);
  333. for (int first = 0; first < static_cast<int>(categories.size()); ++first) {
  334. for (int second = 0; second < static_cast<int>(categories.size());
  335. ++second) {
  336. EXPECT_EQ(first < second,
  337. CompareCategories(categories[first], categories[second]));
  338. }
  339. }
  340. }
  341. TEST_F(ClickBasedCategoryRankerTest, ShouldNotMoveLastCategoryWhenDismissed) {
  342. Category first = AddUnusedRemoteCategory();
  343. Category second = AddUnusedRemoteCategory();
  344. ASSERT_TRUE(CompareCategories(first, second));
  345. NotifyOnCategoryDismissed(second);
  346. EXPECT_TRUE(CompareCategories(first, second));
  347. }
  348. TEST_F(ClickBasedCategoryRankerTest, ShouldRestoreDefaultOrderOnClearHistory) {
  349. std::vector<KnownCategories> default_order =
  350. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  351. Category first = Category::FromKnownCategory(default_order[0]);
  352. Category second = Category::FromKnownCategory(default_order[1]);
  353. ASSERT_TRUE(CompareCategories(first, second));
  354. // Change the order.
  355. while (CompareCategories(first, second)) {
  356. NotifyOnSuggestionOpened(
  357. /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second);
  358. }
  359. ASSERT_FALSE(CompareCategories(first, second));
  360. // The user clears history.
  361. ranker()->ClearHistory(/*begin=*/base::Time(),
  362. /*end=*/base::Time::Max());
  363. // The default order must be restored.
  364. EXPECT_TRUE(CompareCategories(first, second));
  365. }
  366. TEST_F(ClickBasedCategoryRankerTest,
  367. ShouldPreserveRemoteCategoriesOnClearHistory) {
  368. Category first = AddUnusedRemoteCategory();
  369. Category second = AddUnusedRemoteCategory();
  370. ASSERT_TRUE(CompareCategories(first, second));
  371. // The user clears history.
  372. ranker()->ClearHistory(/*begin=*/base::Time(),
  373. /*end=*/base::Time::Max());
  374. // The order does not matter, but the ranker should not die.
  375. CompareCategories(first, second);
  376. }
  377. TEST_F(ClickBasedCategoryRankerTest, ShouldIgnorePartialClearHistory) {
  378. Category first = AddUnusedRemoteCategory();
  379. Category second = AddUnusedRemoteCategory();
  380. ASSERT_TRUE(CompareCategories(first, second));
  381. // Change the order.
  382. while (CompareCategories(first, second)) {
  383. NotifyOnSuggestionOpened(
  384. /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second);
  385. }
  386. ASSERT_FALSE(CompareCategories(first, second));
  387. // The user partially clears history.
  388. base::Time begin = base::Time::Now() - base::Hours(1),
  389. end = base::Time::Max();
  390. ranker()->ClearHistory(begin, end);
  391. // The order should not be cleared.
  392. EXPECT_FALSE(CompareCategories(first, second));
  393. }
  394. TEST_F(ClickBasedCategoryRankerTest,
  395. ShouldEmitNewIndexWhenCategoryMovedUpDueToClick) {
  396. base::HistogramTester histogram_tester;
  397. std::vector<KnownCategories> default_order =
  398. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  399. Category first = Category::FromKnownCategory(default_order[0]);
  400. Category second = Category::FromKnownCategory(default_order[1]);
  401. ASSERT_TRUE(CompareCategories(first, second));
  402. // Increase the score of |second| until the order changes.
  403. while (CompareCategories(first, second)) {
  404. EXPECT_THAT(
  405. histogram_tester.GetAllSamples(kHistogramMovedUpCategoryNewIndex),
  406. IsEmpty());
  407. ranker()->OnSuggestionOpened(second);
  408. }
  409. ASSERT_FALSE(CompareCategories(first, second));
  410. EXPECT_THAT(histogram_tester.GetAllSamples(kHistogramMovedUpCategoryNewIndex),
  411. ElementsAre(base::Bucket(/*min=*/0, /*count=*/1)));
  412. }
  413. TEST_F(ClickBasedCategoryRankerTest,
  414. ShouldNotEmitNewIndexWhenCategoryDismissed) {
  415. base::HistogramTester histogram_tester;
  416. std::vector<KnownCategories> default_order =
  417. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  418. Category category = Category::FromKnownCategory(default_order[0]);
  419. ASSERT_THAT(histogram_tester.GetAllSamples(kHistogramMovedUpCategoryNewIndex),
  420. IsEmpty());
  421. NotifyOnCategoryDismissed(category);
  422. EXPECT_THAT(histogram_tester.GetAllSamples(kHistogramMovedUpCategoryNewIndex),
  423. IsEmpty());
  424. }
  425. TEST_F(ClickBasedCategoryRankerTest,
  426. ShouldNotEmitNewIndexOfMovedUpCategoryWhenHistoryCleared) {
  427. std::vector<KnownCategories> default_order =
  428. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  429. Category first = Category::FromKnownCategory(default_order[0]);
  430. Category second = Category::FromKnownCategory(default_order[1]);
  431. ASSERT_TRUE(CompareCategories(first, second));
  432. // Increase the score of |second| until the order changes.
  433. while (CompareCategories(first, second)) {
  434. ranker()->OnSuggestionOpened(second);
  435. }
  436. ASSERT_FALSE(CompareCategories(first, second));
  437. // The histogram tester is created here to ignore previous events.
  438. base::HistogramTester histogram_tester;
  439. ranker()->ClearHistory(/*begin=*/base::Time(),
  440. /*end=*/base::Time::Max());
  441. // ClearHistory should restore the default order.
  442. ASSERT_TRUE(CompareCategories(first, second));
  443. EXPECT_THAT(histogram_tester.GetAllSamples(kHistogramMovedUpCategoryNewIndex),
  444. IsEmpty());
  445. }
  446. TEST_F(ClickBasedCategoryRankerTest,
  447. ShouldInsertCategoryBeforeSelectedCategory) {
  448. std::vector<KnownCategories> default_order =
  449. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  450. Category first = Category::FromKnownCategory(default_order[0]);
  451. Category second = Category::FromKnownCategory(default_order[1]);
  452. ASSERT_TRUE(CompareCategories(first, second));
  453. Category inserted = GetUnusedRemoteCategory();
  454. ranker()->InsertCategoryBeforeIfNecessary(inserted, second);
  455. EXPECT_TRUE(CompareCategories(first, inserted));
  456. EXPECT_TRUE(CompareCategories(inserted, second));
  457. }
  458. TEST_F(ClickBasedCategoryRankerTest,
  459. ShouldInsertMultipleCategoriesBeforeSelectedCategory) {
  460. std::vector<KnownCategories> default_order =
  461. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  462. Category first = Category::FromKnownCategory(default_order[0]);
  463. Category second = Category::FromKnownCategory(default_order[1]);
  464. ASSERT_TRUE(CompareCategories(first, second));
  465. Category first_inserted = GetUnusedRemoteCategory();
  466. Category second_inserted = GetUnusedRemoteCategory();
  467. ranker()->InsertCategoryBeforeIfNecessary(first_inserted, second);
  468. ranker()->InsertCategoryBeforeIfNecessary(second_inserted, second);
  469. EXPECT_TRUE(CompareCategories(first, first_inserted));
  470. EXPECT_TRUE(CompareCategories(first_inserted, second_inserted));
  471. EXPECT_TRUE(CompareCategories(second_inserted, second));
  472. }
  473. TEST_F(ClickBasedCategoryRankerTest, ShouldInsertCategoryBeforeFirstCategory) {
  474. std::vector<KnownCategories> default_order =
  475. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  476. Category first = Category::FromKnownCategory(default_order[0]);
  477. Category inserted = GetUnusedRemoteCategory();
  478. ranker()->InsertCategoryBeforeIfNecessary(inserted, first);
  479. EXPECT_TRUE(CompareCategories(inserted, first));
  480. }
  481. TEST_F(ClickBasedCategoryRankerTest, ShouldInsertCategoryBeforeRemoteCategory) {
  482. std::vector<KnownCategories> default_order =
  483. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  484. Category remote = AddUnusedRemoteCategory();
  485. Category inserted = GetUnusedRemoteCategory();
  486. ranker()->InsertCategoryBeforeIfNecessary(inserted, remote);
  487. EXPECT_TRUE(CompareCategories(inserted, remote));
  488. }
  489. TEST_F(ClickBasedCategoryRankerTest,
  490. ShouldNotChangeRemainingOrderWhenInsertingBeforeCategory) {
  491. std::vector<KnownCategories> default_order =
  492. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  493. Category anchor = Category::FromKnownCategory(default_order[0]);
  494. Category inserted = GetUnusedRemoteCategory();
  495. ranker()->InsertCategoryBeforeIfNecessary(inserted, anchor);
  496. std::vector<Category> converted_categories =
  497. ConvertKnownCategories(default_order);
  498. for (size_t i = 0; i + 1 < converted_categories.size(); ++i) {
  499. EXPECT_TRUE(CompareCategories(converted_categories[i],
  500. converted_categories[i + 1]));
  501. }
  502. }
  503. TEST_F(ClickBasedCategoryRankerTest,
  504. ShouldInsertCategoriesBeforeAndAfterSameCategory) {
  505. std::vector<KnownCategories> default_order =
  506. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  507. Category first = Category::FromKnownCategory(default_order[0]);
  508. Category second = Category::FromKnownCategory(default_order[1]);
  509. ASSERT_TRUE(CompareCategories(first, second));
  510. Category first_before = GetUnusedRemoteCategory();
  511. ranker()->InsertCategoryBeforeIfNecessary(first_before, second);
  512. Category first_after = GetUnusedRemoteCategory();
  513. ranker()->InsertCategoryAfterIfNecessary(first_after, second);
  514. Category second_before = GetUnusedRemoteCategory();
  515. ranker()->InsertCategoryBeforeIfNecessary(second_before, second);
  516. Category second_after = GetUnusedRemoteCategory();
  517. ranker()->InsertCategoryAfterIfNecessary(second_after, second);
  518. EXPECT_TRUE(CompareCategories(first_before, second_before));
  519. EXPECT_TRUE(CompareCategories(second_before, second));
  520. EXPECT_TRUE(CompareCategories(second, second_after));
  521. EXPECT_TRUE(CompareCategories(second_after, first_after));
  522. }
  523. TEST_F(ClickBasedCategoryRankerTest,
  524. ShouldInsertCategoriesBeforeAndAfterDifferentCategories) {
  525. std::vector<KnownCategories> default_order =
  526. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  527. Category first = Category::FromKnownCategory(default_order[0]);
  528. Category second = Category::FromKnownCategory(default_order[1]);
  529. ASSERT_TRUE(CompareCategories(first, second));
  530. Category first_before = GetUnusedRemoteCategory();
  531. ranker()->InsertCategoryBeforeIfNecessary(first_before, second);
  532. Category first_after = GetUnusedRemoteCategory();
  533. ranker()->InsertCategoryAfterIfNecessary(first_after, first);
  534. Category second_before = GetUnusedRemoteCategory();
  535. ranker()->InsertCategoryBeforeIfNecessary(second_before, second);
  536. Category second_after = GetUnusedRemoteCategory();
  537. ranker()->InsertCategoryAfterIfNecessary(second_after, first);
  538. EXPECT_TRUE(CompareCategories(first, second_after));
  539. EXPECT_TRUE(CompareCategories(second_after, first_after));
  540. EXPECT_TRUE(CompareCategories(first_after, first_before));
  541. EXPECT_TRUE(CompareCategories(first_before, second_before));
  542. EXPECT_TRUE(CompareCategories(second_before, second));
  543. }
  544. TEST_F(ClickBasedCategoryRankerTest,
  545. ShouldNotEmitNewIndexWhenCategoryInserted) {
  546. base::HistogramTester histogram_tester;
  547. std::vector<KnownCategories> default_order =
  548. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  549. Category first = Category::FromKnownCategory(default_order[0]);
  550. ASSERT_THAT(histogram_tester.GetAllSamples(kHistogramMovedUpCategoryNewIndex),
  551. IsEmpty());
  552. Category before = GetUnusedRemoteCategory();
  553. ranker()->InsertCategoryBeforeIfNecessary(before, first);
  554. Category after = GetUnusedRemoteCategory();
  555. ranker()->InsertCategoryAfterIfNecessary(after, first);
  556. EXPECT_THAT(histogram_tester.GetAllSamples(kHistogramMovedUpCategoryNewIndex),
  557. IsEmpty());
  558. }
  559. // TODO(vitaliii): Reuse these tests for ConstantCategoryRanker.
  560. TEST_F(ClickBasedCategoryRankerTest,
  561. ShouldInsertCategoryAfterSelectedCategory) {
  562. std::vector<KnownCategories> default_order =
  563. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  564. Category first = Category::FromKnownCategory(default_order[0]);
  565. Category second = Category::FromKnownCategory(default_order[1]);
  566. ASSERT_TRUE(CompareCategories(first, second));
  567. Category inserted = GetUnusedRemoteCategory();
  568. ranker()->InsertCategoryAfterIfNecessary(inserted, first);
  569. EXPECT_TRUE(CompareCategories(first, inserted));
  570. EXPECT_TRUE(CompareCategories(inserted, second));
  571. }
  572. TEST_F(ClickBasedCategoryRankerTest,
  573. ShouldInsertMultipleCategoriesAfterSelectedCategory) {
  574. std::vector<KnownCategories> default_order =
  575. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  576. Category first = Category::FromKnownCategory(default_order[0]);
  577. Category second = Category::FromKnownCategory(default_order[1]);
  578. ASSERT_TRUE(CompareCategories(first, second));
  579. Category first_inserted = GetUnusedRemoteCategory();
  580. Category second_inserted = GetUnusedRemoteCategory();
  581. ranker()->InsertCategoryAfterIfNecessary(first_inserted, first);
  582. ranker()->InsertCategoryAfterIfNecessary(second_inserted, first);
  583. EXPECT_TRUE(CompareCategories(first, second_inserted));
  584. EXPECT_TRUE(CompareCategories(second_inserted, first_inserted));
  585. EXPECT_TRUE(CompareCategories(first_inserted, second));
  586. }
  587. TEST_F(ClickBasedCategoryRankerTest, ShouldInsertCategoryAfterLastCategory) {
  588. Category last = AddUnusedRemoteCategory();
  589. Category inserted = GetUnusedRemoteCategory();
  590. ranker()->InsertCategoryAfterIfNecessary(inserted, last);
  591. EXPECT_TRUE(CompareCategories(last, inserted));
  592. }
  593. TEST_F(ClickBasedCategoryRankerTest,
  594. ShouldNotChangeRemainingOrderWhenInsertingAfterCategory) {
  595. std::vector<KnownCategories> default_order =
  596. ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
  597. Category anchor = Category::FromKnownCategory(default_order[0]);
  598. Category inserted = GetUnusedRemoteCategory();
  599. ranker()->InsertCategoryAfterIfNecessary(inserted, anchor);
  600. std::vector<Category> converted_categories =
  601. ConvertKnownCategories(default_order);
  602. for (size_t i = 0; i + 1 < converted_categories.size(); ++i) {
  603. EXPECT_TRUE(CompareCategories(converted_categories[i],
  604. converted_categories[i + 1]));
  605. }
  606. }
  607. TEST_F(ClickBasedCategoryRankerTest,
  608. ShouldAssignScoreToInsertedCategoriesBasedOnAnchor) {
  609. Category anchor = AddUnusedRemoteCategory();
  610. NotifyOnSuggestionOpened(/*times=*/25, anchor);
  611. Category inserted_before = GetUnusedRemoteCategory();
  612. ranker()->InsertCategoryBeforeIfNecessary(inserted_before, anchor);
  613. Category inserted_after = GetUnusedRemoteCategory();
  614. ranker()->InsertCategoryAfterIfNecessary(inserted_after, anchor);
  615. Category tester = AddUnusedRemoteCategory();
  616. NotifyOnSuggestionOpened(/*times=*/20, tester);
  617. EXPECT_TRUE(CompareCategories(inserted_before, tester));
  618. EXPECT_TRUE(CompareCategories(inserted_after, tester));
  619. }
  620. } // namespace ntp_snippets