| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "components/offline_pages/background/request_picker.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/test/test_simple_task_runner.h" | |
| 9 #include "base/threading/thread_task_runner_handle.h" | |
| 10 #include "base/time/time.h" | |
| 11 #include "components/offline_pages/background/device_conditions.h" | |
| 12 #include "components/offline_pages/background/offliner_factory.h" | |
| 13 #include "components/offline_pages/background/offliner_policy.h" | |
| 14 #include "components/offline_pages/background/request_notifier.h" | |
| 15 #include "components/offline_pages/background/request_queue.h" | |
| 16 #include "components/offline_pages/background/request_queue_in_memory_store.h" | |
| 17 #include "components/offline_pages/background/save_page_request.h" | |
| 18 #include "components/offline_pages/offline_page_item.h" | |
| 19 #include "testing/gtest/include/gtest/gtest.h" | |
| 20 | |
| 21 namespace offline_pages { | |
| 22 | |
| 23 namespace { | |
| 24 // Data for request 1. | |
| 25 const int64_t kRequestId1 = 17; | |
| 26 const GURL kUrl1("https://google.com"); | |
| 27 const ClientId kClientId1("bookmark", "1234"); | |
| 28 // Data for request 2. | |
| 29 const int64_t kRequestId2 = 42; | |
| 30 const GURL kUrl2("http://nytimes.com"); | |
| 31 const ClientId kClientId2("bookmark", "5678"); | |
| 32 const bool kUserRequested = true; | |
| 33 const int kAttemptCount = 1; | |
| 34 const int kMaxStartedTries = 5; | |
| 35 const int kMaxCompletedTries = 1; | |
| 36 | |
| 37 // Constants for policy values - These settings represent the default values. | |
| 38 const bool kPreferUntried = false; | |
| 39 const bool kPreferEarlier = true; | |
| 40 const bool kPreferRetryCount = true; | |
| 41 const int kBackgroundProcessingTimeBudgetSeconds = 170; | |
| 42 | |
| 43 // Default request | |
| 44 const SavePageRequest kEmptyRequest(0UL, | |
| 45 GURL(""), | |
| 46 ClientId("", ""), | |
| 47 base::Time(), | |
| 48 true); | |
| 49 } // namespace | |
| 50 | |
| 51 class RequestNotifierStub : public RequestNotifier { | |
| 52 public: | |
| 53 RequestNotifierStub() | |
| 54 : last_expired_request_(kEmptyRequest), total_expired_requests_(0) {} | |
| 55 | |
| 56 void NotifyAdded(const SavePageRequest& request) override {} | |
| 57 void NotifyChanged(const SavePageRequest& request) override {} | |
| 58 | |
| 59 void NotifyCompleted(const SavePageRequest& request, | |
| 60 BackgroundSavePageResult status) override { | |
| 61 last_expired_request_ = request; | |
| 62 last_request_expiration_status_ = status; | |
| 63 total_expired_requests_++; | |
| 64 } | |
| 65 | |
| 66 const SavePageRequest& last_expired_request() { | |
| 67 return last_expired_request_; | |
| 68 } | |
| 69 | |
| 70 RequestCoordinator::BackgroundSavePageResult | |
| 71 last_request_expiration_status() { | |
| 72 return last_request_expiration_status_; | |
| 73 } | |
| 74 | |
| 75 int32_t total_expired_requests() { return total_expired_requests_; } | |
| 76 | |
| 77 private: | |
| 78 BackgroundSavePageResult last_request_expiration_status_; | |
| 79 SavePageRequest last_expired_request_; | |
| 80 int32_t total_expired_requests_; | |
| 81 }; | |
| 82 | |
| 83 class RequestPickerTest : public testing::Test { | |
| 84 public: | |
| 85 RequestPickerTest(); | |
| 86 | |
| 87 ~RequestPickerTest() override; | |
| 88 | |
| 89 void SetUp() override; | |
| 90 | |
| 91 void PumpLoop(); | |
| 92 | |
| 93 void AddRequestDone(RequestQueue::AddRequestResult result, | |
| 94 const SavePageRequest& request); | |
| 95 | |
| 96 void RequestPicked(const SavePageRequest& request); | |
| 97 | |
| 98 void RequestNotPicked(const bool non_user_requested_tasks_remaining); | |
| 99 | |
| 100 void QueueRequestsAndChooseOne(const SavePageRequest& request1, | |
| 101 const SavePageRequest& request2); | |
| 102 | |
| 103 RequestNotifierStub* GetNotifier() { return notifier_.get(); } | |
| 104 | |
| 105 protected: | |
| 106 // The request queue is simple enough we will use a real queue with a memory | |
| 107 // store instead of a stub. | |
| 108 std::unique_ptr<RequestQueue> queue_; | |
| 109 std::unique_ptr<RequestPicker> picker_; | |
| 110 std::unique_ptr<RequestNotifierStub> notifier_; | |
| 111 std::unique_ptr<SavePageRequest> last_picked_; | |
| 112 std::unique_ptr<OfflinerPolicy> policy_; | |
| 113 RequestCoordinatorEventLogger event_logger_; | |
| 114 bool request_queue_not_picked_called_; | |
| 115 | |
| 116 private: | |
| 117 scoped_refptr<base::TestSimpleTaskRunner> task_runner_; | |
| 118 base::ThreadTaskRunnerHandle task_runner_handle_; | |
| 119 }; | |
| 120 | |
| 121 RequestPickerTest::RequestPickerTest() | |
| 122 : task_runner_(new base::TestSimpleTaskRunner), | |
| 123 task_runner_handle_(task_runner_) {} | |
| 124 | |
| 125 RequestPickerTest::~RequestPickerTest() {} | |
| 126 | |
| 127 void RequestPickerTest::SetUp() { | |
| 128 std::unique_ptr<RequestQueueInMemoryStore> store( | |
| 129 new RequestQueueInMemoryStore()); | |
| 130 queue_.reset(new RequestQueue(std::move(store))); | |
| 131 policy_.reset(new OfflinerPolicy()); | |
| 132 notifier_.reset(new RequestNotifierStub()); | |
| 133 picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(), | |
| 134 &event_logger_)); | |
| 135 request_queue_not_picked_called_ = false; | |
| 136 } | |
| 137 | |
| 138 void RequestPickerTest::PumpLoop() { | |
| 139 task_runner_->RunUntilIdle(); | |
| 140 } | |
| 141 | |
| 142 void RequestPickerTest::AddRequestDone(RequestQueue::AddRequestResult result, | |
| 143 const SavePageRequest& request) {} | |
| 144 | |
| 145 void RequestPickerTest::RequestPicked(const SavePageRequest& request) { | |
| 146 last_picked_.reset(new SavePageRequest(request)); | |
| 147 } | |
| 148 | |
| 149 void RequestPickerTest::RequestNotPicked( | |
| 150 const bool non_user_requested_tasks_remaining) { | |
| 151 request_queue_not_picked_called_ = true; | |
| 152 } | |
| 153 | |
| 154 // Test helper to queue the two given requests and then pick one of them per | |
| 155 // configured policy. | |
| 156 void RequestPickerTest::QueueRequestsAndChooseOne( | |
| 157 const SavePageRequest& request1, const SavePageRequest& request2) { | |
| 158 DeviceConditions conditions; | |
| 159 std::set<int64_t> disabled_requests; | |
| 160 // Add test requests on the Queue. | |
| 161 queue_->AddRequest(request1, base::Bind(&RequestPickerTest::AddRequestDone, | |
| 162 base::Unretained(this))); | |
| 163 queue_->AddRequest(request2, base::Bind(&RequestPickerTest::AddRequestDone, | |
| 164 base::Unretained(this))); | |
| 165 | |
| 166 // Pump the loop to give the async queue the opportunity to do the adds. | |
| 167 PumpLoop(); | |
| 168 | |
| 169 // Call the method under test. | |
| 170 picker_->ChooseNextRequest( | |
| 171 base::Bind(&RequestPickerTest::RequestPicked, base::Unretained(this)), | |
| 172 base::Bind(&RequestPickerTest::RequestNotPicked, base::Unretained(this)), | |
| 173 &conditions, disabled_requests); | |
| 174 | |
| 175 // Pump the loop again to give the async queue the opportunity to return | |
| 176 // results from the Get operation, and for the picker to call the "picked" | |
| 177 // callback. | |
| 178 PumpLoop(); | |
| 179 } | |
| 180 | |
| 181 TEST_F(RequestPickerTest, PickFromEmptyQueue) { | |
| 182 DeviceConditions conditions; | |
| 183 std::set<int64_t> disabled_requests; | |
| 184 picker_->ChooseNextRequest( | |
| 185 base::Bind(&RequestPickerTest::RequestPicked, base::Unretained(this)), | |
| 186 base::Bind(&RequestPickerTest::RequestNotPicked, base::Unretained(this)), | |
| 187 &conditions, disabled_requests); | |
| 188 | |
| 189 // Pump the loop again to give the async queue the opportunity to return | |
| 190 // results from the Get operation, and for the picker to call the "QueueEmpty" | |
| 191 // callback. | |
| 192 PumpLoop(); | |
| 193 | |
| 194 EXPECT_TRUE(request_queue_not_picked_called_); | |
| 195 } | |
| 196 | |
| 197 TEST_F(RequestPickerTest, ChooseRequestWithHigherRetryCount) { | |
| 198 policy_.reset(new OfflinerPolicy( | |
| 199 kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries, | |
| 200 kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds)); | |
| 201 picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(), | |
| 202 &event_logger_)); | |
| 203 | |
| 204 base::Time creation_time = base::Time::Now(); | |
| 205 SavePageRequest request1( | |
| 206 kRequestId1, kUrl1, kClientId1, creation_time, kUserRequested); | |
| 207 SavePageRequest request2( | |
| 208 kRequestId2, kUrl2, kClientId2, creation_time, kUserRequested); | |
| 209 request2.set_completed_attempt_count(kAttemptCount); | |
| 210 | |
| 211 QueueRequestsAndChooseOne(request1, request2); | |
| 212 | |
| 213 EXPECT_EQ(kRequestId2, last_picked_->request_id()); | |
| 214 EXPECT_FALSE(request_queue_not_picked_called_); | |
| 215 } | |
| 216 | |
| 217 TEST_F(RequestPickerTest, ChooseRequestWithSameRetryCountButEarlier) { | |
| 218 base::Time creation_time1 = | |
| 219 base::Time::Now() - base::TimeDelta::FromSeconds(10); | |
| 220 base::Time creation_time2 = base::Time::Now(); | |
| 221 SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1, | |
| 222 kUserRequested); | |
| 223 SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2, | |
| 224 kUserRequested); | |
| 225 | |
| 226 QueueRequestsAndChooseOne(request1, request2); | |
| 227 | |
| 228 EXPECT_EQ(kRequestId1, last_picked_->request_id()); | |
| 229 EXPECT_FALSE(request_queue_not_picked_called_); | |
| 230 } | |
| 231 | |
| 232 TEST_F(RequestPickerTest, ChooseEarlierRequest) { | |
| 233 // We need a custom policy object prefering recency to retry count. | |
| 234 policy_.reset(new OfflinerPolicy( | |
| 235 kPreferUntried, kPreferEarlier, !kPreferRetryCount, kMaxStartedTries, | |
| 236 kMaxCompletedTries, kBackgroundProcessingTimeBudgetSeconds)); | |
| 237 picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(), | |
| 238 &event_logger_)); | |
| 239 | |
| 240 base::Time creation_time1 = | |
| 241 base::Time::Now() - base::TimeDelta::FromSeconds(10); | |
| 242 base::Time creation_time2 = base::Time::Now(); | |
| 243 SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1, | |
| 244 kUserRequested); | |
| 245 SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2, | |
| 246 kUserRequested); | |
| 247 request2.set_completed_attempt_count(kAttemptCount); | |
| 248 | |
| 249 QueueRequestsAndChooseOne(request1, request2); | |
| 250 | |
| 251 EXPECT_EQ(kRequestId1, last_picked_->request_id()); | |
| 252 EXPECT_FALSE(request_queue_not_picked_called_); | |
| 253 } | |
| 254 | |
| 255 TEST_F(RequestPickerTest, ChooseSameTimeRequestWithHigherRetryCount) { | |
| 256 // We need a custom policy object preferring recency to retry count. | |
| 257 policy_.reset(new OfflinerPolicy( | |
| 258 kPreferUntried, kPreferEarlier, !kPreferRetryCount, kMaxStartedTries, | |
| 259 kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds)); | |
| 260 picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(), | |
| 261 &event_logger_)); | |
| 262 | |
| 263 base::Time creation_time = base::Time::Now(); | |
| 264 SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time, | |
| 265 kUserRequested); | |
| 266 SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time, | |
| 267 kUserRequested); | |
| 268 request2.set_completed_attempt_count(kAttemptCount); | |
| 269 | |
| 270 QueueRequestsAndChooseOne(request1, request2); | |
| 271 | |
| 272 EXPECT_EQ(kRequestId2, last_picked_->request_id()); | |
| 273 EXPECT_FALSE(request_queue_not_picked_called_); | |
| 274 } | |
| 275 | |
| 276 TEST_F(RequestPickerTest, ChooseRequestWithLowerRetryCount) { | |
| 277 // We need a custom policy object preferring lower retry count. | |
| 278 policy_.reset(new OfflinerPolicy( | |
| 279 !kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries, | |
| 280 kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds)); | |
| 281 picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(), | |
| 282 &event_logger_)); | |
| 283 | |
| 284 base::Time creation_time = base::Time::Now(); | |
| 285 SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time, | |
| 286 kUserRequested); | |
| 287 SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time, | |
| 288 kUserRequested); | |
| 289 request2.set_completed_attempt_count(kAttemptCount); | |
| 290 | |
| 291 QueueRequestsAndChooseOne(request1, request2); | |
| 292 | |
| 293 EXPECT_EQ(kRequestId1, last_picked_->request_id()); | |
| 294 EXPECT_FALSE(request_queue_not_picked_called_); | |
| 295 } | |
| 296 | |
| 297 TEST_F(RequestPickerTest, ChooseLaterRequest) { | |
| 298 // We need a custom policy preferring recency over retry, and later requests. | |
| 299 policy_.reset(new OfflinerPolicy( | |
| 300 kPreferUntried, !kPreferEarlier, !kPreferRetryCount, kMaxStartedTries, | |
| 301 kMaxCompletedTries, kBackgroundProcessingTimeBudgetSeconds)); | |
| 302 picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(), | |
| 303 &event_logger_)); | |
| 304 | |
| 305 base::Time creation_time1 = | |
| 306 base::Time::Now() - base::TimeDelta::FromSeconds(10); | |
| 307 base::Time creation_time2 = base::Time::Now(); | |
| 308 SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1, | |
| 309 kUserRequested); | |
| 310 SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2, | |
| 311 kUserRequested); | |
| 312 | |
| 313 QueueRequestsAndChooseOne(request1, request2); | |
| 314 | |
| 315 EXPECT_EQ(kRequestId2, last_picked_->request_id()); | |
| 316 EXPECT_FALSE(request_queue_not_picked_called_); | |
| 317 } | |
| 318 | |
| 319 TEST_F(RequestPickerTest, ChooseNonExpiredRequest) { | |
| 320 base::Time creation_time = base::Time::Now(); | |
| 321 base::Time expired_time = | |
| 322 creation_time - base::TimeDelta::FromSeconds( | |
| 323 policy_->GetRequestExpirationTimeInSeconds() + 60); | |
| 324 SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time, | |
| 325 kUserRequested); | |
| 326 SavePageRequest request2(kRequestId2, kUrl2, kClientId2, expired_time, | |
| 327 kUserRequested); | |
| 328 | |
| 329 QueueRequestsAndChooseOne(request1, request2); | |
| 330 | |
| 331 PumpLoop(); | |
| 332 | |
| 333 EXPECT_EQ(kRequestId1, last_picked_->request_id()); | |
| 334 EXPECT_FALSE(request_queue_not_picked_called_); | |
| 335 EXPECT_EQ(kRequestId2, GetNotifier()->last_expired_request().request_id()); | |
| 336 EXPECT_EQ(RequestNotifier::BackgroundSavePageResult::EXPIRED, | |
| 337 GetNotifier()->last_request_expiration_status()); | |
| 338 EXPECT_EQ(1, GetNotifier()->total_expired_requests()); | |
| 339 } | |
| 340 | |
| 341 TEST_F(RequestPickerTest, ChooseRequestThatHasNotExceededStartLimit) { | |
| 342 base::Time creation_time1 = | |
| 343 base::Time::Now() - base::TimeDelta::FromSeconds(1); | |
| 344 base::Time creation_time2 = base::Time::Now(); | |
| 345 SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1, | |
| 346 kUserRequested); | |
| 347 SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2, | |
| 348 kUserRequested); | |
| 349 | |
| 350 // With default policy settings, we should choose the earlier request. | |
| 351 // However, we will make the earlier reqeust exceed the limit. | |
| 352 request1.set_started_attempt_count(policy_->GetMaxStartedTries()); | |
| 353 | |
| 354 QueueRequestsAndChooseOne(request1, request2); | |
| 355 | |
| 356 EXPECT_EQ(kRequestId2, last_picked_->request_id()); | |
| 357 EXPECT_FALSE(request_queue_not_picked_called_); | |
| 358 } | |
| 359 | |
| 360 TEST_F(RequestPickerTest, ChooseRequestThatHasNotExceededCompletionLimit) { | |
| 361 base::Time creation_time1 = | |
| 362 base::Time::Now() - base::TimeDelta::FromSeconds(1); | |
| 363 base::Time creation_time2 = base::Time::Now(); | |
| 364 SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1, | |
| 365 kUserRequested); | |
| 366 SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2, | |
| 367 kUserRequested); | |
| 368 | |
| 369 // With default policy settings, we should choose the earlier request. | |
| 370 // However, we will make the earlier reqeust exceed the limit. | |
| 371 request1.set_completed_attempt_count(policy_->GetMaxCompletedTries()); | |
| 372 | |
| 373 QueueRequestsAndChooseOne(request1, request2); | |
| 374 | |
| 375 EXPECT_EQ(kRequestId2, last_picked_->request_id()); | |
| 376 EXPECT_FALSE(request_queue_not_picked_called_); | |
| 377 } | |
| 378 | |
| 379 | |
| 380 TEST_F(RequestPickerTest, ChooseRequestThatIsNotDisabled) { | |
| 381 policy_.reset(new OfflinerPolicy( | |
| 382 kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries, | |
| 383 kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds)); | |
| 384 picker_.reset(new RequestPicker(queue_.get(), policy_.get(), notifier_.get(), | |
| 385 &event_logger_)); | |
| 386 | |
| 387 base::Time creation_time = base::Time::Now(); | |
| 388 SavePageRequest request1( | |
| 389 kRequestId1, kUrl1, kClientId1, creation_time, kUserRequested); | |
| 390 SavePageRequest request2( | |
| 391 kRequestId2, kUrl2, kClientId2, creation_time, kUserRequested); | |
| 392 request2.set_completed_attempt_count(kAttemptCount); | |
| 393 | |
| 394 // put request 2 on disabled list, ensure request1 picked instead, | |
| 395 // even though policy would prefer 2. | |
| 396 std::set<int64_t> disabled_requests {kRequestId2}; | |
| 397 DeviceConditions conditions; | |
| 398 | |
| 399 // Add test requests on the Queue. | |
| 400 queue_->AddRequest(request1, base::Bind(&RequestPickerTest::AddRequestDone, | |
| 401 base::Unretained(this))); | |
| 402 queue_->AddRequest(request2, base::Bind(&RequestPickerTest::AddRequestDone, | |
| 403 base::Unretained(this))); | |
| 404 | |
| 405 // Pump the loop to give the async queue the opportunity to do the adds. | |
| 406 PumpLoop(); | |
| 407 | |
| 408 // Call the method under test. | |
| 409 picker_->ChooseNextRequest( | |
| 410 base::Bind(&RequestPickerTest::RequestPicked, base::Unretained(this)), | |
| 411 base::Bind(&RequestPickerTest::RequestNotPicked, base::Unretained(this)), | |
| 412 &conditions, disabled_requests); | |
| 413 | |
| 414 // Pump the loop again to give the async queue the opportunity to return | |
| 415 // results from the Get operation, and for the picker to call the "picked" | |
| 416 // callback. | |
| 417 PumpLoop(); | |
| 418 | |
| 419 EXPECT_EQ(kRequestId1, last_picked_->request_id()); | |
| 420 EXPECT_FALSE(request_queue_not_picked_called_); | |
| 421 } | |
| 422 } // namespace offline_pages | |
| OLD | NEW |