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

Side by Side Diff: chrome/browser/budget_service/budget_database_unittest.cc

Issue 2620393002: Refactor budget computation to be more tuneable. (Closed)
Patch Set: Formatting Created 3 years, 11 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
OLDNEW
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 "chrome/browser/budget_service/budget_database.h" 5 #include "chrome/browser/budget_service/budget_database.h"
6 6
7 #include <math.h>
8
Peter Beverloo 2017/01/12 14:40:04 style nit: no newline
harkness 2017/01/12 17:44:15 Done.
7 #include <vector> 9 #include <vector>
8 10
9 #include "base/memory/ptr_util.h" 11 #include "base/memory/ptr_util.h"
10 #include "base/run_loop.h" 12 #include "base/run_loop.h"
11 #include "base/test/histogram_tester.h" 13 #include "base/test/histogram_tester.h"
12 #include "base/test/simple_test_clock.h" 14 #include "base/test/simple_test_clock.h"
13 #include "base/threading/thread_task_runner_handle.h" 15 #include "base/threading/thread_task_runner_handle.h"
14 #include "chrome/browser/budget_service/budget.pb.h" 16 #include "chrome/browser/budget_service/budget.pb.h"
15 #include "chrome/browser/engagement/site_engagement_service.h" 17 #include "chrome/browser/engagement/site_engagement_service.h"
16 #include "chrome/test/base/testing_profile.h" 18 #include "chrome/test/base/testing_profile.h"
17 #include "components/leveldb_proto/proto_database.h" 19 #include "components/leveldb_proto/proto_database.h"
18 #include "components/leveldb_proto/proto_database_impl.h" 20 #include "components/leveldb_proto/proto_database_impl.h"
19 #include "content/public/browser/browser_thread.h" 21 #include "content/public/browser/browser_thread.h"
20 #include "content/public/test/test_browser_thread_bundle.h" 22 #include "content/public/test/test_browser_thread_bundle.h"
21 #include "mojo/public/cpp/bindings/array.h" 23 #include "mojo/public/cpp/bindings/array.h"
22 #include "testing/gtest/include/gtest/gtest.h" 24 #include "testing/gtest/include/gtest/gtest.h"
23 #include "url/gurl.h" 25 #include "url/gurl.h"
24 #include "url/origin.h" 26 #include "url/origin.h"
25 27
26 namespace { 28 namespace {
27 29
28 const double kDefaultExpirationInHours = 96; 30 const double kDefaultExpirationInDays = 4;
29 const double kDefaultEngagement = 30.0; 31 const double kDefaultEngagement = 25;
Peter Beverloo 2017/01/12 14:40:04 Is this true? The amount of bootstrap points in Si
harkness 2017/01/12 17:44:15 That's just the value we set for the purposes of t
32 const double kMaxDailyBudget = 12;
33 const double kMaxSES = 100;
Peter Beverloo 2017/01/12 14:40:04 constexpr Would also be good to document whether
Peter Beverloo 2017/01/12 14:40:04 kMaxSES -> SiteEngagementScore::kMaxPoints
harkness 2017/01/12 17:44:15 Done.
harkness 2017/01/12 17:44:15 Done.
30 34
31 const char kTestOrigin[] = "https://example.com"; 35 const char kTestOrigin[] = "https://example.com";
32 36
33 } // namespace 37 } // namespace
34 38
35 class BudgetDatabaseTest : public ::testing::Test { 39 class BudgetDatabaseTest : public ::testing::Test {
36 public: 40 public:
37 BudgetDatabaseTest() 41 BudgetDatabaseTest()
38 : success_(false), 42 : success_(false),
39 db_(&profile_, 43 db_(&profile_,
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
108 TEST_F(BudgetDatabaseTest, GetBudgetNoBudgetOrSES) { 112 TEST_F(BudgetDatabaseTest, GetBudgetNoBudgetOrSES) {
109 GetBudgetDetails(); 113 GetBudgetDetails();
110 ASSERT_TRUE(success_); 114 ASSERT_TRUE(success_);
111 ASSERT_EQ(2U, prediction_.size()); 115 ASSERT_EQ(2U, prediction_.size());
112 EXPECT_EQ(0, prediction_[0]->budget_at); 116 EXPECT_EQ(0, prediction_[0]->budget_at);
113 } 117 }
114 118
115 TEST_F(BudgetDatabaseTest, AddEngagementBudgetTest) { 119 TEST_F(BudgetDatabaseTest, AddEngagementBudgetTest) {
116 base::SimpleTestClock* clock = SetClockForTesting(); 120 base::SimpleTestClock* clock = SetClockForTesting();
117 base::Time expiration_time = 121 base::Time expiration_time =
118 clock->Now() + base::TimeDelta::FromHours(kDefaultExpirationInHours); 122 clock->Now() + base::TimeDelta::FromDays(kDefaultExpirationInDays);
119 123
120 // Set the default site engagement. 124 // Set the default site engagement.
121 SetSiteEngagementScore(kDefaultEngagement); 125 SetSiteEngagementScore(kDefaultEngagement);
122 126
123 // The budget should include a full share of the engagement. 127 // The budget should include kDefaultExpirationInDays days worth of
128 // engagement.
129 double daily_budget = kMaxDailyBudget * kDefaultEngagement / kMaxSES;
124 GetBudgetDetails(); 130 GetBudgetDetails();
125 ASSERT_TRUE(success_); 131 ASSERT_TRUE(success_);
126 ASSERT_EQ(2U, prediction_.size()); 132 ASSERT_EQ(2U, prediction_.size());
127 ASSERT_EQ(kDefaultEngagement, prediction_[0]->budget_at); 133 ASSERT_DOUBLE_EQ(daily_budget * kDefaultExpirationInDays,
134 prediction_[0]->budget_at);
128 ASSERT_EQ(0, prediction_[1]->budget_at); 135 ASSERT_EQ(0, prediction_[1]->budget_at);
129 ASSERT_EQ(expiration_time.ToDoubleT(), prediction_[1]->time); 136 ASSERT_EQ(expiration_time.ToDoubleT(), prediction_[1]->time);
130 137
131 // Advance time 1 day and add more engagement budget. 138 // Advance time 1 day and add more engagement budget.
132 clock->Advance(base::TimeDelta::FromDays(1)); 139 clock->Advance(base::TimeDelta::FromDays(1));
133 GetBudgetDetails(); 140 GetBudgetDetails();
134 141
135 // The budget should now have 1 full share plus 1 daily budget. 142 // The budget should now have 1 full share plus 1 daily budget.
136 ASSERT_TRUE(success_); 143 ASSERT_TRUE(success_);
137 ASSERT_EQ(3U, prediction_.size()); 144 ASSERT_EQ(3U, prediction_.size());
138 double daily_budget = kDefaultEngagement * 24 / kDefaultExpirationInHours; 145 ASSERT_DOUBLE_EQ(daily_budget * (kDefaultExpirationInDays + 1),
139 ASSERT_DOUBLE_EQ(kDefaultEngagement + daily_budget,
140 prediction_[0]->budget_at); 146 prediction_[0]->budget_at);
141 ASSERT_DOUBLE_EQ(daily_budget, prediction_[1]->budget_at); 147 ASSERT_DOUBLE_EQ(daily_budget, prediction_[1]->budget_at);
142 ASSERT_EQ(expiration_time.ToDoubleT(), prediction_[1]->time); 148 ASSERT_EQ(expiration_time.ToDoubleT(), prediction_[1]->time);
143 ASSERT_EQ(0, prediction_[2]->budget_at); 149 ASSERT_DOUBLE_EQ(0, prediction_[2]->budget_at);
144 ASSERT_EQ((expiration_time + base::TimeDelta::FromDays(1)).ToDoubleT(), 150 ASSERT_EQ((expiration_time + base::TimeDelta::FromDays(1)).ToDoubleT(),
145 prediction_[2]->time); 151 prediction_[2]->time);
146 152
147 // Advance time by 59 minutes and check that no engagement budget is added 153 // Advance time by 59 minutes and check that no engagement budget is added
148 // since budget should only be added for > 1 hour increments. 154 // since budget should only be added for > 1 hour increments.
149 clock->Advance(base::TimeDelta::FromMinutes(59)); 155 clock->Advance(base::TimeDelta::FromMinutes(59));
150 GetBudgetDetails(); 156 GetBudgetDetails();
151 157
152 // The budget should be the same as before the attempted add. 158 // The budget should be the same as before the attempted add.
153 ASSERT_TRUE(success_); 159 ASSERT_TRUE(success_);
154 ASSERT_EQ(3U, prediction_.size()); 160 ASSERT_EQ(3U, prediction_.size());
155 ASSERT_DOUBLE_EQ(kDefaultEngagement + daily_budget, 161 ASSERT_DOUBLE_EQ(daily_budget * (kDefaultExpirationInDays + 1),
156 prediction_[0]->budget_at); 162 prediction_[0]->budget_at);
157 } 163 }
158 164
159 TEST_F(BudgetDatabaseTest, SpendBudgetTest) { 165 TEST_F(BudgetDatabaseTest, SpendBudgetTest) {
160 base::SimpleTestClock* clock = SetClockForTesting(); 166 base::SimpleTestClock* clock = SetClockForTesting();
161 167
162 // Set the default site engagement. 168 // Set the default site engagement.
163 SetSiteEngagementScore(kDefaultEngagement); 169 SetSiteEngagementScore(kDefaultEngagement);
164 170
165 // Intialize the budget with several chunks. 171 // Intialize the budget with several chunks.
166 GetBudgetDetails(); 172 GetBudgetDetails();
167 clock->Advance(base::TimeDelta::FromDays(1)); 173 clock->Advance(base::TimeDelta::FromDays(1));
168 GetBudgetDetails(); 174 GetBudgetDetails();
169 clock->Advance(base::TimeDelta::FromDays(1)); 175 clock->Advance(base::TimeDelta::FromDays(1));
170 GetBudgetDetails(); 176 GetBudgetDetails();
171 177
172 // Spend an amount of budget less than kDefaultEngagement. 178 // Spend an amount of budget less than the daily budget.
173 ASSERT_TRUE(SpendBudget(1)); 179 ASSERT_TRUE(SpendBudget(1));
174 GetBudgetDetails(); 180 GetBudgetDetails();
175 181
176 // There should still be three chunks of budget of size kDefaultEngagement-1, 182 // There should still be three chunks of budget of size daily_budget-1,
177 // kDefaultEngagement, and kDefaultEngagement. 183 // daily_budget, and kDefaultExpirationInDays * daily_budget.
184 double daily_budget = kMaxDailyBudget * kDefaultEngagement / kMaxSES;
178 ASSERT_EQ(4U, prediction_.size()); 185 ASSERT_EQ(4U, prediction_.size());
179 double daily_budget = kDefaultEngagement * 24 / kDefaultExpirationInHours; 186 ASSERT_DOUBLE_EQ((2 + kDefaultExpirationInDays) * daily_budget - 1,
180 ASSERT_DOUBLE_EQ(kDefaultEngagement + 2 * daily_budget - 1,
181 prediction_[0]->budget_at); 187 prediction_[0]->budget_at);
182 ASSERT_DOUBLE_EQ(daily_budget * 2, prediction_[1]->budget_at); 188 ASSERT_DOUBLE_EQ(daily_budget * 2, prediction_[1]->budget_at);
183 ASSERT_DOUBLE_EQ(daily_budget, prediction_[2]->budget_at); 189 ASSERT_DOUBLE_EQ(daily_budget, prediction_[2]->budget_at);
184 ASSERT_DOUBLE_EQ(0, prediction_[3]->budget_at); 190 ASSERT_DOUBLE_EQ(0, prediction_[3]->budget_at);
185 191
186 // Now spend enough that it will use up the rest of the first chunk and all of 192 // Now spend enough that it will use up the rest of the first chunk and all of
187 // the second chunk, but not all of the third chunk. 193 // the second chunk, but not all of the third chunk.
188 ASSERT_TRUE(SpendBudget(kDefaultEngagement + daily_budget)); 194 ASSERT_TRUE(SpendBudget((1 + kDefaultExpirationInDays) * daily_budget));
189 GetBudgetDetails(); 195 GetBudgetDetails();
190 ASSERT_EQ(2U, prediction_.size()); 196 ASSERT_EQ(2U, prediction_.size());
191 ASSERT_DOUBLE_EQ(daily_budget - 1, prediction_[0]->budget_at); 197 ASSERT_DOUBLE_EQ(daily_budget - 1, prediction_[0]->budget_at);
192 198
193 // Validate that the code returns false if SpendBudget tries to spend more 199 // Validate that the code returns false if SpendBudget tries to spend more
194 // budget than the origin has. 200 // budget than the origin has.
195 EXPECT_FALSE(SpendBudget(kDefaultEngagement)); 201 EXPECT_FALSE(SpendBudget(kDefaultEngagement));
196 GetBudgetDetails(); 202 GetBudgetDetails();
197 ASSERT_EQ(2U, prediction_.size()); 203 ASSERT_EQ(2U, prediction_.size());
198 ASSERT_DOUBLE_EQ(daily_budget - 1, prediction_[0]->budget_at); 204 ASSERT_DOUBLE_EQ(daily_budget - 1, prediction_[0]->budget_at);
199 205
200 // Advance time until the last remaining chunk should be expired, then query 206 // Advance time until the last remaining chunk should be expired, then query
201 // for the full engagement worth of budget. 207 // for the full engagement worth of budget.
202 clock->Advance(base::TimeDelta::FromHours(kDefaultExpirationInHours + 1)); 208 clock->Advance(base::TimeDelta::FromDays(kDefaultExpirationInDays + 1));
203 EXPECT_TRUE(SpendBudget(kDefaultEngagement)); 209 EXPECT_TRUE(SpendBudget(daily_budget * kDefaultExpirationInDays));
204 } 210 }
205 211
206 // There are times when a device's clock could move backwards in time, either 212 // There are times when a device's clock could move backwards in time, either
207 // due to hardware issues or user actions. Test here to make sure that even if 213 // due to hardware issues or user actions. Test here to make sure that even if
208 // time goes backwards and then forwards again, the origin isn't granted extra 214 // time goes backwards and then forwards again, the origin isn't granted extra
209 // budget. 215 // budget.
210 TEST_F(BudgetDatabaseTest, GetBudgetNegativeTime) { 216 TEST_F(BudgetDatabaseTest, GetBudgetNegativeTime) {
211 base::SimpleTestClock* clock = SetClockForTesting(); 217 base::SimpleTestClock* clock = SetClockForTesting();
212 218
213 // Set the default site engagement. 219 // Set the default site engagement.
(...skipping 27 matching lines...) Expand all
241 TEST_F(BudgetDatabaseTest, CheckBackgroundBudgetHistogram) { 247 TEST_F(BudgetDatabaseTest, CheckBackgroundBudgetHistogram) {
242 base::SimpleTestClock* clock = SetClockForTesting(); 248 base::SimpleTestClock* clock = SetClockForTesting();
243 249
244 // Set the default site engagement. 250 // Set the default site engagement.
245 SetSiteEngagementScore(kDefaultEngagement); 251 SetSiteEngagementScore(kDefaultEngagement);
246 252
247 // Initialize the budget with some interesting chunks: 30 budget (full 253 // Initialize the budget with some interesting chunks: 30 budget (full
248 // engagement), 15 budget (half of the engagement), 0 budget (less than an 254 // engagement), 15 budget (half of the engagement), 0 budget (less than an
249 // hour), and then after the first two expire, another 30 budget. 255 // hour), and then after the first two expire, another 30 budget.
250 GetBudgetDetails(); 256 GetBudgetDetails();
251 clock->Advance(base::TimeDelta::FromHours(kDefaultExpirationInHours / 2)); 257 clock->Advance(base::TimeDelta::FromDays(kDefaultExpirationInDays / 2));
252 GetBudgetDetails(); 258 GetBudgetDetails();
253 clock->Advance(base::TimeDelta::FromMinutes(59)); 259 clock->Advance(base::TimeDelta::FromMinutes(59));
254 GetBudgetDetails(); 260 GetBudgetDetails();
255 clock->Advance(base::TimeDelta::FromHours(kDefaultExpirationInHours + 1)); 261 clock->Advance(base::TimeDelta::FromDays(kDefaultExpirationInDays + 1));
256 GetBudgetDetails(); 262 GetBudgetDetails();
257 263
258 // The BackgroundBudget UMA is recorded when budget is added to the origin. 264 // The BackgroundBudget UMA is recorded when budget is added to the origin.
259 // This can happen a maximum of once per hour so there should be two entries. 265 // This can happen a maximum of once per hour so there should be two entries.
260 std::vector<base::Bucket> buckets = 266 std::vector<base::Bucket> buckets =
261 GetHistogramTester()->GetAllSamples("PushMessaging.BackgroundBudget"); 267 GetHistogramTester()->GetAllSamples("PushMessaging.BackgroundBudget");
262 ASSERT_EQ(2U, buckets.size()); 268 ASSERT_EQ(2U, buckets.size());
263 // First bucket is for full engagement, which should have 2 entries. 269 // First bucket is for full award, which should have 2 entries.
264 EXPECT_EQ(kDefaultEngagement, buckets[0].min); 270 double full_award =
271 kMaxDailyBudget * kDefaultEngagement / kMaxSES * kDefaultExpirationInDays;
272 EXPECT_EQ(floor(full_award), buckets[0].min);
Peter Beverloo 2017/01/12 14:40:04 Out of interest, why is this necessary?
harkness 2017/01/12 17:44:15 The buckets in the histograms have a min set to in
265 EXPECT_EQ(2, buckets[0].count); 273 EXPECT_EQ(2, buckets[0].count);
266 // Second bucket is for 1.5 * engagement, which should have 1 entry. 274 // Second bucket is for 1.5 * award, which should have 1 entry.
267 EXPECT_EQ(kDefaultEngagement * 1.5, buckets[1].min); 275 EXPECT_EQ(floor(full_award * 1.5), buckets[1].min);
268 EXPECT_EQ(1, buckets[1].count); 276 EXPECT_EQ(1, buckets[1].count);
269 } 277 }
270 278
271 TEST_F(BudgetDatabaseTest, CheckEngagementHistograms) { 279 TEST_F(BudgetDatabaseTest, CheckEngagementHistograms) {
272 base::SimpleTestClock* clock = SetClockForTesting(); 280 base::SimpleTestClock* clock = SetClockForTesting();
273 281
274 // Set the engagement to twice the cost of an action. 282 // Manipulate the engagement so that the budget is twice the cost of an
283 // action.
275 double cost = 2; 284 double cost = 2;
276 double engagement = cost * 2; 285 double engagement =
286 2 * cost * kMaxSES / kDefaultExpirationInDays / kMaxDailyBudget;
277 SetSiteEngagementScore(engagement); 287 SetSiteEngagementScore(engagement);
278 288
279 // Get the budget, which will award a chunk of budget equal to engagement. 289 // Get the budget, which will award a chunk of budget equal to engagement.
280 GetBudgetDetails(); 290 GetBudgetDetails();
281 291
282 // Now spend the budget to trigger the UMA recording the SES score. The first 292 // Now spend the budget to trigger the UMA recording the SES score. The first
283 // call shouldn't write any UMA. The second should write a lowSES entry, and 293 // call shouldn't write any UMA. The second should write a lowSES entry, and
284 // the third should write a noSES entry. 294 // the third should write a noSES entry.
285 ASSERT_TRUE(SpendBudget(cost)); 295 ASSERT_TRUE(SpendBudget(cost));
286 ASSERT_TRUE(SpendBudget(cost)); 296 ASSERT_TRUE(SpendBudget(cost));
287 ASSERT_FALSE(SpendBudget(cost)); 297 ASSERT_FALSE(SpendBudget(cost));
288 298
289 // Advance the clock by 12 days (to guarantee a full new engagement grant) 299 // Advance the clock by 12 days (to guarantee a full new engagement grant)
290 // then change the SES score to get a different UMA entry, then spend the 300 // then change the SES score to get a different UMA entry, then spend the
291 // budget again. 301 // budget again.
292 clock->Advance(base::TimeDelta::FromDays(12)); 302 clock->Advance(base::TimeDelta::FromDays(12));
293 GetBudgetDetails(); 303 GetBudgetDetails();
294 SetSiteEngagementScore(engagement * 2); 304 SetSiteEngagementScore(engagement * 2);
295 ASSERT_TRUE(SpendBudget(cost)); 305 ASSERT_TRUE(SpendBudget(cost));
296 ASSERT_TRUE(SpendBudget(cost)); 306 ASSERT_TRUE(SpendBudget(cost));
297 ASSERT_FALSE(SpendBudget(cost)); 307 ASSERT_FALSE(SpendBudget(cost));
298 308
299 // Now check the UMA. Both UMA should have 2 buckets with 1 entry each. 309 // Now check the UMA. Both UMA should have 2 buckets with 1 entry each.
300 std::vector<base::Bucket> no_budget_buckets = 310 std::vector<base::Bucket> no_budget_buckets =
301 GetHistogramTester()->GetAllSamples("PushMessaging.SESForNoBudgetOrigin"); 311 GetHistogramTester()->GetAllSamples("PushMessaging.SESForNoBudgetOrigin");
302 ASSERT_EQ(2U, no_budget_buckets.size()); 312 ASSERT_EQ(2U, no_budget_buckets.size());
303 EXPECT_EQ(engagement, no_budget_buckets[0].min); 313 EXPECT_EQ(floor(engagement), no_budget_buckets[0].min);
304 EXPECT_EQ(1, no_budget_buckets[0].count); 314 EXPECT_EQ(1, no_budget_buckets[0].count);
305 EXPECT_EQ(engagement * 2, no_budget_buckets[1].min); 315 EXPECT_EQ(floor(engagement * 2), no_budget_buckets[1].min);
306 EXPECT_EQ(1, no_budget_buckets[1].count); 316 EXPECT_EQ(1, no_budget_buckets[1].count);
307 317
308 std::vector<base::Bucket> low_budget_buckets = 318 std::vector<base::Bucket> low_budget_buckets =
309 GetHistogramTester()->GetAllSamples( 319 GetHistogramTester()->GetAllSamples(
310 "PushMessaging.SESForLowBudgetOrigin"); 320 "PushMessaging.SESForLowBudgetOrigin");
311 ASSERT_EQ(2U, low_budget_buckets.size()); 321 ASSERT_EQ(2U, low_budget_buckets.size());
312 EXPECT_EQ(engagement, low_budget_buckets[0].min); 322 EXPECT_EQ(floor(engagement), low_budget_buckets[0].min);
313 EXPECT_EQ(1, low_budget_buckets[0].count); 323 EXPECT_EQ(1, low_budget_buckets[0].count);
314 EXPECT_EQ(engagement * 2, low_budget_buckets[1].min); 324 EXPECT_EQ(floor(engagement * 2), low_budget_buckets[1].min);
315 EXPECT_EQ(1, low_budget_buckets[1].count); 325 EXPECT_EQ(1, low_budget_buckets[1].count);
316 } 326 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698