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

Side by Side Diff: components/ntp_snippets/user_classifier_unittest.cc

Issue 2761313002: [User classifier] Add a unit-test (Closed)
Patch Set: Marc's comment 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 unified diff | Download patch
« no previous file with comments | « components/ntp_snippets/BUILD.gn ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2017 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 "components/ntp_snippets/user_classifier.h"
6
Marc Treib 2017/03/21 17:49:43 nitty nit: missing some std includes, like memory,
jkrcal 2017/03/22 08:45:18 Done.
7 #include "base/memory/ptr_util.h"
8 #include "base/test/histogram_tester.h"
9 #include "base/test/simple_test_clock.h"
10 #include "base/time/time.h"
11 #include "components/ntp_snippets/features.h"
12 #include "components/ntp_snippets/ntp_snippets_constants.h"
13 #include "components/prefs/pref_registry_simple.h"
14 #include "components/prefs/testing_pref_service.h"
15 #include "components/variations/variations_params_manager.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18
19 using testing::DoubleNear;
20 using testing::Eq;
21 using testing::Gt;
22 using testing::Lt;
23 using testing::SizeIs;
24
25 namespace ntp_snippets {
26 namespace {
27
28 char kNowString[] = "2017-03-01 10:45";
29
30 class UserClassifierTest : public testing::Test {
31 public:
32 UserClassifierTest() {
33 UserClassifier::RegisterProfilePrefs(test_prefs_.registry());
34 }
35
36 UserClassifier* CreateUserClassifier() {
37 auto test_clock = base::MakeUnique<base::SimpleTestClock>();
38 test_clock_ = test_clock.get();
39
40 base::Time now;
41 CHECK(base::Time::FromUTCString(kNowString, &now));
42 test_clock_->SetNow(now);
43
44 user_classifier_ =
45 base::MakeUnique<UserClassifier>(&test_prefs_, std::move(test_clock));
46 return user_classifier_.get();
47 }
48
49 protected:
50 TestingPrefServiceSimple test_prefs_;
51
52 base::SimpleTestClock* test_clock_;
Marc Treib 2017/03/21 17:49:43 // Owned by the UserClassifier. ?
jkrcal 2017/03/22 08:45:18 Done.
53 std::unique_ptr<UserClassifier> user_classifier_;
Marc Treib 2017/03/21 17:49:43 Could all these be private? (And instead have publ
jkrcal 2017/03/22 08:45:18 Done.
54
55 private:
56 DISALLOW_COPY_AND_ASSIGN(UserClassifierTest);
57 };
58
59 TEST_F(UserClassifierTest, ShouldBeActiveNtpUserInitially) {
60 UserClassifier* user_classifier = CreateUserClassifier();
61 EXPECT_THAT(user_classifier->GetUserClass(),
62 Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
63 }
64
65 TEST_F(UserClassifierTest,
66 ShouldBecomeActiveSuggestionsConsumerByClickingOften) {
67 UserClassifier* user_classifier = CreateUserClassifier();
68
69 // After one click still only an active user.
70 user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
71 EXPECT_THAT(user_classifier->GetUserClass(),
72 Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
73
74 // After a few more clicks, become an active consumer.
75 for (int i = 0; i < 5; i++) {
76 test_clock_->Advance(base::TimeDelta::FromHours(1));
77 user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
78 }
79 EXPECT_THAT(user_classifier->GetUserClass(),
80 Eq(UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER));
81 }
82
83 TEST_F(UserClassifierTest,
84 ShouldBecomeActiveSuggestionsConsumerByClickingOftenWithDecreasedParam) {
85 // Increase the param to one half.
86 variations::testing::VariationParamsManager variation_params(
87 kStudyName,
88 {{"user_classifier_active_consumer_clicks_at_least_once_per_hours",
89 "36"}},
90 {kArticleSuggestionsFeature.name});
91 UserClassifier* user_classifier = CreateUserClassifier();
92
93 // After two clicks still only an active user.
94 user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
95 test_clock_->Advance(base::TimeDelta::FromHours(1));
96 user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
97 EXPECT_THAT(user_classifier->GetUserClass(),
98 Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
99
100 // One more click to become an active consumer.
101 test_clock_->Advance(base::TimeDelta::FromHours(1));
102 user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
103 EXPECT_THAT(user_classifier->GetUserClass(),
104 Eq(UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER));
105 }
106
107 TEST_F(UserClassifierTest, ShouldBecomeRareNtpUserByNoActivity) {
108 UserClassifier* user_classifier = CreateUserClassifier();
109
110 // After two days of waiting still an active user.
111 test_clock_->Advance(base::TimeDelta::FromDays(2));
112 EXPECT_THAT(user_classifier->GetUserClass(),
113 Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
114
115 // Two more days to become a rare user.
116 test_clock_->Advance(base::TimeDelta::FromDays(2));
117 EXPECT_THAT(user_classifier->GetUserClass(),
118 Eq(UserClassifier::UserClass::RARE_NTP_USER));
119 }
120
121 TEST_F(UserClassifierTest,
122 ShouldBecomeRareNtpUserByNoActivityWithDecreasedParam) {
123 // Decrease the param to one half.
124 variations::testing::VariationParamsManager variation_params(
125 kStudyName,
126 {{"user_classifier_rare_user_opens_ntp_at_most_once_per_hours", "48"}},
127 {kArticleSuggestionsFeature.name});
128 UserClassifier* user_classifier = CreateUserClassifier();
129
130 // After one days of waiting still an active user.
131 test_clock_->Advance(base::TimeDelta::FromDays(1));
132 EXPECT_THAT(user_classifier->GetUserClass(),
133 Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
134
135 // One more day to become a rare user.
136 test_clock_->Advance(base::TimeDelta::FromDays(1));
137 EXPECT_THAT(user_classifier->GetUserClass(),
138 Eq(UserClassifier::UserClass::RARE_NTP_USER));
139 }
140
141 class UserClassifierMetricTest
142 : public UserClassifierTest,
143 public ::testing::WithParamInterface<
144 std::pair<UserClassifier::Metric, std::string>> {
145 public:
146 UserClassifierMetricTest() : UserClassifierTest() {}
147
148 private:
149 DISALLOW_COPY_AND_ASSIGN(UserClassifierMetricTest);
150 };
151
152 TEST_P(UserClassifierMetricTest, ShouldDecreaseEstimateAfterEvent) {
153 UserClassifier::Metric metric = GetParam().first;
154 UserClassifier* user_classifier = CreateUserClassifier();
155
156 // The initial event does not decrease the estimate.
157 user_classifier->OnEvent(metric);
158
159 for (int i = 0; i < 10; i++) {
160 test_clock_->Advance(base::TimeDelta::FromHours(1));
161 double old_metric = user_classifier->GetEstimatedAvgTime(metric);
162 user_classifier->OnEvent(metric);
163 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Lt(old_metric));
164 }
165 }
166
167 TEST_P(UserClassifierMetricTest, ShouldReportToUmaOnEvent) {
168 UserClassifier::Metric metric = GetParam().first;
169 const std::string& histogram_name = GetParam().second;
170 base::HistogramTester histogram_tester;
171 UserClassifier* user_classifier = CreateUserClassifier();
172
173 user_classifier->OnEvent(metric);
174 EXPECT_THAT(histogram_tester.GetAllSamples(histogram_name), SizeIs(1));
175 }
176
177 TEST_P(UserClassifierMetricTest, ShouldConvergeTowardsPattern) {
178 UserClassifier::Metric metric = GetParam().first;
179 UserClassifier* user_classifier = CreateUserClassifier();
180
181 // Have the pattern of an event every five hours and start changing it towards
182 // an event every 10 hours.
183 for (int i = 0; i < 100; i++) {
184 test_clock_->Advance(base::TimeDelta::FromHours(5));
185 user_classifier->OnEvent(metric);
186 }
187 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric),
188 DoubleNear(5.0, 0.1));
189 for (int i = 0; i < 3; i++) {
190 test_clock_->Advance(base::TimeDelta::FromHours(10));
191 user_classifier->OnEvent(metric);
192 }
193 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Gt(5.5));
194 for (int i = 0; i < 100; i++) {
195 test_clock_->Advance(base::TimeDelta::FromHours(10));
196 user_classifier->OnEvent(metric);
197 }
198 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric),
199 DoubleNear(10.0, 0.1));
200 }
201
202 TEST_P(UserClassifierMetricTest, ShouldIgnoreSubsequentEventsForHalfAnHour) {
203 UserClassifier::Metric metric = GetParam().first;
204 UserClassifier* user_classifier = CreateUserClassifier();
205
206 // The initial event
207 user_classifier->OnEvent(metric);
208 // Subsequent events get ignored for the next 30 minutes.
209 for (int i = 0; i < 5; i++) {
210 test_clock_->Advance(base::TimeDelta::FromMinutes(5));
211 double old_metric = user_classifier->GetEstimatedAvgTime(metric);
212 user_classifier->OnEvent(metric);
213 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Eq(old_metric));
214 }
215 // An event 30 minutes after the initial event is finally not ignored.
216 test_clock_->Advance(base::TimeDelta::FromMinutes(5));
217 double old_metric = user_classifier->GetEstimatedAvgTime(metric);
218 user_classifier->OnEvent(metric);
219 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Lt(old_metric));
220 }
221
222 TEST_P(UserClassifierMetricTest,
223 ShouldIgnoreSubsequentEventsWithIncreasedLimit) {
224 UserClassifier::Metric metric = GetParam().first;
225 // Increase the min_hours to 1.0, i.e. 60 minutes.
226 variations::testing::VariationParamsManager variation_params(
227 kStudyName, {{"user_classifier_min_hours", "1.0"}},
228 {kArticleSuggestionsFeature.name});
229 UserClassifier* user_classifier = CreateUserClassifier();
230
231 // The initial event
232 user_classifier->OnEvent(metric);
233 // Subsequent events get ignored for the next 60 minutes.
234 for (int i = 0; i < 11; i++) {
235 test_clock_->Advance(base::TimeDelta::FromMinutes(5));
236 double old_metric = user_classifier->GetEstimatedAvgTime(metric);
237 user_classifier->OnEvent(metric);
238 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Eq(old_metric));
239 }
240 // An event 60 minutes after the initial event is finally not ignored.
241 test_clock_->Advance(base::TimeDelta::FromMinutes(5));
242 double old_metric = user_classifier->GetEstimatedAvgTime(metric);
243 user_classifier->OnEvent(metric);
244 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Lt(old_metric));
245 }
246
247 TEST_P(UserClassifierMetricTest, ShouldCapDelayBetweenEvents) {
248 UserClassifier::Metric metric = GetParam().first;
249 UserClassifier* user_classifier = CreateUserClassifier();
250
251 // The initial event
252 user_classifier->OnEvent(metric);
253 // Wait for an insane amount of time
254 test_clock_->Advance(base::TimeDelta::FromDays(365));
255 user_classifier->OnEvent(metric);
256 double metric_after_a_year = user_classifier->GetEstimatedAvgTime(metric);
257
258 // Now repeat the same with s/one year/one week.
259 user_classifier->ClearClassificationForDebugging();
260 user_classifier->OnEvent(metric);
261 test_clock_->Advance(base::TimeDelta::FromDays(7));
262 user_classifier->OnEvent(metric);
263
264 // The results should be the same.
265 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric),
266 Eq(metric_after_a_year));
267 }
268
269 TEST_P(UserClassifierMetricTest,
270 ShouldCapDelayBetweenEventsWithDecreasedLimit) {
271 UserClassifier::Metric metric = GetParam().first;
272 // Decrease the max_hours to 72, i.e. 3 days.
273 variations::testing::VariationParamsManager variation_params(
274 kStudyName, {{"user_classifier_max_hours", "72"}},
275 {kArticleSuggestionsFeature.name});
276 UserClassifier* user_classifier = CreateUserClassifier();
277
278 // The initial event
279 user_classifier->OnEvent(metric);
280 // Wait for an insane amount of time
281 test_clock_->Advance(base::TimeDelta::FromDays(365));
282 user_classifier->OnEvent(metric);
283 double metric_after_a_year = user_classifier->GetEstimatedAvgTime(metric);
284
285 // Now repeat the same with s/one year/two days.
286 user_classifier->ClearClassificationForDebugging();
287 user_classifier->OnEvent(metric);
288 test_clock_->Advance(base::TimeDelta::FromDays(3));
289 user_classifier->OnEvent(metric);
290
291 // The results should be the same.
292 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric),
293 Eq(metric_after_a_year));
294 }
295
296 INSTANTIATE_TEST_CASE_P(
297 , // An empty prefix for the parametrized tests names (no need to
298 // distinguish the only instance we make here).
299 UserClassifierMetricTest,
300 testing::Values(
301 std::make_pair(UserClassifier::Metric::NTP_OPENED,
302 "NewTabPage.UserClassifier.AverageHoursToOpenNTP"),
303 std::make_pair(
304 UserClassifier::Metric::SUGGESTIONS_SHOWN,
305 "NewTabPage.UserClassifier.AverageHoursToShowSuggestions"),
306 std::make_pair(
307 UserClassifier::Metric::SUGGESTIONS_USED,
308 "NewTabPage.UserClassifier.AverageHoursToUseSuggestions")));
309
310 } // namespace
311 } // namespace ntp_snippets
OLDNEW
« 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