OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/ntp_snippets/category_rankers/click_based_category_ranker.h
" | 5 #include "components/ntp_snippets/category_rankers/click_based_category_ranker.h
" |
6 | 6 |
7 #include "base/memory/ptr_util.h" | 7 #include "base/memory/ptr_util.h" |
| 8 #include "base/strings/string_number_conversions.h" |
8 #include "base/test/simple_test_clock.h" | 9 #include "base/test/simple_test_clock.h" |
9 #include "base/time/default_clock.h" | 10 #include "base/time/default_clock.h" |
10 #include "base/time/time.h" | 11 #include "base/time/time.h" |
11 #include "components/ntp_snippets/category.h" | 12 #include "components/ntp_snippets/category.h" |
12 #include "components/ntp_snippets/category_rankers/constant_category_ranker.h" | 13 #include "components/ntp_snippets/category_rankers/constant_category_ranker.h" |
| 14 #include "components/ntp_snippets/features.h" |
| 15 #include "components/ntp_snippets/ntp_snippets_constants.h" |
13 #include "components/prefs/testing_pref_service.h" | 16 #include "components/prefs/testing_pref_service.h" |
| 17 #include "components/variations/variations_params_manager.h" |
14 #include "testing/gmock/include/gmock/gmock.h" | 18 #include "testing/gmock/include/gmock/gmock.h" |
15 #include "testing/gtest/include/gtest/gtest.h" | 19 #include "testing/gtest/include/gtest/gtest.h" |
16 | 20 |
17 namespace ntp_snippets { | 21 namespace ntp_snippets { |
18 | 22 |
19 class ClickBasedCategoryRankerTest : public testing::Test { | 23 class ClickBasedCategoryRankerTest : public testing::Test { |
20 public: | 24 public: |
21 ClickBasedCategoryRankerTest() | 25 ClickBasedCategoryRankerTest() |
22 : pref_service_(base::MakeUnique<TestingPrefServiceSimple>()), | 26 : pref_service_(base::MakeUnique<TestingPrefServiceSimple>()), |
23 unused_remote_category_id_( | 27 unused_remote_category_id_( |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
58 void NotifyOnSuggestionOpened(int times, Category category) { | 62 void NotifyOnSuggestionOpened(int times, Category category) { |
59 for (int i = 0; i < times; ++i) { | 63 for (int i = 0; i < times; ++i) { |
60 ranker()->OnSuggestionOpened(category); | 64 ranker()->OnSuggestionOpened(category); |
61 } | 65 } |
62 } | 66 } |
63 | 67 |
64 void NotifyOnCategoryDismissed(Category category) { | 68 void NotifyOnCategoryDismissed(Category category) { |
65 ranker()->OnCategoryDismissed(category); | 69 ranker()->OnCategoryDismissed(category); |
66 } | 70 } |
67 | 71 |
| 72 void SetDismissedCategoryPenaltyVariationParam(int value) { |
| 73 variation_params_manager_.SetVariationParamsWithFeatureAssociations( |
| 74 ntp_snippets::kStudyName, |
| 75 {{"click_based_category_ranker-dismissed_category_penalty", |
| 76 base::IntToString(value)}}, |
| 77 {kCategoryRanker.name}); |
| 78 } |
| 79 |
68 ClickBasedCategoryRanker* ranker() { return ranker_.get(); } | 80 ClickBasedCategoryRanker* ranker() { return ranker_.get(); } |
69 | 81 |
70 private: | 82 private: |
71 std::unique_ptr<TestingPrefServiceSimple> pref_service_; | 83 std::unique_ptr<TestingPrefServiceSimple> pref_service_; |
72 int unused_remote_category_id_; | 84 int unused_remote_category_id_; |
73 std::unique_ptr<ClickBasedCategoryRanker> ranker_; | 85 std::unique_ptr<ClickBasedCategoryRanker> ranker_; |
| 86 variations::testing::VariationParamsManager variation_params_manager_; |
74 | 87 |
75 DISALLOW_COPY_AND_ASSIGN(ClickBasedCategoryRankerTest); | 88 DISALLOW_COPY_AND_ASSIGN(ClickBasedCategoryRankerTest); |
76 }; | 89 }; |
77 | 90 |
78 TEST_F(ClickBasedCategoryRankerTest, ShouldSortRemoteCategoriesByWhenAdded) { | 91 TEST_F(ClickBasedCategoryRankerTest, ShouldSortRemoteCategoriesByWhenAdded) { |
79 const Category first = GetUnusedRemoteCategory(); | 92 const Category first = GetUnusedRemoteCategory(); |
80 const Category second = GetUnusedRemoteCategory(); | 93 const Category second = GetUnusedRemoteCategory(); |
81 // Categories are added in decreasing id order to test that they are not | 94 // Categories are added in decreasing id order to test that they are not |
82 // compared by id. | 95 // compared by id. |
83 ranker()->AppendCategoryIfNecessary(second); | 96 ranker()->AppendCategoryIfNecessary(second); |
(...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
317 | 330 |
318 // Ensure that |Now()| is different from |before| by injecting our clock. | 331 // Ensure that |Now()| is different from |before| by injecting our clock. |
319 auto test_clock = base::MakeUnique<base::SimpleTestClock>(); | 332 auto test_clock = base::MakeUnique<base::SimpleTestClock>(); |
320 test_clock->SetNow(base::Time::Now() + base::TimeDelta::FromSeconds(10)); | 333 test_clock->SetNow(base::Time::Now() + base::TimeDelta::FromSeconds(10)); |
321 ResetRanker(std::move(test_clock)); | 334 ResetRanker(std::move(test_clock)); |
322 | 335 |
323 EXPECT_EQ(before, ranker()->GetLastDecayTime()); | 336 EXPECT_EQ(before, ranker()->GetLastDecayTime()); |
324 } | 337 } |
325 | 338 |
326 TEST_F(ClickBasedCategoryRankerTest, ShouldMoveCategoryDownWhenDismissed) { | 339 TEST_F(ClickBasedCategoryRankerTest, ShouldMoveCategoryDownWhenDismissed) { |
| 340 SetDismissedCategoryPenaltyVariationParam(2); |
| 341 |
327 // Take top categories. | 342 // Take top categories. |
328 std::vector<KnownCategories> default_order = | 343 std::vector<KnownCategories> default_order = |
329 ConstantCategoryRanker::GetKnownCategoriesDefaultOrder(); | 344 ConstantCategoryRanker::GetKnownCategoriesDefaultOrder(); |
330 Category first = Category::FromKnownCategory(default_order[0]); | 345 Category first = Category::FromKnownCategory(default_order[0]); |
331 Category second = Category::FromKnownCategory(default_order[1]); | 346 Category second = Category::FromKnownCategory(default_order[1]); |
332 | 347 |
333 ASSERT_TRUE(CompareCategories(first, second)); | 348 ASSERT_TRUE(CompareCategories(first, second)); |
334 NotifyOnCategoryDismissed(first); | 349 NotifyOnCategoryDismissed(first); |
335 EXPECT_FALSE(CompareCategories(first, second)); | 350 EXPECT_FALSE(CompareCategories(first, second)); |
336 } | 351 } |
337 | 352 |
338 TEST_F(ClickBasedCategoryRankerTest, | 353 TEST_F(ClickBasedCategoryRankerTest, |
339 ShouldMoveSecondToLastCategoryDownWhenDismissed) { | 354 ShouldMoveSecondToLastCategoryDownWhenDismissed) { |
| 355 SetDismissedCategoryPenaltyVariationParam(2); |
| 356 |
340 // Add categories to the bottom. | 357 // Add categories to the bottom. |
341 Category first = AddUnusedRemoteCategory(); | 358 Category first = AddUnusedRemoteCategory(); |
342 Category second = AddUnusedRemoteCategory(); | 359 Category second = AddUnusedRemoteCategory(); |
343 | 360 |
344 ASSERT_TRUE(CompareCategories(first, second)); | 361 ASSERT_TRUE(CompareCategories(first, second)); |
345 NotifyOnCategoryDismissed(first); | 362 NotifyOnCategoryDismissed(first); |
346 EXPECT_FALSE(CompareCategories(first, second)); | 363 EXPECT_FALSE(CompareCategories(first, second)); |
347 } | 364 } |
348 | 365 |
349 TEST_F(ClickBasedCategoryRankerTest, | 366 TEST_F(ClickBasedCategoryRankerTest, |
350 ShouldNotMoveCategoryTooMuchDownWhenDismissed) { | 367 ShouldNotMoveCategoryTooMuchDownWhenDismissed) { |
| 368 SetDismissedCategoryPenaltyVariationParam(2); |
| 369 |
351 // Add enough categories to the end. | 370 // Add enough categories to the end. |
352 std::vector<Category> categories; | 371 std::vector<Category> categories; |
353 const int penalty = ClickBasedCategoryRanker::GetDismissedCategoryPenalty(); | 372 const int penalty = ClickBasedCategoryRanker::GetDismissedCategoryPenalty(); |
354 for (int i = 0; i < 2 * penalty + 10; ++i) { | 373 for (int i = 0; i < 2 * penalty + 10; ++i) { |
355 categories.push_back(AddUnusedRemoteCategory()); | 374 categories.push_back(AddUnusedRemoteCategory()); |
356 } | 375 } |
357 | 376 |
358 const int target = penalty + 1; | 377 const int target = penalty + 1; |
359 Category target_category = categories[target]; | 378 Category target_category = categories[target]; |
360 for (int i = 0; i < static_cast<int>(categories.size()); ++i) { | 379 for (int i = 0; i < static_cast<int>(categories.size()); ++i) { |
361 ASSERT_EQ(i < target, CompareCategories(categories[i], target_category)); | 380 ASSERT_EQ(i < target, CompareCategories(categories[i], target_category)); |
362 } | 381 } |
363 | 382 |
364 // This should move exactly |penalty| categories up. | 383 // This should move exactly |penalty| categories up. |
365 NotifyOnCategoryDismissed(categories[target]); | 384 NotifyOnCategoryDismissed(categories[target]); |
366 | 385 |
367 // Reflect expected change in |categories|. | 386 // Reflect expected change in |categories|. |
368 const int expected = target + penalty; | 387 const int expected = target + penalty; |
369 for (int i = target; i + 1 <= expected; ++i) { | 388 for (int i = target; i + 1 <= expected; ++i) { |
370 std::swap(categories[i], categories[i + 1]); | 389 std::swap(categories[i], categories[i + 1]); |
371 } | 390 } |
372 | 391 |
373 for (int i = 0; i < static_cast<int>(categories.size()); ++i) { | 392 for (int i = 0; i < static_cast<int>(categories.size()); ++i) { |
374 EXPECT_EQ(i < expected, CompareCategories(categories[i], target_category)); | 393 EXPECT_EQ(i < expected, CompareCategories(categories[i], target_category)); |
375 } | 394 } |
376 } | 395 } |
377 | 396 |
378 TEST_F(ClickBasedCategoryRankerTest, | 397 TEST_F(ClickBasedCategoryRankerTest, |
379 ShouldNotChangeOrderOfOtherCategoriesWhenDismissed) { | 398 ShouldNotChangeOrderOfOtherCategoriesWhenDismissed) { |
| 399 SetDismissedCategoryPenaltyVariationParam(2); |
| 400 |
380 // Add enough categories to the end. | 401 // Add enough categories to the end. |
381 std::vector<Category> categories; | 402 std::vector<Category> categories; |
382 const int penalty = ClickBasedCategoryRanker::GetDismissedCategoryPenalty(); | 403 const int penalty = ClickBasedCategoryRanker::GetDismissedCategoryPenalty(); |
383 for (int i = 0; i < 2 * penalty + 10; ++i) { | 404 for (int i = 0; i < 2 * penalty + 10; ++i) { |
384 categories.push_back(AddUnusedRemoteCategory()); | 405 categories.push_back(AddUnusedRemoteCategory()); |
385 } | 406 } |
386 | 407 |
387 int target = penalty + 1; | 408 int target = penalty + 1; |
388 // This should not change order of all other categories. | 409 // This should not change order of all other categories. |
389 NotifyOnCategoryDismissed(categories[target]); | 410 NotifyOnCategoryDismissed(categories[target]); |
390 | 411 |
391 categories.erase(categories.begin() + target); | 412 categories.erase(categories.begin() + target); |
392 for (int first = 0; first < static_cast<int>(categories.size()); ++first) { | 413 for (int first = 0; first < static_cast<int>(categories.size()); ++first) { |
393 for (int second = 0; second < static_cast<int>(categories.size()); | 414 for (int second = 0; second < static_cast<int>(categories.size()); |
394 ++second) { | 415 ++second) { |
395 EXPECT_EQ(first < second, | 416 EXPECT_EQ(first < second, |
396 CompareCategories(categories[first], categories[second])); | 417 CompareCategories(categories[first], categories[second])); |
397 } | 418 } |
398 } | 419 } |
399 } | 420 } |
400 | 421 |
401 TEST_F(ClickBasedCategoryRankerTest, ShouldNotMoveLastCategoryWhenDismissed) { | 422 TEST_F(ClickBasedCategoryRankerTest, ShouldNotMoveLastCategoryWhenDismissed) { |
| 423 SetDismissedCategoryPenaltyVariationParam(2); |
| 424 |
402 Category first = AddUnusedRemoteCategory(); | 425 Category first = AddUnusedRemoteCategory(); |
403 Category second = AddUnusedRemoteCategory(); | 426 Category second = AddUnusedRemoteCategory(); |
404 | 427 |
405 ASSERT_TRUE(CompareCategories(first, second)); | 428 ASSERT_TRUE(CompareCategories(first, second)); |
406 NotifyOnCategoryDismissed(second); | 429 NotifyOnCategoryDismissed(second); |
407 EXPECT_TRUE(CompareCategories(first, second)); | 430 EXPECT_TRUE(CompareCategories(first, second)); |
408 } | 431 } |
409 | 432 |
410 TEST_F(ClickBasedCategoryRankerTest, | 433 TEST_F(ClickBasedCategoryRankerTest, |
411 ShouldReduceLastCategoryClicksWhenDismissed) { | 434 ShouldReduceLastCategoryClicksWhenDismissed) { |
| 435 SetDismissedCategoryPenaltyVariationParam(2); |
| 436 |
412 Category first = AddUnusedRemoteCategory(); | 437 Category first = AddUnusedRemoteCategory(); |
413 Category second = AddUnusedRemoteCategory(); | 438 Category second = AddUnusedRemoteCategory(); |
414 | 439 |
415 ASSERT_TRUE(CompareCategories(first, second)); | 440 ASSERT_TRUE(CompareCategories(first, second)); |
416 | 441 |
417 NotifyOnSuggestionOpened( | 442 NotifyOnSuggestionOpened( |
418 /*times=*/1, second); | 443 /*times=*/1, second); |
419 | 444 |
420 // This should reduce the click count back to 0. | 445 // This should reduce the click count back to 0. |
421 NotifyOnCategoryDismissed(second); | 446 NotifyOnCategoryDismissed(second); |
422 | 447 |
423 // Try to move the second category up assuming that the previous click is | 448 // Try to move the second category up assuming that the previous click is |
424 // still there. | 449 // still there. |
425 NotifyOnSuggestionOpened( | 450 NotifyOnSuggestionOpened( |
426 /*times=*/ClickBasedCategoryRanker::GetPassingMargin() - 1, second); | 451 /*times=*/ClickBasedCategoryRanker::GetPassingMargin() - 1, second); |
427 | 452 |
428 EXPECT_TRUE(CompareCategories(first, second)); | 453 EXPECT_TRUE(CompareCategories(first, second)); |
429 | 454 |
430 NotifyOnSuggestionOpened( | 455 NotifyOnSuggestionOpened( |
431 /*times=*/1, second); | 456 /*times=*/1, second); |
432 EXPECT_FALSE(CompareCategories(first, second)); | 457 EXPECT_FALSE(CompareCategories(first, second)); |
433 } | 458 } |
434 | 459 |
| 460 TEST_F(ClickBasedCategoryRankerTest, |
| 461 ShouldTakeVariationValueForDismissedCategoryPenalty) { |
| 462 const int penalty = 10203; |
| 463 SetDismissedCategoryPenaltyVariationParam(penalty); |
| 464 EXPECT_EQ(penalty, ClickBasedCategoryRanker::GetDismissedCategoryPenalty()); |
| 465 } |
| 466 |
| 467 TEST_F(ClickBasedCategoryRankerTest, |
| 468 ShouldDoNothingWhenCategoryDismissedIfPenaltyIsZero) { |
| 469 SetDismissedCategoryPenaltyVariationParam(0); |
| 470 |
| 471 // Add dummy remote categories to ensure that the following categories are not |
| 472 // in the top anymore. |
| 473 AddUnusedRemoteCategories( |
| 474 ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin()); |
| 475 |
| 476 Category first = AddUnusedRemoteCategory(); |
| 477 Category second = AddUnusedRemoteCategory(); |
| 478 Category third = AddUnusedRemoteCategory(); |
| 479 |
| 480 NotifyOnSuggestionOpened( |
| 481 /*times=*/1, second); |
| 482 |
| 483 // This should be ignored, because the penalty is set to 0. |
| 484 NotifyOnCategoryDismissed(second); |
| 485 |
| 486 // The second category should stay where it was. |
| 487 EXPECT_TRUE(CompareCategories(first, second)); |
| 488 EXPECT_TRUE(CompareCategories(second, third)); |
| 489 |
| 490 // Try to move the second category up assuming that the previous click is |
| 491 // still there. |
| 492 NotifyOnSuggestionOpened( |
| 493 /*times=*/ClickBasedCategoryRanker::GetPassingMargin() - 1, second); |
| 494 |
| 495 // It should overtake the first category, because the dismissal should be |
| 496 // ignored and the click should remain. |
| 497 EXPECT_FALSE(CompareCategories(first, second)); |
| 498 } |
| 499 |
435 TEST_F(ClickBasedCategoryRankerTest, ShouldRestoreDefaultOrderOnClearHistory) { | 500 TEST_F(ClickBasedCategoryRankerTest, ShouldRestoreDefaultOrderOnClearHistory) { |
436 std::vector<KnownCategories> default_order = | 501 std::vector<KnownCategories> default_order = |
437 ConstantCategoryRanker::GetKnownCategoriesDefaultOrder(); | 502 ConstantCategoryRanker::GetKnownCategoriesDefaultOrder(); |
438 Category first = Category::FromKnownCategory(default_order[0]); | 503 Category first = Category::FromKnownCategory(default_order[0]); |
439 Category second = Category::FromKnownCategory(default_order[1]); | 504 Category second = Category::FromKnownCategory(default_order[1]); |
440 | 505 |
441 ASSERT_TRUE(CompareCategories(first, second)); | 506 ASSERT_TRUE(CompareCategories(first, second)); |
442 | 507 |
443 // Change the order. | 508 // Change the order. |
444 while (CompareCategories(first, second)) { | 509 while (CompareCategories(first, second)) { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
488 // The user partially clears history. | 553 // The user partially clears history. |
489 base::Time begin = base::Time::Now() - base::TimeDelta::FromHours(1), | 554 base::Time begin = base::Time::Now() - base::TimeDelta::FromHours(1), |
490 end = base::Time::Max(); | 555 end = base::Time::Max(); |
491 ranker()->ClearHistory(begin, end); | 556 ranker()->ClearHistory(begin, end); |
492 | 557 |
493 // The order should not be cleared. | 558 // The order should not be cleared. |
494 EXPECT_FALSE(CompareCategories(first, second)); | 559 EXPECT_FALSE(CompareCategories(first, second)); |
495 } | 560 } |
496 | 561 |
497 } // namespace ntp_snippets | 562 } // namespace ntp_snippets |
OLD | NEW |