Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(361)

Unified Diff: components/ntp_snippets/user_classifier_unittest.cc

Issue 2761313002: [User classifier] Add a unit-test (Closed)
Patch Set: Marc's comments #2 Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « components/ntp_snippets/BUILD.gn ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: components/ntp_snippets/user_classifier_unittest.cc
diff --git a/components/ntp_snippets/user_classifier_unittest.cc b/components/ntp_snippets/user_classifier_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f8f3a43a1aa06500c17dde7bbfca66f12f6114e5
--- /dev/null
+++ b/components/ntp_snippets/user_classifier_unittest.cc
@@ -0,0 +1,317 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/ntp_snippets/user_classifier.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/test/histogram_tester.h"
+#include "base/test/simple_test_clock.h"
+#include "base/time/time.h"
+#include "components/ntp_snippets/features.h"
+#include "components/ntp_snippets/ntp_snippets_constants.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/variations/variations_params_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::DoubleNear;
+using testing::Eq;
+using testing::Gt;
+using testing::Lt;
+using testing::SizeIs;
+
+namespace ntp_snippets {
+namespace {
+
+char kNowString[] = "2017-03-01 10:45";
+
+class UserClassifierTest : public testing::Test {
+ public:
+ UserClassifierTest() {
+ UserClassifier::RegisterProfilePrefs(test_prefs_.registry());
+ }
+
+ UserClassifier* CreateUserClassifier() {
+ auto test_clock = base::MakeUnique<base::SimpleTestClock>();
+ test_clock_ = test_clock.get();
+
+ base::Time now;
+ CHECK(base::Time::FromUTCString(kNowString, &now));
+ test_clock_->SetNow(now);
+
+ user_classifier_ =
+ base::MakeUnique<UserClassifier>(&test_prefs_, std::move(test_clock));
+ return user_classifier_.get();
+ }
+
+ base::SimpleTestClock* test_clock() { return test_clock_; }
+
+ private:
+ TestingPrefServiceSimple test_prefs_;
+ std::unique_ptr<UserClassifier> user_classifier_;
+
+ // Owned by the UserClassifier.
+ base::SimpleTestClock* test_clock_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserClassifierTest);
+};
+
+TEST_F(UserClassifierTest, ShouldBeActiveNtpUserInitially) {
+ UserClassifier* user_classifier = CreateUserClassifier();
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
+}
+
+TEST_F(UserClassifierTest,
+ ShouldBecomeActiveSuggestionsConsumerByClickingOften) {
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // After one click still only an active user.
+ user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
+
+ // After a few more clicks, become an active consumer.
+ for (int i = 0; i < 5; i++) {
+ test_clock()->Advance(base::TimeDelta::FromHours(1));
+ user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
+ }
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER));
+}
+
+TEST_F(UserClassifierTest,
+ ShouldBecomeActiveSuggestionsConsumerByClickingOftenWithDecreasedParam) {
+ // Increase the param to one half.
+ variations::testing::VariationParamsManager variation_params(
+ kStudyName,
+ {{"user_classifier_active_consumer_clicks_at_least_once_per_hours",
+ "36"}},
+ {kArticleSuggestionsFeature.name});
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // After two clicks still only an active user.
+ user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
+ test_clock()->Advance(base::TimeDelta::FromHours(1));
+ user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
+
+ // One more click to become an active consumer.
+ test_clock()->Advance(base::TimeDelta::FromHours(1));
+ user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER));
+}
+
+TEST_F(UserClassifierTest, ShouldBecomeRareNtpUserByNoActivity) {
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // After two days of waiting still an active user.
+ test_clock()->Advance(base::TimeDelta::FromDays(2));
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
+
+ // Two more days to become a rare user.
+ test_clock()->Advance(base::TimeDelta::FromDays(2));
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::RARE_NTP_USER));
+}
+
+TEST_F(UserClassifierTest,
+ ShouldBecomeRareNtpUserByNoActivityWithDecreasedParam) {
+ // Decrease the param to one half.
+ variations::testing::VariationParamsManager variation_params(
+ kStudyName,
+ {{"user_classifier_rare_user_opens_ntp_at_most_once_per_hours", "48"}},
+ {kArticleSuggestionsFeature.name});
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // After one days of waiting still an active user.
+ test_clock()->Advance(base::TimeDelta::FromDays(1));
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
+
+ // One more day to become a rare user.
+ test_clock()->Advance(base::TimeDelta::FromDays(1));
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::RARE_NTP_USER));
+}
+
+class UserClassifierMetricTest
+ : public UserClassifierTest,
+ public ::testing::WithParamInterface<
+ std::pair<UserClassifier::Metric, std::string>> {
+ public:
+ UserClassifierMetricTest() : UserClassifierTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(UserClassifierMetricTest);
+};
+
+TEST_P(UserClassifierMetricTest, ShouldDecreaseEstimateAfterEvent) {
+ UserClassifier::Metric metric = GetParam().first;
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // The initial event does not decrease the estimate.
+ user_classifier->OnEvent(metric);
+
+ for (int i = 0; i < 10; i++) {
+ test_clock()->Advance(base::TimeDelta::FromHours(1));
+ double old_metric = user_classifier->GetEstimatedAvgTime(metric);
+ user_classifier->OnEvent(metric);
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Lt(old_metric));
+ }
+}
+
+TEST_P(UserClassifierMetricTest, ShouldReportToUmaOnEvent) {
+ UserClassifier::Metric metric = GetParam().first;
+ const std::string& histogram_name = GetParam().second;
+ base::HistogramTester histogram_tester;
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ user_classifier->OnEvent(metric);
+ EXPECT_THAT(histogram_tester.GetAllSamples(histogram_name), SizeIs(1));
+}
+
+TEST_P(UserClassifierMetricTest, ShouldConvergeTowardsPattern) {
+ UserClassifier::Metric metric = GetParam().first;
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // Have the pattern of an event every five hours and start changing it towards
+ // an event every 10 hours.
+ for (int i = 0; i < 100; i++) {
+ test_clock()->Advance(base::TimeDelta::FromHours(5));
+ user_classifier->OnEvent(metric);
+ }
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric),
+ DoubleNear(5.0, 0.1));
+ for (int i = 0; i < 3; i++) {
+ test_clock()->Advance(base::TimeDelta::FromHours(10));
+ user_classifier->OnEvent(metric);
+ }
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Gt(5.5));
+ for (int i = 0; i < 100; i++) {
+ test_clock()->Advance(base::TimeDelta::FromHours(10));
+ user_classifier->OnEvent(metric);
+ }
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric),
+ DoubleNear(10.0, 0.1));
+}
+
+TEST_P(UserClassifierMetricTest, ShouldIgnoreSubsequentEventsForHalfAnHour) {
+ UserClassifier::Metric metric = GetParam().first;
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // The initial event
+ user_classifier->OnEvent(metric);
+ // Subsequent events get ignored for the next 30 minutes.
+ for (int i = 0; i < 5; i++) {
+ test_clock()->Advance(base::TimeDelta::FromMinutes(5));
+ double old_metric = user_classifier->GetEstimatedAvgTime(metric);
+ user_classifier->OnEvent(metric);
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Eq(old_metric));
+ }
+ // An event 30 minutes after the initial event is finally not ignored.
+ test_clock()->Advance(base::TimeDelta::FromMinutes(5));
+ double old_metric = user_classifier->GetEstimatedAvgTime(metric);
+ user_classifier->OnEvent(metric);
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Lt(old_metric));
+}
+
+TEST_P(UserClassifierMetricTest,
+ ShouldIgnoreSubsequentEventsWithIncreasedLimit) {
+ UserClassifier::Metric metric = GetParam().first;
+ // Increase the min_hours to 1.0, i.e. 60 minutes.
+ variations::testing::VariationParamsManager variation_params(
+ kStudyName, {{"user_classifier_min_hours", "1.0"}},
+ {kArticleSuggestionsFeature.name});
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // The initial event
+ user_classifier->OnEvent(metric);
+ // Subsequent events get ignored for the next 60 minutes.
+ for (int i = 0; i < 11; i++) {
+ test_clock()->Advance(base::TimeDelta::FromMinutes(5));
+ double old_metric = user_classifier->GetEstimatedAvgTime(metric);
+ user_classifier->OnEvent(metric);
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Eq(old_metric));
+ }
+ // An event 60 minutes after the initial event is finally not ignored.
+ test_clock()->Advance(base::TimeDelta::FromMinutes(5));
+ double old_metric = user_classifier->GetEstimatedAvgTime(metric);
+ user_classifier->OnEvent(metric);
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Lt(old_metric));
+}
+
+TEST_P(UserClassifierMetricTest, ShouldCapDelayBetweenEvents) {
+ UserClassifier::Metric metric = GetParam().first;
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // The initial event
+ user_classifier->OnEvent(metric);
+ // Wait for an insane amount of time
+ test_clock()->Advance(base::TimeDelta::FromDays(365));
+ user_classifier->OnEvent(metric);
+ double metric_after_a_year = user_classifier->GetEstimatedAvgTime(metric);
+
+ // Now repeat the same with s/one year/one week.
+ user_classifier->ClearClassificationForDebugging();
+ user_classifier->OnEvent(metric);
+ test_clock()->Advance(base::TimeDelta::FromDays(7));
+ user_classifier->OnEvent(metric);
+
+ // The results should be the same.
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric),
+ Eq(metric_after_a_year));
+}
+
+TEST_P(UserClassifierMetricTest,
+ ShouldCapDelayBetweenEventsWithDecreasedLimit) {
+ UserClassifier::Metric metric = GetParam().first;
+ // Decrease the max_hours to 72, i.e. 3 days.
+ variations::testing::VariationParamsManager variation_params(
+ kStudyName, {{"user_classifier_max_hours", "72"}},
+ {kArticleSuggestionsFeature.name});
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // The initial event
+ user_classifier->OnEvent(metric);
+ // Wait for an insane amount of time
+ test_clock()->Advance(base::TimeDelta::FromDays(365));
+ user_classifier->OnEvent(metric);
+ double metric_after_a_year = user_classifier->GetEstimatedAvgTime(metric);
+
+ // Now repeat the same with s/one year/two days.
+ user_classifier->ClearClassificationForDebugging();
+ user_classifier->OnEvent(metric);
+ test_clock()->Advance(base::TimeDelta::FromDays(3));
+ user_classifier->OnEvent(metric);
+
+ // The results should be the same.
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric),
+ Eq(metric_after_a_year));
+}
+
+INSTANTIATE_TEST_CASE_P(
+ , // An empty prefix for the parametrized tests names (no need to
+ // distinguish the only instance we make here).
+ UserClassifierMetricTest,
+ testing::Values(
+ std::make_pair(UserClassifier::Metric::NTP_OPENED,
+ "NewTabPage.UserClassifier.AverageHoursToOpenNTP"),
+ std::make_pair(
+ UserClassifier::Metric::SUGGESTIONS_SHOWN,
+ "NewTabPage.UserClassifier.AverageHoursToShowSuggestions"),
+ std::make_pair(
+ UserClassifier::Metric::SUGGESTIONS_USED,
+ "NewTabPage.UserClassifier.AverageHoursToUseSuggestions")));
+
+} // namespace
+} // namespace ntp_snippets
« no previous file with comments | « components/ntp_snippets/BUILD.gn ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698