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

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

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

Powered by Google App Engine
This is Rietveld 408576698