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_queue_store.h" | |
6 | |
7 #include <memory> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/files/file_path.h" | |
11 #include "base/files/scoped_temp_dir.h" | |
12 #include "base/test/test_simple_task_runner.h" | |
13 #include "base/threading/thread_task_runner_handle.h" | |
14 #include "components/offline_pages/background/request_queue.h" | |
15 #include "components/offline_pages/background/request_queue_in_memory_store.h" | |
16 #include "components/offline_pages/background/request_queue_store_sql.h" | |
17 #include "components/offline_pages/background/save_page_request.h" | |
18 #include "testing/gtest/include/gtest/gtest.h" | |
19 | |
20 namespace offline_pages { | |
21 | |
22 using UpdateStatus = RequestQueueStore::UpdateStatus; | |
23 | |
24 namespace { | |
25 const int64_t kRequestId = 42; | |
26 const int64_t kRequestId2 = 44; | |
27 const int64_t kRequestId3 = 47; | |
28 const GURL kUrl("http://example.com"); | |
29 const GURL kUrl2("http://another-example.com"); | |
30 const ClientId kClientId("bookmark", "1234"); | |
31 const ClientId kClientId2("async", "5678"); | |
32 const bool kUserRequested = true; | |
33 | |
34 enum class LastResult { | |
35 kNone, | |
36 kFalse, | |
37 kTrue, | |
38 }; | |
39 | |
40 } // namespace | |
41 | |
42 // Class that serves as a base for testing different implementations of the | |
43 // |RequestQueueStore|. Specific implementations extend the templatized version | |
44 // of this class and provide appropriate store factory. | |
45 class RequestQueueStoreTestBase : public testing::Test { | |
46 public: | |
47 RequestQueueStoreTestBase(); | |
48 | |
49 // Test overrides. | |
50 void TearDown() override; | |
51 | |
52 void PumpLoop(); | |
53 void ClearResults(); | |
54 | |
55 // Callback used for get requests. | |
56 void GetRequestsDone(bool result, | |
57 std::vector<std::unique_ptr<SavePageRequest>> requests); | |
58 // Callback used for add/update request. | |
59 void AddOrUpdateDone(UpdateStatus result); | |
60 void AddRequestDone(ItemActionStatus status); | |
61 void UpdateRequestDone(std::unique_ptr<UpdateRequestsResult> result); | |
62 // Callback used for reset. | |
63 void ResetDone(bool result); | |
64 | |
65 LastResult last_result() const { return last_result_; } | |
66 UpdateStatus last_update_status() const { return last_update_status_; } | |
67 const std::vector<std::unique_ptr<SavePageRequest>>& last_requests() const { | |
68 return last_requests_; | |
69 } | |
70 ItemActionStatus last_add_status() const { return last_add_status_; } | |
71 | |
72 UpdateRequestsResult* last_update_result() const { | |
73 return last_update_result_.get(); | |
74 } | |
75 | |
76 protected: | |
77 base::ScopedTempDir temp_directory_; | |
78 | |
79 private: | |
80 LastResult last_result_; | |
81 UpdateStatus last_update_status_; | |
82 ItemActionStatus last_add_status_; | |
83 std::unique_ptr<UpdateRequestsResult> last_update_result_; | |
84 std::vector<std::unique_ptr<SavePageRequest>> last_requests_; | |
85 | |
86 scoped_refptr<base::TestSimpleTaskRunner> task_runner_; | |
87 base::ThreadTaskRunnerHandle task_runner_handle_; | |
88 }; | |
89 | |
90 RequestQueueStoreTestBase::RequestQueueStoreTestBase() | |
91 : last_result_(LastResult::kNone), | |
92 last_update_status_(UpdateStatus::FAILED), | |
93 last_add_status_(ItemActionStatus::NOT_FOUND), | |
94 task_runner_(new base::TestSimpleTaskRunner), | |
95 task_runner_handle_(task_runner_) { | |
96 EXPECT_TRUE(temp_directory_.CreateUniqueTempDir()); | |
97 } | |
98 | |
99 void RequestQueueStoreTestBase::TearDown() { | |
100 // Wait for all the pieces of the store to delete itself properly. | |
101 PumpLoop(); | |
102 } | |
103 | |
104 void RequestQueueStoreTestBase::PumpLoop() { | |
105 task_runner_->RunUntilIdle(); | |
106 } | |
107 | |
108 void RequestQueueStoreTestBase::ClearResults() { | |
109 last_result_ = LastResult::kNone; | |
110 last_update_status_ = UpdateStatus::FAILED; | |
111 last_add_status_ = ItemActionStatus::NOT_FOUND; | |
112 last_requests_.clear(); | |
113 last_update_result_.reset(nullptr); | |
114 } | |
115 | |
116 void RequestQueueStoreTestBase::GetRequestsDone( | |
117 bool result, | |
118 std::vector<std::unique_ptr<SavePageRequest>> requests) { | |
119 last_result_ = result ? LastResult::kTrue : LastResult::kFalse; | |
120 last_requests_ = std::move(requests); | |
121 } | |
122 | |
123 void RequestQueueStoreTestBase::AddOrUpdateDone(UpdateStatus status) { | |
124 last_update_status_ = status; | |
125 } | |
126 | |
127 void RequestQueueStoreTestBase::AddRequestDone(ItemActionStatus status) { | |
128 last_add_status_ = status; | |
129 } | |
130 | |
131 void RequestQueueStoreTestBase::UpdateRequestDone( | |
132 std::unique_ptr<UpdateRequestsResult> result) { | |
133 last_update_result_ = std::move(result); | |
134 } | |
135 | |
136 void RequestQueueStoreTestBase::ResetDone(bool result) { | |
137 last_result_ = result ? LastResult::kTrue : LastResult::kFalse; | |
138 } | |
139 | |
140 // Defines interface for the store factory. | |
141 class RequestQueueStoreFactory { | |
142 public: | |
143 virtual RequestQueueStore* BuildStore(const base::FilePath& path) = 0; | |
144 }; | |
145 | |
146 // Implements a store factory for in memory store. | |
147 class RequestQueueInMemoryStoreFactory : public RequestQueueStoreFactory { | |
148 public: | |
149 RequestQueueStore* BuildStore(const base::FilePath& path) override { | |
150 RequestQueueStore* store = new RequestQueueInMemoryStore(); | |
151 return store; | |
152 } | |
153 }; | |
154 | |
155 // Implements a store factory for SQLite based implementation of the store. | |
156 class RequestQueueStoreSQLFactory : public RequestQueueStoreFactory { | |
157 public: | |
158 RequestQueueStore* BuildStore(const base::FilePath& path) override { | |
159 RequestQueueStore* store = | |
160 new RequestQueueStoreSQL(base::ThreadTaskRunnerHandle::Get(), path); | |
161 return store; | |
162 } | |
163 }; | |
164 | |
165 // Defines a store test fixture templatized by the store factory. | |
166 template <typename T> | |
167 class RequestQueueStoreTest : public RequestQueueStoreTestBase { | |
168 public: | |
169 std::unique_ptr<RequestQueueStore> BuildStore(); | |
170 | |
171 protected: | |
172 T factory_; | |
173 }; | |
174 | |
175 template <typename T> | |
176 std::unique_ptr<RequestQueueStore> RequestQueueStoreTest<T>::BuildStore() { | |
177 std::unique_ptr<RequestQueueStore> store( | |
178 factory_.BuildStore(temp_directory_.GetPath())); | |
179 return store; | |
180 } | |
181 | |
182 // |StoreTypes| lists all factories, based on which the tests will be created. | |
183 typedef testing::Types<RequestQueueInMemoryStoreFactory, | |
184 RequestQueueStoreSQLFactory> | |
185 StoreTypes; | |
186 | |
187 // This portion causes test fixtures to be defined. | |
188 // Notice that in the store we are using "this->" to refer to the methods | |
189 // defined on the |RequestQuieueStoreBaseTest| class. That's by design. | |
190 TYPED_TEST_CASE(RequestQueueStoreTest, StoreTypes); | |
191 | |
192 TYPED_TEST(RequestQueueStoreTest, GetRequestsEmpty) { | |
193 std::unique_ptr<RequestQueueStore> store(this->BuildStore()); | |
194 store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone, | |
195 base::Unretained(this))); | |
196 ASSERT_EQ(LastResult::kNone, this->last_result()); | |
197 this->PumpLoop(); | |
198 ASSERT_EQ(LastResult::kTrue, this->last_result()); | |
199 ASSERT_TRUE(this->last_requests().empty()); | |
200 } | |
201 | |
202 TYPED_TEST(RequestQueueStoreTest, GetRequestsByIds) { | |
203 std::unique_ptr<RequestQueueStore> store(this->BuildStore()); | |
204 base::Time creation_time = base::Time::Now(); | |
205 SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time, | |
206 kUserRequested); | |
207 store->AddRequest(request1, | |
208 base::Bind(&RequestQueueStoreTestBase::AddRequestDone, | |
209 base::Unretained(this))); | |
210 SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time, | |
211 kUserRequested); | |
212 store->AddRequest(request2, | |
213 base::Bind(&RequestQueueStoreTestBase::AddRequestDone, | |
214 base::Unretained(this))); | |
215 this->PumpLoop(); | |
216 this->ClearResults(); | |
217 | |
218 std::vector<int64_t> request_ids{kRequestId, kRequestId2}; | |
219 store->GetRequestsByIds( | |
220 request_ids, base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone, | |
221 base::Unretained(this))); | |
222 | |
223 ASSERT_FALSE(this->last_update_result()); | |
224 this->PumpLoop(); | |
225 ASSERT_TRUE(this->last_update_result()); | |
226 EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size()); | |
227 EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first); | |
228 EXPECT_EQ(ItemActionStatus::SUCCESS, | |
229 this->last_update_result()->item_statuses[0].second); | |
230 EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first); | |
231 EXPECT_EQ(ItemActionStatus::SUCCESS, | |
232 this->last_update_result()->item_statuses[1].second); | |
233 EXPECT_EQ(2UL, this->last_update_result()->updated_items.size()); | |
234 EXPECT_EQ(request1, this->last_update_result()->updated_items.at(0)); | |
235 EXPECT_EQ(request2, this->last_update_result()->updated_items.at(1)); | |
236 this->ClearResults(); | |
237 | |
238 request_ids.clear(); | |
239 request_ids.push_back(kRequestId); | |
240 request_ids.push_back(kRequestId3); | |
241 request_ids.push_back(kRequestId); | |
242 | |
243 store->GetRequestsByIds( | |
244 request_ids, base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone, | |
245 base::Unretained(this))); | |
246 | |
247 ASSERT_FALSE(this->last_update_result()); | |
248 this->PumpLoop(); | |
249 ASSERT_TRUE(this->last_update_result()); | |
250 EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size()); | |
251 EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first); | |
252 EXPECT_EQ(ItemActionStatus::SUCCESS, | |
253 this->last_update_result()->item_statuses[0].second); | |
254 EXPECT_EQ(kRequestId3, this->last_update_result()->item_statuses[1].first); | |
255 EXPECT_EQ(ItemActionStatus::NOT_FOUND, | |
256 this->last_update_result()->item_statuses[1].second); | |
257 EXPECT_EQ(1UL, this->last_update_result()->updated_items.size()); | |
258 EXPECT_EQ(request1, this->last_update_result()->updated_items.at(0)); | |
259 } | |
260 | |
261 TYPED_TEST(RequestQueueStoreTest, AddRequest) { | |
262 std::unique_ptr<RequestQueueStore> store(this->BuildStore()); | |
263 base::Time creation_time = base::Time::Now(); | |
264 SavePageRequest request( | |
265 kRequestId, kUrl, kClientId, creation_time, kUserRequested); | |
266 | |
267 store->AddRequest(request, | |
268 base::Bind(&RequestQueueStoreTestBase::AddRequestDone, | |
269 base::Unretained(this))); | |
270 ASSERT_EQ(ItemActionStatus::NOT_FOUND, this->last_add_status()); | |
271 this->PumpLoop(); | |
272 ASSERT_EQ(ItemActionStatus::SUCCESS, this->last_add_status()); | |
273 | |
274 // Verifying get reqeust results after a request was added. | |
275 this->ClearResults(); | |
276 store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone, | |
277 base::Unretained(this))); | |
278 ASSERT_EQ(LastResult::kNone, this->last_result()); | |
279 this->PumpLoop(); | |
280 ASSERT_EQ(LastResult::kTrue, this->last_result()); | |
281 ASSERT_EQ(1ul, this->last_requests().size()); | |
282 ASSERT_EQ(request, *(this->last_requests()[0].get())); | |
283 | |
284 // Verify it is not possible to add the same request twice. | |
285 this->ClearResults(); | |
286 store->AddRequest(request, | |
287 base::Bind(&RequestQueueStoreTestBase::AddRequestDone, | |
288 base::Unretained(this))); | |
289 ASSERT_EQ(ItemActionStatus::NOT_FOUND, this->last_add_status()); | |
290 this->PumpLoop(); | |
291 ASSERT_EQ(ItemActionStatus::ALREADY_EXISTS, this->last_add_status()); | |
292 | |
293 // Check that there is still only one item in the store. | |
294 this->ClearResults(); | |
295 store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone, | |
296 base::Unretained(this))); | |
297 ASSERT_EQ(LastResult::kNone, this->last_result()); | |
298 this->PumpLoop(); | |
299 ASSERT_EQ(LastResult::kTrue, this->last_result()); | |
300 ASSERT_EQ(1ul, this->last_requests().size()); | |
301 } | |
302 | |
303 TYPED_TEST(RequestQueueStoreTest, UpdateRequest) { | |
304 std::unique_ptr<RequestQueueStore> store(this->BuildStore()); | |
305 base::Time creation_time = base::Time::Now(); | |
306 SavePageRequest original_request( | |
307 kRequestId, kUrl, kClientId, creation_time, kUserRequested); | |
308 store->AddRequest(original_request, | |
309 base::Bind(&RequestQueueStoreTestBase::AddRequestDone, | |
310 base::Unretained(this))); | |
311 this->PumpLoop(); | |
312 this->ClearResults(); | |
313 | |
314 base::Time new_creation_time = | |
315 creation_time + base::TimeDelta::FromMinutes(1); | |
316 base::Time activation_time = creation_time + base::TimeDelta::FromHours(6); | |
317 // Try updating an existing request. | |
318 SavePageRequest updated_request(kRequestId, kUrl, kClientId, | |
319 new_creation_time, activation_time, | |
320 kUserRequested); | |
321 // Try to update a non-existing request. | |
322 SavePageRequest updated_request2(kRequestId2, kUrl, kClientId, | |
323 new_creation_time, activation_time, | |
324 kUserRequested); | |
325 std::vector<SavePageRequest> requests_to_update{updated_request, | |
326 updated_request2}; | |
327 store->UpdateRequests( | |
328 requests_to_update, | |
329 base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone, | |
330 base::Unretained(this))); | |
331 ASSERT_FALSE(this->last_update_result()); | |
332 this->PumpLoop(); | |
333 ASSERT_TRUE(this->last_update_result()); | |
334 EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size()); | |
335 EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first); | |
336 EXPECT_EQ(ItemActionStatus::SUCCESS, | |
337 this->last_update_result()->item_statuses[0].second); | |
338 EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first); | |
339 EXPECT_EQ(ItemActionStatus::NOT_FOUND, | |
340 this->last_update_result()->item_statuses[1].second); | |
341 EXPECT_EQ(1UL, this->last_update_result()->updated_items.size()); | |
342 EXPECT_EQ(updated_request, | |
343 *(this->last_update_result()->updated_items.begin())); | |
344 | |
345 // Verifying get reqeust results after a request was updated. | |
346 this->ClearResults(); | |
347 store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone, | |
348 base::Unretained(this))); | |
349 ASSERT_EQ(LastResult::kNone, this->last_result()); | |
350 this->PumpLoop(); | |
351 ASSERT_EQ(LastResult::kTrue, this->last_result()); | |
352 ASSERT_EQ(1ul, this->last_requests().size()); | |
353 ASSERT_EQ(updated_request, *(this->last_requests()[0].get())); | |
354 } | |
355 | |
356 TYPED_TEST(RequestQueueStoreTest, RemoveRequests) { | |
357 std::unique_ptr<RequestQueueStore> store(this->BuildStore()); | |
358 base::Time creation_time = base::Time::Now(); | |
359 SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time, | |
360 kUserRequested); | |
361 store->AddRequest(request1, | |
362 base::Bind(&RequestQueueStoreTestBase::AddRequestDone, | |
363 base::Unretained(this))); | |
364 SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time, | |
365 kUserRequested); | |
366 store->AddRequest(request2, | |
367 base::Bind(&RequestQueueStoreTestBase::AddRequestDone, | |
368 base::Unretained(this))); | |
369 this->PumpLoop(); | |
370 this->ClearResults(); | |
371 | |
372 std::vector<int64_t> request_ids{kRequestId, kRequestId2}; | |
373 store->RemoveRequests( | |
374 request_ids, base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone, | |
375 base::Unretained(this))); | |
376 | |
377 ASSERT_FALSE(this->last_update_result()); | |
378 this->PumpLoop(); | |
379 ASSERT_TRUE(this->last_update_result()); | |
380 EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size()); | |
381 EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first); | |
382 EXPECT_EQ(ItemActionStatus::SUCCESS, | |
383 this->last_update_result()->item_statuses[0].second); | |
384 EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first); | |
385 EXPECT_EQ(ItemActionStatus::SUCCESS, | |
386 this->last_update_result()->item_statuses[1].second); | |
387 EXPECT_EQ(2UL, this->last_update_result()->updated_items.size()); | |
388 EXPECT_EQ(request1, this->last_update_result()->updated_items.at(0)); | |
389 EXPECT_EQ(request2, this->last_update_result()->updated_items.at(1)); | |
390 this->ClearResults(); | |
391 | |
392 store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone, | |
393 base::Unretained(this))); | |
394 this->PumpLoop(); | |
395 ASSERT_EQ(LastResult::kTrue, this->last_result()); | |
396 ASSERT_TRUE(this->last_requests().empty()); | |
397 this->ClearResults(); | |
398 | |
399 // Try to remove a request that is not in the queue. | |
400 store->RemoveRequests( | |
401 request_ids, base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone, | |
402 base::Unretained(this))); | |
403 ASSERT_FALSE(this->last_update_result()); | |
404 this->PumpLoop(); | |
405 ASSERT_TRUE(this->last_update_result()); | |
406 // When requests are missing, we expect the results to say so, but since they | |
407 // are missing, no requests should have been returned. | |
408 EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size()); | |
409 EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first); | |
410 EXPECT_EQ(ItemActionStatus::NOT_FOUND, | |
411 this->last_update_result()->item_statuses[0].second); | |
412 EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first); | |
413 EXPECT_EQ(ItemActionStatus::NOT_FOUND, | |
414 this->last_update_result()->item_statuses[1].second); | |
415 EXPECT_EQ(0UL, this->last_update_result()->updated_items.size()); | |
416 } | |
417 | |
418 TYPED_TEST(RequestQueueStoreTest, ResetStore) { | |
419 std::unique_ptr<RequestQueueStore> store(this->BuildStore()); | |
420 base::Time creation_time = base::Time::Now(); | |
421 SavePageRequest original_request( | |
422 kRequestId, kUrl, kClientId, creation_time, kUserRequested); | |
423 store->AddRequest(original_request, | |
424 base::Bind(&RequestQueueStoreTestBase::AddRequestDone, | |
425 base::Unretained(this))); | |
426 this->PumpLoop(); | |
427 this->ClearResults(); | |
428 | |
429 store->Reset(base::Bind(&RequestQueueStoreTestBase::ResetDone, | |
430 base::Unretained(this))); | |
431 ASSERT_EQ(LastResult::kNone, this->last_result()); | |
432 this->PumpLoop(); | |
433 ASSERT_EQ(LastResult::kTrue, this->last_result()); | |
434 this->ClearResults(); | |
435 | |
436 store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone, | |
437 base::Unretained(this))); | |
438 this->PumpLoop(); | |
439 ASSERT_EQ(LastResult::kTrue, this->last_result()); | |
440 ASSERT_TRUE(this->last_requests().empty()); | |
441 } | |
442 | |
443 class RequestQueueStoreSQLTest | |
444 : public RequestQueueStoreTest<RequestQueueStoreSQLFactory> {}; | |
445 | |
446 // Makes sure that persistent DB is actually persisting requests across store | |
447 // restarts. | |
448 TEST_F(RequestQueueStoreSQLTest, SaveCloseReopenRead) { | |
449 std::unique_ptr<RequestQueueStore> store(BuildStore()); | |
450 base::Time creation_time = base::Time::Now(); | |
451 SavePageRequest original_request( | |
452 kRequestId, kUrl, kClientId, creation_time, kUserRequested); | |
453 store->AddRequest(original_request, | |
454 base::Bind(&RequestQueueStoreTestBase::AddRequestDone, | |
455 base::Unretained(this))); | |
456 PumpLoop(); | |
457 ClearResults(); | |
458 | |
459 // Resets the store, using the same temp directory. The contents should be | |
460 // intact. First reset is done separately to release DB lock. | |
461 store.reset(); | |
462 store = BuildStore(); | |
463 store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone, | |
464 base::Unretained(this))); | |
465 ASSERT_EQ(LastResult::kNone, this->last_result()); | |
466 this->PumpLoop(); | |
467 ASSERT_EQ(LastResult::kTrue, this->last_result()); | |
468 ASSERT_EQ(1ul, this->last_requests().size()); | |
469 ASSERT_TRUE(original_request == *(this->last_requests().at(0).get())); | |
470 } | |
471 | |
472 } // offline_pages | |
OLD | NEW |