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

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

Issue 2761313002: [User classifier] Add a unit-test (Closed)
Patch Set: 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
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 std::unique_ptr<UserClassifier> CreateUserClassifier() {
37 auto test_clock = base::MakeUnique<base::SimpleTestClock>();
38 test_clock_ = test_clock.get();
Marc Treib 2017/03/21 15:15:45 This is dangerous: It hands out the UserClassifier
jkrcal 2017/03/21 17:29:15 Fixed - the classifier is now owned by the test cl
39
40 base::Time now;
41 CHECK(base::Time::FromUTCString(kNowString, &now));
42 test_clock_->SetNow(now);
43
44 return base::MakeUnique<UserClassifier>(&test_prefs_,
45 std::move(test_clock));
46 }
47
48 protected:
49 TestingPrefServiceSimple test_prefs_;
50 base::SimpleTestClock* test_clock_;
51
52 private:
53 DISALLOW_COPY_AND_ASSIGN(UserClassifierTest);
54 };
55
56 TEST_F(UserClassifierTest, ShouldBeActiveNtpUserInitially) {
57 auto user_classifier = CreateUserClassifier();
58 EXPECT_THAT(user_classifier->GetUserClass(),
59 Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
60 }
61
62 TEST_F(UserClassifierTest,
63 ShouldBecomeActiveSuggestionsConsumerByClickingOften) {
64 auto user_classifier = CreateUserClassifier();
65
66 // After one click still only an active user.
67 user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
68 EXPECT_THAT(user_classifier->GetUserClass(),
69 Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
70
71 // One more click to become an active consumer.
Marc Treib 2017/03/21 15:15:46 Do we really want to test that the *second* click
jkrcal 2017/03/21 17:29:15 Made it a bit more relaxed.
72 test_clock_->Advance(base::TimeDelta::FromHours(1));
73 user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
74 EXPECT_THAT(user_classifier->GetUserClass(),
75 Eq(UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER));
76 }
77
78 TEST_F(UserClassifierTest,
79 ShouldBecomeActiveSuggestionsConsumerByClickingOftenWithDecreasedParam) {
80 // Increase the param to one half.
81 variations::testing::VariationParamsManager variation_params(
82 kStudyName,
83 {{"user_classifier_active_consumer_clicks_at_least_once_per_hours",
84 "36"}},
85 {kArticleSuggestionsFeature.name});
86 auto user_classifier = CreateUserClassifier();
87
88 // After two clicks still only an active user.
89 user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
90 test_clock_->Advance(base::TimeDelta::FromHours(1));
91 user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
92 EXPECT_THAT(user_classifier->GetUserClass(),
93 Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
94
95 // One more click to become an active consumer.
96 test_clock_->Advance(base::TimeDelta::FromHours(1));
97 user_classifier->OnEvent(UserClassifier::Metric::SUGGESTIONS_USED);
98 EXPECT_THAT(user_classifier->GetUserClass(),
99 Eq(UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER));
100 }
101
102 TEST_F(UserClassifierTest, ShouldBecomeRareNtpUserByNoActivity) {
103 auto user_classifier = CreateUserClassifier();
104
105 // After two days of waiting still an active user.
106 test_clock_->Advance(base::TimeDelta::FromDays(2));
107 EXPECT_THAT(user_classifier->GetUserClass(),
108 Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
109
110 // Two more days to become a rare user.
111 test_clock_->Advance(base::TimeDelta::FromDays(2));
112 EXPECT_THAT(user_classifier->GetUserClass(),
113 Eq(UserClassifier::UserClass::RARE_NTP_USER));
114 }
115
116 TEST_F(UserClassifierTest,
117 ShouldBecomeRareNtpUserByNoActivityWithDecreasedParam) {
118 // Decrease the param to one half.
119 variations::testing::VariationParamsManager variation_params(
120 kStudyName,
121 {{"user_classifier_rare_user_opens_ntp_at_most_once_per_hours", "48"}},
122 {kArticleSuggestionsFeature.name});
123 auto user_classifier = CreateUserClassifier();
124
125 // After one days of waiting still an active user.
126 test_clock_->Advance(base::TimeDelta::FromDays(1));
127 EXPECT_THAT(user_classifier->GetUserClass(),
128 Eq(UserClassifier::UserClass::ACTIVE_NTP_USER));
129
130 // One more day to become a rare user.
131 test_clock_->Advance(base::TimeDelta::FromDays(1));
132 EXPECT_THAT(user_classifier->GetUserClass(),
133 Eq(UserClassifier::UserClass::RARE_NTP_USER));
134 }
135
136 class UserClassifierMetricTest
137 : public UserClassifierTest,
138 public ::testing::WithParamInterface<
139 std::pair<UserClassifier::Metric, std::string>> {
140 public:
141 UserClassifierMetricTest() : UserClassifierTest() {}
142
143 private:
144 DISALLOW_COPY_AND_ASSIGN(UserClassifierMetricTest);
145 };
146
147 TEST_P(UserClassifierMetricTest, ShouldDecreaseEstimateAfterEvent) {
148 UserClassifier::Metric metric = GetParam().first;
149 auto user_classifier = CreateUserClassifier();
150
151 // The initial event does not decrease the estimate.
152 user_classifier->OnEvent(metric);
153
154 for (int i = 0; i < 10; i++) {
155 test_clock_->Advance(base::TimeDelta::FromHours(1));
156 double old_metric = user_classifier->GetEstimatedAvgTime(metric);
157 user_classifier->OnEvent(metric);
158 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Lt(old_metric));
159 }
160 }
161
162 TEST_P(UserClassifierMetricTest, ShouldReportToUmaOnEvent) {
163 UserClassifier::Metric metric = GetParam().first;
164 const std::string& histogram_name = GetParam().second;
165 base::HistogramTester histogram_tester;
166 auto user_classifier = CreateUserClassifier();
167
168 user_classifier->OnEvent(metric);
169 EXPECT_THAT(histogram_tester.GetAllSamples(histogram_name), SizeIs(1));
170 }
171
172 TEST_P(UserClassifierMetricTest, ShouldConvergeTowardsPattern) {
173 UserClassifier::Metric metric = GetParam().first;
174 auto user_classifier = CreateUserClassifier();
175
176 // Have the pattern of an event every five hours and start changing it towards
177 // an event every 10 hours.
178 for (int i = 0; i < 100; i++) {
179 test_clock_->Advance(base::TimeDelta::FromHours(5));
180 user_classifier->OnEvent(metric);
181 }
182 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric),
183 DoubleNear(5.0, 0.1));
184 for (int i = 0; i < 3; i++) {
185 test_clock_->Advance(base::TimeDelta::FromHours(10));
186 user_classifier->OnEvent(metric);
187 }
188 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Gt(5.5));
189 for (int i = 0; i < 100; i++) {
190 test_clock_->Advance(base::TimeDelta::FromHours(10));
191 user_classifier->OnEvent(metric);
192 }
193 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric),
194 DoubleNear(10.0, 0.1));
195 }
196
197 TEST_P(UserClassifierMetricTest, ShouldIgnoreSubsequentEvents) {
198 UserClassifier::Metric metric = GetParam().first;
199 auto user_classifier = CreateUserClassifier();
200
201 // The initial event
202 user_classifier->OnEvent(metric);
203 // No change for 25 minutes.
204 for (int i = 0; i < 5; i++) {
205 test_clock_->Advance(base::TimeDelta::FromMinutes(5));
206 double old_metric = user_classifier->GetEstimatedAvgTime(metric);
207 user_classifier->OnEvent(metric);
208 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Eq(old_metric));
209 }
210 // After 30 minutes, it gets updated.
Marc Treib 2017/03/21 15:15:46 ...I don't get this one. What's going on here? May
jkrcal 2017/03/21 17:29:15 Done.
211 test_clock_->Advance(base::TimeDelta::FromMinutes(5));
212 double old_metric = user_classifier->GetEstimatedAvgTime(metric);
213 user_classifier->OnEvent(metric);
214 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Lt(old_metric));
215 }
216
217 TEST_P(UserClassifierMetricTest,
218 ShouldIgnoreSubsequentEventsWithIncreasedLimit) {
219 UserClassifier::Metric metric = GetParam().first;
220 // Increase the min_hours to 1.0, i.e. 60 minutes.
221 variations::testing::VariationParamsManager variation_params(
222 kStudyName, {{"user_classifier_min_hours", "1.0"}},
223 {kArticleSuggestionsFeature.name});
224 auto user_classifier = CreateUserClassifier();
225
226 // The initial event
227 user_classifier->OnEvent(metric);
228 // No change for 55 minutes.
229 for (int i = 0; i < 11; i++) {
230 test_clock_->Advance(base::TimeDelta::FromMinutes(5));
231 double old_metric = user_classifier->GetEstimatedAvgTime(metric);
232 user_classifier->OnEvent(metric);
233 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Eq(old_metric));
234 }
235 // After 60 minutes, it gets updated.
236 test_clock_->Advance(base::TimeDelta::FromMinutes(5));
237 double old_metric = user_classifier->GetEstimatedAvgTime(metric);
238 user_classifier->OnEvent(metric);
239 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric), Lt(old_metric));
240 }
241
242 TEST_P(UserClassifierMetricTest, ShouldCapDelayBetweenEvents) {
243 UserClassifier::Metric metric = GetParam().first;
244 auto user_classifier = CreateUserClassifier();
245
246 // The initial event
247 user_classifier->OnEvent(metric);
248 // Wait for an insane amount of time
249 test_clock_->Advance(base::TimeDelta::FromDays(365));
250 user_classifier->OnEvent(metric);
251 double metric_after_a_year = user_classifier->GetEstimatedAvgTime(metric);
252
253 // Now repeat the same with s/one year/one week.
254 user_classifier->ClearClassificationForDebugging();
255 user_classifier->OnEvent(metric);
256 test_clock_->Advance(base::TimeDelta::FromDays(7));
257 user_classifier->OnEvent(metric);
258
259 // The results should be the same.
260 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric),
261 Eq(metric_after_a_year));
262 }
263
264 TEST_P(UserClassifierMetricTest,
265 ShouldCapDelayBetweenEventsWithDecreasedLimit) {
266 UserClassifier::Metric metric = GetParam().first;
267 // Decrease the max_hours to 72, i.e. 3 days.
268 variations::testing::VariationParamsManager variation_params(
269 kStudyName, {{"user_classifier_max_hours", "72"}},
270 {kArticleSuggestionsFeature.name});
271 auto user_classifier = CreateUserClassifier();
272
273 // The initial event
274 user_classifier->OnEvent(metric);
275 // Wait for an insane amount of time
276 test_clock_->Advance(base::TimeDelta::FromDays(365));
277 user_classifier->OnEvent(metric);
278 double metric_after_a_year = user_classifier->GetEstimatedAvgTime(metric);
279
280 // Now repeat the same with s/one year/two days.
281 user_classifier->ClearClassificationForDebugging();
282 user_classifier->OnEvent(metric);
283 test_clock_->Advance(base::TimeDelta::FromDays(3));
284 user_classifier->OnEvent(metric);
285
286 // The results should be the same.
287 EXPECT_THAT(user_classifier->GetEstimatedAvgTime(metric),
288 Eq(metric_after_a_year));
289 }
290
291 INSTANTIATE_TEST_CASE_P(
292 ,
Marc Treib 2017/03/21 15:15:45 What's this?
jkrcal 2017/03/21 17:29:15 This is a prefix for instantiating the tests. An e
293 UserClassifierMetricTest,
294 testing::Values(
295 std::make_pair(UserClassifier::Metric::NTP_OPENED,
296 "NewTabPage.UserClassifier.AverageHoursToOpenNTP"),
297 std::make_pair(
298 UserClassifier::Metric::SUGGESTIONS_SHOWN,
299 "NewTabPage.UserClassifier.AverageHoursToShowSuggestions"),
300 std::make_pair(
301 UserClassifier::Metric::SUGGESTIONS_USED,
302 "NewTabPage.UserClassifier.AverageHoursToUseSuggestions")));
303
304 } // namespace
305 } // 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