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

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

Issue 2255813002: AddEngagementBudget and SpendBudget added to BudgetDatabase. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Code review comments Created 4 years, 4 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 "base/run_loop.h" 7 #include "base/run_loop.h"
8 #include "base/test/simple_test_clock.h" 8 #include "base/test/simple_test_clock.h"
9 #include "base/threading/thread_task_runner_handle.h" 9 #include "base/threading/thread_task_runner_handle.h"
10 #include "chrome/browser/budget_service/budget.pb.h" 10 #include "chrome/browser/budget_service/budget.pb.h"
11 #include "chrome/test/base/testing_profile.h" 11 #include "chrome/test/base/testing_profile.h"
12 #include "components/leveldb_proto/proto_database.h" 12 #include "components/leveldb_proto/proto_database.h"
13 #include "components/leveldb_proto/proto_database_impl.h" 13 #include "components/leveldb_proto/proto_database_impl.h"
14 #include "content/public/browser/browser_thread.h" 14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/test/test_browser_thread_bundle.h" 15 #include "content/public/test/test_browser_thread_bundle.h"
16 #include "testing/gtest/include/gtest/gtest.h" 16 #include "testing/gtest/include/gtest/gtest.h"
17 17
18 namespace { 18 namespace {
19 19
20 const double kDefaultBudget1 = 1.234; 20 const double kDefaultBudget1 = 1.234;
21 const double kDefaultBudget2 = 2.345; 21 const double kDefaultBudget2 = 2.345;
22 const double kDefaultExpirationInHours = 72; 22 const double kDefaultExpirationInHours = 72;
23 const double kDefaultEngagement = 30.0;
23 24
24 const char kTestOrigin[] = "https://example.com"; 25 const char kTestOrigin[] = "https://example.com";
25 26
26 } // namespace 27 } // namespace
27 28
28 class BudgetDatabaseTest : public ::testing::Test { 29 class BudgetDatabaseTest : public ::testing::Test {
29 public: 30 public:
30 BudgetDatabaseTest() 31 BudgetDatabaseTest()
31 : success_(false), 32 : success_(false),
32 db_(profile_.GetPath().Append(FILE_PATH_LITERAL("BudgetDabase")), 33 db_(profile_.GetPath().Append(FILE_PATH_LITERAL("BudgetDabase")),
33 base::ThreadTaskRunnerHandle::Get()) {} 34 base::ThreadTaskRunnerHandle::Get()) {}
34 35
35 // The BudgetDatabase assumes that a budget will always be queried before it 36 // The BudgetDatabase assumes that a budget will always be queried before it
36 // is written to. Use GetBudgetDetails() to pre-populate the cache. 37 // is written to. Use GetBudgetDetails() to pre-populate the cache.
37 void SetUp() override { GetBudgetDetails(); } 38 void SetUp() override { GetBudgetDetails(); }
38 39
39 void AddBudgetComplete(base::Closure run_loop_closure, bool success) { 40 void WriteBudgetComplete(base::Closure run_loop_closure, bool success) {
40 success_ = success; 41 success_ = success;
41 run_loop_closure.Run(); 42 run_loop_closure.Run();
42 } 43 }
43 44
44 // Add budget to the origin. 45 // Add budget to the origin.
45 bool AddBudget(const GURL& origin, double amount) { 46 bool AddBudget(const GURL& origin, double amount) {
46 base::RunLoop run_loop; 47 base::RunLoop run_loop;
47 db_.AddBudget(origin, amount, 48 db_.AddBudget(origin, amount,
48 base::Bind(&BudgetDatabaseTest::AddBudgetComplete, 49 base::Bind(&BudgetDatabaseTest::WriteBudgetComplete,
49 base::Unretained(this), run_loop.QuitClosure())); 50 base::Unretained(this), run_loop.QuitClosure()));
50 run_loop.Run(); 51 run_loop.Run();
51 return success_; 52 return success_;
52 } 53 }
53 54
55 // Add engagement based budget to the origin.
56 bool AddEngagementBudget(const GURL& origin, double sesScore) {
57 base::RunLoop run_loop;
58 db_.AddEngagementBudget(
59 origin, sesScore,
60 base::Bind(&BudgetDatabaseTest::WriteBudgetComplete,
61 base::Unretained(this), run_loop.QuitClosure()));
62 run_loop.Run();
63 return success_;
64 }
65
66 // Spend budget for the origin.
67 bool SpendBudget(const GURL& origin, double amount) {
68 base::RunLoop run_loop;
69 db_.SpendBudget(origin, amount,
70 base::Bind(&BudgetDatabaseTest::WriteBudgetComplete,
71 base::Unretained(this), run_loop.QuitClosure()));
72 run_loop.Run();
73 return success_;
74 }
75
54 void GetBudgetDetailsComplete( 76 void GetBudgetDetailsComplete(
55 base::Closure run_loop_closure, 77 base::Closure run_loop_closure,
56 bool success, 78 bool success,
57 const BudgetDatabase::BudgetExpectation& expectation) { 79 const BudgetDatabase::BudgetPrediction& prediction) {
58 success_ = success; 80 success_ = success;
59 expectation_ = expectation; 81 // Convert BudgetPrediction to a vector for random access to check values.
82 prediction_.clear();
johnme 2016/08/22 14:50:31 You can probably just do prediction_.assign(predic
harkness 2016/08/23 09:55:56 Good point. Done.
83 for (auto& chunk : prediction)
84 prediction_.push_back(chunk);
60 run_loop_closure.Run(); 85 run_loop_closure.Run();
61 } 86 }
62 87
63 // Get the full set of budget expectations for the origin. 88 // Get the full set of budget predictions for the origin.
64 void GetBudgetDetails() { 89 void GetBudgetDetails() {
65 base::RunLoop run_loop; 90 base::RunLoop run_loop;
66 db_.GetBudgetDetails( 91 db_.GetBudgetDetails(
67 GURL(kTestOrigin), 92 GURL(kTestOrigin),
68 base::Bind(&BudgetDatabaseTest::GetBudgetDetailsComplete, 93 base::Bind(&BudgetDatabaseTest::GetBudgetDetailsComplete,
69 base::Unretained(this), run_loop.QuitClosure())); 94 base::Unretained(this), run_loop.QuitClosure()));
70 run_loop.Run(); 95 run_loop.Run();
71 } 96 }
72 97
73 Profile* profile() { return &profile_; } 98 Profile* profile() { return &profile_; }
74 const BudgetDatabase::BudgetExpectation& expectation() {
75 return expectation_;
76 }
77 99
78 // Setup a test clock so that the tests can control time. 100 // Setup a test clock so that the tests can control time.
79 base::SimpleTestClock* SetClockForTesting() { 101 base::SimpleTestClock* SetClockForTesting() {
80 base::SimpleTestClock* clock = new base::SimpleTestClock(); 102 base::SimpleTestClock* clock = new base::SimpleTestClock();
81 db_.SetClockForTesting(base::WrapUnique(clock)); 103 db_.SetClockForTesting(base::WrapUnique(clock));
82 return clock; 104 return clock;
83 } 105 }
84 106
85 // Query the database to check if the origin is in the cache. 107 // Query the database to check if the origin is in the cache.
86 bool IsCached(const GURL& origin) { return db_.IsCached(origin); } 108 bool IsCached(const GURL& origin) { return db_.IsCached(origin); }
87 109
88 protected: 110 protected:
89 bool success_; 111 bool success_;
112 std::vector<BudgetDatabase::BudgetStatus> prediction_;
90 113
91 private: 114 private:
92 content::TestBrowserThreadBundle thread_bundle_; 115 content::TestBrowserThreadBundle thread_bundle_;
93 std::unique_ptr<budget_service::Budget> budget_; 116 std::unique_ptr<budget_service::Budget> budget_;
94 TestingProfile profile_; 117 TestingProfile profile_;
95 BudgetDatabase db_; 118 BudgetDatabase db_;
96 BudgetDatabase::BudgetExpectation expectation_;
97 }; 119 };
98 120
99 TEST_F(BudgetDatabaseTest, ReadAndWriteTest) { 121 TEST_F(BudgetDatabaseTest, ReadAndWriteTest) {
100 const GURL origin(kTestOrigin); 122 const GURL origin(kTestOrigin);
101 base::SimpleTestClock* clock = SetClockForTesting(); 123 base::SimpleTestClock* clock = SetClockForTesting();
102 base::TimeDelta expiration( 124 base::TimeDelta expiration(
103 base::TimeDelta::FromHours(kDefaultExpirationInHours)); 125 base::TimeDelta::FromHours(kDefaultExpirationInHours));
104 base::Time starting_time = clock->Now(); 126 base::Time starting_time = clock->Now();
105 base::Time expiration_time = clock->Now() + expiration; 127 base::Time expiration_time = clock->Now() + expiration;
106 128
107 // Add two budget chunks with different expirations (default expiration and 129 // Add two budget chunks with different expirations (default expiration and
108 // default expiration + 1 day). 130 // default expiration + 1 day).
109 ASSERT_TRUE(AddBudget(origin, kDefaultBudget1)); 131 ASSERT_TRUE(AddBudget(origin, kDefaultBudget1));
110 clock->Advance(base::TimeDelta::FromDays(1)); 132 clock->Advance(base::TimeDelta::FromDays(1));
111 ASSERT_TRUE(AddBudget(origin, kDefaultBudget2)); 133 ASSERT_TRUE(AddBudget(origin, kDefaultBudget2));
112 134
113 // Get the budget. 135 // Get the budget.
114 GetBudgetDetails(); 136 GetBudgetDetails();
115 137
116 // Get the expectation and validate it. 138 // Get the prediction and validate it.
117 const auto& expected_value = expectation();
118 ASSERT_TRUE(success_); 139 ASSERT_TRUE(success_);
119 ASSERT_EQ(3U, expected_value.size()); 140 ASSERT_EQ(3U, prediction_.size());
120 141
121 // Make sure that the correct data is returned. 142 // Make sure that the correct data is returned.
122 auto iter = expected_value.begin();
123
124 // First value should be [total_budget, now] 143 // First value should be [total_budget, now]
125 EXPECT_EQ(kDefaultBudget1 + kDefaultBudget2, iter->budget_at); 144 EXPECT_EQ(kDefaultBudget1 + kDefaultBudget2, prediction_[0].budget_at);
126 EXPECT_EQ(clock->Now(), iter->time); 145 EXPECT_EQ(clock->Now(), prediction_[0].time);
127 146
128 // The next value should be the budget after the first chunk expires. 147 // The next value should be the budget after the first chunk expires.
129 iter++; 148 EXPECT_EQ(kDefaultBudget2, prediction_[1].budget_at);
130 EXPECT_EQ(kDefaultBudget2, iter->budget_at); 149 EXPECT_EQ(expiration_time, prediction_[1].time);
131 EXPECT_EQ(expiration_time, iter->time);
132 150
133 // The final value gives the budget of 0.0 after the second chunk expires. 151 // The final value gives the budget of 0.0 after the second chunk expires.
134 expiration_time += base::TimeDelta::FromDays(1); 152 expiration_time += base::TimeDelta::FromDays(1);
135 iter++; 153 EXPECT_EQ(0, prediction_[2].budget_at);
136 EXPECT_EQ(0, iter->budget_at); 154 EXPECT_EQ(expiration_time, prediction_[2].time);
137 EXPECT_EQ(expiration_time, iter->time);
138 155
139 // Advance the time until the first chunk of budget should be expired. 156 // Advance the time until the first chunk of budget should be expired.
140 clock->SetNow(starting_time + 157 clock->SetNow(starting_time +
141 base::TimeDelta::FromHours(kDefaultExpirationInHours)); 158 base::TimeDelta::FromHours(kDefaultExpirationInHours));
142 159
143 // Get the new budget and check that kDefaultBudget1 has been removed. 160 // Get the new budget and check that kDefaultBudget1 has been removed.
144 GetBudgetDetails(); 161 GetBudgetDetails();
145 iter = expectation().begin(); 162 ASSERT_EQ(2U, prediction_.size());
146 ASSERT_EQ(2U, expectation().size()); 163 EXPECT_EQ(kDefaultBudget2, prediction_[0].budget_at);
147 EXPECT_EQ(kDefaultBudget2, iter->budget_at); 164 EXPECT_EQ(0, prediction_[1].budget_at);
148 iter++;
149 EXPECT_EQ(0, iter->budget_at);
150 165
151 // Advace the time until both chunks of budget should be expired. 166 // Advace the time until both chunks of budget should be expired.
152 clock->SetNow(starting_time + 167 clock->SetNow(starting_time +
153 base::TimeDelta::FromHours(kDefaultExpirationInHours) + 168 base::TimeDelta::FromHours(kDefaultExpirationInHours) +
154 base::TimeDelta::FromDays(1)); 169 base::TimeDelta::FromDays(1));
155 170
156 GetBudgetDetails(); 171 GetBudgetDetails();
157 iter = expectation().begin(); 172 ASSERT_EQ(1U, prediction_.size());
158 ASSERT_EQ(1U, expectation().size()); 173 EXPECT_EQ(0, prediction_[0].budget_at);
159 EXPECT_EQ(0, iter->budget_at);
160 174
161 // Now that the entire budget has expired, check that the entry in the map 175 // Now that the entire budget has expired, check that the entry in the map
162 // has been removed. 176 // has been removed.
163 EXPECT_FALSE(IsCached(origin)); 177 EXPECT_FALSE(IsCached(origin));
164 } 178 }
179
180 TEST_F(BudgetDatabaseTest, AddEngagementBudgetTest) {
181 const GURL origin(kTestOrigin);
182 base::SimpleTestClock* clock = SetClockForTesting();
183 base::Time expirationTime =
Peter Beverloo 2016/08/22 18:04:27 nit: s/expirationTime/expiration_time/
harkness 2016/08/23 09:55:56 Done.
184 clock->Now() + base::TimeDelta::FromHours(kDefaultExpirationInHours);
185
186 // Add a chunk of budget to a non-existant origin. This should add the full
187 // amount of engagement.
188 ASSERT_TRUE(AddEngagementBudget(origin, kDefaultEngagement));
189
190 // The budget should include a full share of the engagement.
191 GetBudgetDetails();
192 ASSERT_TRUE(success_);
193 ASSERT_EQ(2U, prediction_.size());
194 ASSERT_EQ(kDefaultEngagement, prediction_[0].budget_at);
195 ASSERT_EQ(0, prediction_[1].budget_at);
196 ASSERT_EQ(expirationTime, prediction_[1].time);
197
198 // Advance time 1 day and add more engagement budget.
199 clock->Advance(base::TimeDelta::FromDays(1));
200 ASSERT_TRUE(AddEngagementBudget(origin, kDefaultEngagement));
201
202 // The budget should now have 1 full share plus 1/3 share.
203 GetBudgetDetails();
204 ASSERT_TRUE(success_);
205 ASSERT_EQ(3U, prediction_.size());
206 ASSERT_DOUBLE_EQ(kDefaultEngagement * 4 / 3, prediction_[0].budget_at);
207 ASSERT_DOUBLE_EQ(kDefaultEngagement * 1 / 3, prediction_[1].budget_at);
208 ASSERT_EQ(expirationTime, prediction_[1].time);
209 ASSERT_EQ(0, prediction_[2].budget_at);
210 ASSERT_EQ(expirationTime + base::TimeDelta::FromDays(1), prediction_[2].time);
211
212 // Advance time by 59 minutes and check that no engagement budget is added
213 // since budget should only be added for > 1 hour increments.
214 clock->Advance(base::TimeDelta::FromMinutes(59));
215 ASSERT_TRUE(AddEngagementBudget(origin, kDefaultEngagement));
216
217 // The budget should be the same as before the attempted add.
218 GetBudgetDetails();
219 ASSERT_TRUE(success_);
220 ASSERT_EQ(3U, prediction_.size());
221 ASSERT_DOUBLE_EQ(kDefaultEngagement * 4 / 3, prediction_[0].budget_at);
222 }
223
224 TEST_F(BudgetDatabaseTest, SpendBudgetTest) {
225 const GURL origin(kTestOrigin);
226 base::SimpleTestClock* clock = SetClockForTesting();
227 base::Time starting_time = clock->Now();
228
229 // Intialize the budget with several chunks.
230 ASSERT_TRUE(AddBudget(origin, kDefaultBudget1));
231 clock->Advance(base::TimeDelta::FromDays(1));
232 ASSERT_TRUE(AddBudget(origin, kDefaultBudget1));
233 clock->Advance(base::TimeDelta::FromDays(1));
234 ASSERT_TRUE(AddBudget(origin, kDefaultBudget1));
235
236 // Reset the clock then spend an amount of budget less than kDefaultBudget.
237 clock->SetNow(starting_time);
238 ASSERT_TRUE(SpendBudget(origin, 1));
239 GetBudgetDetails();
240
241 // There should still be three chunks of budget of size kDefaultBudget-1,
242 // kDefaultBudget, and kDefaultBudget.
243 ASSERT_EQ(4U, prediction_.size());
244 ASSERT_DOUBLE_EQ(kDefaultBudget1 * 3 - 1, prediction_[0].budget_at);
245 ASSERT_DOUBLE_EQ(kDefaultBudget1 * 2, prediction_[1].budget_at);
246 ASSERT_DOUBLE_EQ(kDefaultBudget1, prediction_[2].budget_at);
247 ASSERT_DOUBLE_EQ(0, prediction_[3].budget_at);
248
249 // Now spend enough that it will use up the rest of the first chunk and all of
250 // the second chunk, but not all of the third chunk.
251 ASSERT_TRUE(SpendBudget(origin, kDefaultBudget1 * 2));
252 GetBudgetDetails();
253 ASSERT_EQ(2U, prediction_.size());
254 ASSERT_DOUBLE_EQ(kDefaultBudget1 - 1, prediction_.begin()->budget_at);
255
256 // Validate that the code returns false if SpendBudget tries to spend more
257 // budget than the origin has.
258 EXPECT_FALSE(SpendBudget(origin, kDefaultBudget1));
259 GetBudgetDetails();
260 ASSERT_EQ(2U, prediction_.size());
261 ASSERT_DOUBLE_EQ(kDefaultBudget1 - 1, prediction_.begin()->budget_at);
262
263 // Advance time until the last remaining chunk should be expired, then query
264 // for what would be a valid amount of budget if the chunks weren't expired.
265 clock->Advance(base::TimeDelta::FromDays(6));
266 EXPECT_FALSE(SpendBudget(origin, 0.01));
267 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698