| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/offline_pages/core/background/request_queue_store.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/files/file_path.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/test/test_mock_time_task_runner.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/clock.h" |
| #include "components/offline_pages/core/background/request_queue.h" |
| #include "components/offline_pages/core/background/save_page_request.h" |
| #include "components/offline_pages/core/offline_clock.h" |
| #include "sql/database.h" |
| #include "sql/statement.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace offline_pages { |
| |
| #define REQUEST_QUEUE_TABLE_NAME "request_queue_v1" |
| |
| using UpdateStatus = RequestQueueStore::UpdateStatus; |
| |
| namespace { |
| const int64_t kRequestId = 42; |
| const int64_t kRequestId2 = 44; |
| const int64_t kRequestId3 = 47; |
| const GURL kUrl("http://example.com"); |
| const GURL kUrl2("http://another-example.com"); |
| const ClientId kClientId("bookmark", "1234"); |
| const ClientId kClientId2("async", "5678"); |
| const bool kUserRequested = true; |
| const char kRequestOrigin[] = "abc.xyz"; |
| |
| enum class LastResult { |
| RESULT_NONE, |
| RESULT_FALSE, |
| RESULT_TRUE, |
| }; |
| |
| SavePageRequest GetTestRequest() { |
| SavePageRequest request(kRequestId, kUrl, kClientId, |
| base::Time::FromDeltaSinceWindowsEpoch( |
| base::TimeDelta::FromSeconds(1000)), |
| kUserRequested); |
| // Set fields to non-default values. |
| request.set_fail_state(offline_items_collection::FailState::FILE_NO_SPACE); |
| request.set_started_attempt_count(2); |
| request.set_completed_attempt_count(3); |
| request.set_last_attempt_time(base::Time::FromDeltaSinceWindowsEpoch( |
| base::TimeDelta::FromSeconds(400))); |
| request.set_request_origin("http://www.origin.com"); |
| // Note: pending_state is not stored. |
| request.set_original_url(kUrl2); |
| return request; |
| } |
| |
| void BuildTestStoreWithSchemaFromM57(const base::FilePath& file) { |
| sql::Database connection; |
| ASSERT_TRUE( |
| connection.Open(file.Append(FILE_PATH_LITERAL("RequestQueue.db")))); |
| ASSERT_TRUE(connection.is_open()); |
| ASSERT_TRUE(connection.BeginTransaction()); |
| ASSERT_TRUE( |
| connection.Execute("CREATE TABLE " REQUEST_QUEUE_TABLE_NAME |
| " (request_id INTEGER PRIMARY KEY NOT NULL," |
| " creation_time INTEGER NOT NULL," |
| " activation_time INTEGER NOT NULL DEFAULT 0," |
| " last_attempt_time INTEGER NOT NULL DEFAULT 0," |
| " started_attempt_count INTEGER NOT NULL," |
| " completed_attempt_count INTEGER NOT NULL," |
| " state INTEGER NOT NULL DEFAULT 0," |
| " url VARCHAR NOT NULL," |
| " client_namespace VARCHAR NOT NULL," |
| " client_id VARCHAR NOT NULL" |
| ")")); |
| |
| ASSERT_TRUE(connection.CommitTransaction()); |
| sql::Statement statement(connection.GetUniqueStatement( |
| "INSERT OR IGNORE INTO " REQUEST_QUEUE_TABLE_NAME |
| " (request_id, creation_time, activation_time," |
| " last_attempt_time, started_attempt_count, completed_attempt_count," |
| " state, url, client_namespace, client_id)" |
| " VALUES " |
| " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); |
| |
| statement.BindInt64(0, kRequestId); |
| statement.BindInt64(1, 0); |
| statement.BindInt64(2, 0); |
| statement.BindInt64(3, 0); |
| statement.BindInt64(4, 0); |
| statement.BindInt64(5, 0); |
| statement.BindInt64(6, 0); |
| statement.BindString(7, kUrl.spec()); |
| statement.BindString(8, kClientId.name_space); |
| statement.BindString(9, kClientId.id); |
| ASSERT_TRUE(statement.Run()); |
| ASSERT_TRUE(connection.DoesTableExist(REQUEST_QUEUE_TABLE_NAME)); |
| ASSERT_FALSE( |
| connection.DoesColumnExist(REQUEST_QUEUE_TABLE_NAME, "original_url")); |
| } |
| |
| void BuildTestStoreWithSchemaFromM58(const base::FilePath& file) { |
| sql::Database connection; |
| ASSERT_TRUE( |
| connection.Open(file.Append(FILE_PATH_LITERAL("RequestQueue.db")))); |
| ASSERT_TRUE(connection.is_open()); |
| ASSERT_TRUE(connection.BeginTransaction()); |
| ASSERT_TRUE( |
| connection.Execute("CREATE TABLE " REQUEST_QUEUE_TABLE_NAME |
| " (request_id INTEGER PRIMARY KEY NOT NULL," |
| " creation_time INTEGER NOT NULL," |
| " activation_time INTEGER NOT NULL DEFAULT 0," |
| " last_attempt_time INTEGER NOT NULL DEFAULT 0," |
| " started_attempt_count INTEGER NOT NULL," |
| " completed_attempt_count INTEGER NOT NULL," |
| " state INTEGER NOT NULL DEFAULT 0," |
| " url VARCHAR NOT NULL," |
| " client_namespace VARCHAR NOT NULL," |
| " client_id VARCHAR NOT NULL," |
| " original_url VARCHAR NOT NULL" |
| ")")); |
| |
| ASSERT_TRUE(connection.CommitTransaction()); |
| sql::Statement statement(connection.GetUniqueStatement( |
| "INSERT OR IGNORE INTO " REQUEST_QUEUE_TABLE_NAME |
| " (request_id, creation_time, activation_time," |
| " last_attempt_time, started_attempt_count, completed_attempt_count," |
| " state, url, client_namespace, client_id, original_url)" |
| " VALUES " |
| " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); |
| |
| statement.BindInt64(0, kRequestId); |
| statement.BindInt64(1, 0); |
| statement.BindInt64(2, 0); |
| statement.BindInt64(3, 0); |
| statement.BindInt64(4, 0); |
| statement.BindInt64(5, 0); |
| statement.BindInt64(6, 0); |
| statement.BindString(7, kUrl.spec()); |
| statement.BindString(8, kClientId.name_space); |
| statement.BindString(9, kClientId.id); |
| statement.BindString(10, kUrl2.spec()); |
| ASSERT_TRUE(statement.Run()); |
| ASSERT_TRUE(connection.DoesTableExist(REQUEST_QUEUE_TABLE_NAME)); |
| ASSERT_FALSE( |
| connection.DoesColumnExist(REQUEST_QUEUE_TABLE_NAME, "request_origin")); |
| } |
| |
| void BuildTestStoreWithSchemaFromM61(const base::FilePath& file) { |
| sql::Database connection; |
| ASSERT_TRUE( |
| connection.Open(file.Append(FILE_PATH_LITERAL("RequestQueue.db")))); |
| ASSERT_TRUE(connection.is_open()); |
| ASSERT_TRUE(connection.BeginTransaction()); |
| ASSERT_TRUE( |
| connection.Execute("CREATE TABLE " REQUEST_QUEUE_TABLE_NAME |
| " (request_id INTEGER PRIMARY KEY NOT NULL," |
| " creation_time INTEGER NOT NULL," |
| " activation_time INTEGER NOT NULL DEFAULT 0," |
| " last_attempt_time INTEGER NOT NULL DEFAULT 0," |
| " started_attempt_count INTEGER NOT NULL," |
| " completed_attempt_count INTEGER NOT NULL," |
| " state INTEGER NOT NULL DEFAULT 0," |
| " url VARCHAR NOT NULL," |
| " client_namespace VARCHAR NOT NULL," |
| " client_id VARCHAR NOT NULL," |
| " original_url VARCHAR NOT NULL DEFAULT ''," |
| " request_origin VARCHAR NOT NULL DEFAULT ''" |
| ")")); |
| |
| ASSERT_TRUE(connection.CommitTransaction()); |
| sql::Statement statement(connection.GetUniqueStatement( |
| "INSERT OR IGNORE INTO " REQUEST_QUEUE_TABLE_NAME |
| " (request_id, creation_time, activation_time," |
| " last_attempt_time, started_attempt_count, completed_attempt_count," |
| " state, url, client_namespace, client_id, original_url, request_origin)" |
| " VALUES " |
| " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); |
| |
| statement.BindInt64(0, kRequestId); |
| statement.BindInt64(1, 0); |
| statement.BindInt64(2, 0); |
| statement.BindInt64(3, 0); |
| statement.BindInt64(4, 0); |
| statement.BindInt64(5, 0); |
| statement.BindInt64(6, 0); |
| statement.BindString(7, kUrl.spec()); |
| statement.BindString(8, kClientId.name_space); |
| statement.BindString(9, kClientId.id); |
| statement.BindString(10, kUrl2.spec()); |
| statement.BindString(11, kRequestOrigin); |
| ASSERT_TRUE(statement.Run()); |
| ASSERT_TRUE(connection.DoesTableExist(REQUEST_QUEUE_TABLE_NAME)); |
| ASSERT_FALSE( |
| connection.DoesColumnExist(REQUEST_QUEUE_TABLE_NAME, "fail_state")); |
| } |
| |
| // Class that serves as a base for testing different implementations of the |
| // |RequestQueueStore|. Specific implementations extend the templatized version |
| // of this class and provide appropriate store factory. |
| class RequestQueueStoreTestBase : public testing::Test { |
| public: |
| RequestQueueStoreTestBase(); |
| |
| // Test overrides. |
| void TearDown() override; |
| |
| void PumpLoop(); |
| void ClearResults(); |
| void InitializeStore(RequestQueueStore* store); |
| |
| void InitializeCallback(bool success); |
| // Callback used for get requests. |
| void GetRequestsDone(bool result, |
| std::vector<std::unique_ptr<SavePageRequest>> requests); |
| // Callback used for add/update request. |
| void AddOrUpdateDone(UpdateStatus result); |
| void AddRequestDone(ItemActionStatus status); |
| void UpdateRequestDone(UpdateRequestsResult result); |
| // Callback used for reset. |
| void ResetDone(bool result); |
| |
| LastResult last_result() const { return last_result_; } |
| UpdateStatus last_update_status() const { return last_update_status_; } |
| const std::vector<std::unique_ptr<SavePageRequest>>& last_requests() const { |
| return last_requests_; |
| } |
| ItemActionStatus last_add_status() const { return last_add_status_; } |
| |
| UpdateRequestsResult* last_update_result() const { |
| return last_update_result_.get(); |
| } |
| |
| protected: |
| base::ScopedTempDir temp_directory_; |
| |
| private: |
| LastResult last_result_; |
| UpdateStatus last_update_status_; |
| ItemActionStatus last_add_status_; |
| std::unique_ptr<UpdateRequestsResult> last_update_result_; |
| std::vector<std::unique_ptr<SavePageRequest>> last_requests_; |
| |
| scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; |
| base::ThreadTaskRunnerHandle task_runner_handle_; |
| }; |
| |
| RequestQueueStoreTestBase::RequestQueueStoreTestBase() |
| : last_result_(LastResult::RESULT_NONE), |
| last_update_status_(UpdateStatus::FAILED), |
| last_add_status_(ItemActionStatus::NOT_FOUND), |
| task_runner_(new base::TestMockTimeTaskRunner), |
| task_runner_handle_(task_runner_) { |
| EXPECT_TRUE(temp_directory_.CreateUniqueTempDir()); |
| } |
| |
| void RequestQueueStoreTestBase::TearDown() { |
| // Wait for all the pieces of the store to delete itself properly. |
| PumpLoop(); |
| } |
| |
| void RequestQueueStoreTestBase::PumpLoop() { |
| task_runner_->RunUntilIdle(); |
| } |
| |
| void RequestQueueStoreTestBase::ClearResults() { |
| last_result_ = LastResult::RESULT_NONE; |
| last_update_status_ = UpdateStatus::FAILED; |
| last_add_status_ = ItemActionStatus::NOT_FOUND; |
| last_requests_.clear(); |
| last_update_result_.reset(nullptr); |
| } |
| |
| void RequestQueueStoreTestBase::InitializeStore(RequestQueueStore* store) { |
| store->Initialize(base::BindOnce( |
| &RequestQueueStoreTestBase::InitializeCallback, base::Unretained(this))); |
| PumpLoop(); |
| ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result()); |
| ClearResults(); |
| } |
| |
| void RequestQueueStoreTestBase::InitializeCallback(bool success) { |
| last_result_ = success ? LastResult::RESULT_TRUE : LastResult::RESULT_FALSE; |
| } |
| |
| void RequestQueueStoreTestBase::GetRequestsDone( |
| bool result, |
| std::vector<std::unique_ptr<SavePageRequest>> requests) { |
| last_result_ = result ? LastResult::RESULT_TRUE : LastResult::RESULT_FALSE; |
| last_requests_ = std::move(requests); |
| } |
| |
| void RequestQueueStoreTestBase::AddOrUpdateDone(UpdateStatus status) { |
| last_update_status_ = status; |
| } |
| |
| void RequestQueueStoreTestBase::AddRequestDone(ItemActionStatus status) { |
| last_add_status_ = status; |
| } |
| |
| void RequestQueueStoreTestBase::UpdateRequestDone(UpdateRequestsResult result) { |
| last_update_result_ = |
| std::make_unique<UpdateRequestsResult>(std::move(result)); |
| } |
| |
| void RequestQueueStoreTestBase::ResetDone(bool result) { |
| last_result_ = result ? LastResult::RESULT_TRUE : LastResult::RESULT_FALSE; |
| } |
| |
| // Defines a store test fixture templatized by the store factory. |
| class RequestQueueStoreTest : public RequestQueueStoreTestBase { |
| public: |
| std::unique_ptr<RequestQueueStore> BuildStore() { |
| return std::make_unique<RequestQueueStore>( |
| base::ThreadTaskRunnerHandle::Get(), temp_directory_.GetPath()); |
| } |
| std::unique_ptr<RequestQueueStore> BuildStoreWithOldSchema(int version) { |
| if (version == 57) { |
| BuildTestStoreWithSchemaFromM57(temp_directory_.GetPath()); |
| } else if (version == 58) { |
| BuildTestStoreWithSchemaFromM58(temp_directory_.GetPath()); |
| } else if (version == 61) { |
| BuildTestStoreWithSchemaFromM61(temp_directory_.GetPath()); |
| } |
| |
| return std::make_unique<RequestQueueStore>( |
| base::ThreadTaskRunnerHandle::Get(), temp_directory_.GetPath()); |
| } |
| |
| protected: |
| }; |
| |
| // This portion causes test fixtures to be defined. |
| // Notice that in the store we are using "this->" to refer to the methods |
| // defined on the |RequestQuieueStoreBaseTest| class. That's by design. |
| |
| TEST_F(RequestQueueStoreTest, UpgradeFromVersion57Store) { |
| std::unique_ptr<RequestQueueStore> store = BuildStoreWithOldSchema(57); |
| // In-memory store does not support upgrading. |
| if (!store) |
| return; |
| this->InitializeStore(store.get()); |
| |
| store->GetRequests(base::BindOnce(&RequestQueueStoreTestBase::GetRequestsDone, |
| base::Unretained(this))); |
| this->PumpLoop(); |
| ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result()); |
| ASSERT_EQ(1u, this->last_requests().size()); |
| EXPECT_EQ(kRequestId, this->last_requests()[0]->request_id()); |
| EXPECT_EQ(kUrl, this->last_requests()[0]->url()); |
| EXPECT_EQ(GURL(), this->last_requests()[0]->original_url()); |
| } |
| |
| TEST_F(RequestQueueStoreTest, UpgradeFromVersion58Store) { |
| std::unique_ptr<RequestQueueStore> store(BuildStoreWithOldSchema(58)); |
| // In-memory store does not support upgrading. |
| if (!store) |
| return; |
| this->InitializeStore(store.get()); |
| |
| store->GetRequests(base::BindOnce(&RequestQueueStoreTestBase::GetRequestsDone, |
| base::Unretained(this))); |
| this->PumpLoop(); |
| ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result()); |
| ASSERT_EQ(1u, this->last_requests().size()); |
| EXPECT_EQ(kRequestId, this->last_requests()[0]->request_id()); |
| EXPECT_EQ(kUrl, this->last_requests()[0]->url()); |
| EXPECT_EQ(kUrl2, this->last_requests()[0]->original_url()); |
| EXPECT_EQ("", this->last_requests()[0]->request_origin()); |
| } |
| |
| TEST_F(RequestQueueStoreTest, UpgradeFromVersion61Store) { |
| std::unique_ptr<RequestQueueStore> store(BuildStoreWithOldSchema(61)); |
| // In-memory store does not support upgrading. |
| if (!store) |
| return; |
| this->InitializeStore(store.get()); |
| |
| store->GetRequests(base::BindOnce(&RequestQueueStoreTestBase::GetRequestsDone, |
| base::Unretained(this))); |
| this->PumpLoop(); |
| ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result()); |
| ASSERT_EQ(1u, this->last_requests().size()); |
| EXPECT_EQ(kRequestId, this->last_requests()[0]->request_id()); |
| EXPECT_EQ(kUrl, this->last_requests()[0]->url()); |
| EXPECT_EQ(kUrl2, this->last_requests()[0]->original_url()); |
| EXPECT_EQ(kRequestOrigin, this->last_requests()[0]->request_origin()); |
| EXPECT_EQ(0, static_cast<int>(this->last_requests()[0]->fail_state())); |
| } |
| |
| TEST_F(RequestQueueStoreTest, GetRequestsEmpty) { |
| std::unique_ptr<RequestQueueStore> store(this->BuildStore()); |
| this->InitializeStore(store.get()); |
| |
| store->GetRequests(base::BindOnce(&RequestQueueStoreTestBase::GetRequestsDone, |
| base::Unretained(this))); |
| ASSERT_EQ(LastResult::RESULT_NONE, this->last_result()); |
| this->PumpLoop(); |
| ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result()); |
| ASSERT_TRUE(this->last_requests().empty()); |
| } |
| |
| TEST_F(RequestQueueStoreTest, GetRequestsByIds) { |
| std::unique_ptr<RequestQueueStore> store(this->BuildStore()); |
| this->InitializeStore(store.get()); |
| |
| base::Time creation_time = OfflineClock()->Now(); |
| SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time, |
| kUserRequested); |
| store->AddRequest(request1, |
| base::BindOnce(&RequestQueueStoreTestBase::AddRequestDone, |
| base::Unretained(this))); |
| SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time, |
| kUserRequested); |
| store->AddRequest(request2, |
| base::BindOnce(&RequestQueueStoreTestBase::AddRequestDone, |
| base::Unretained(this))); |
| this->PumpLoop(); |
| this->ClearResults(); |
| |
| std::vector<int64_t> request_ids{kRequestId, kRequestId2}; |
| store->GetRequestsByIds( |
| request_ids, base::BindOnce(&RequestQueueStoreTestBase::UpdateRequestDone, |
| base::Unretained(this))); |
| |
| ASSERT_FALSE(this->last_update_result()); |
| this->PumpLoop(); |
| ASSERT_TRUE(this->last_update_result()); |
| EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size()); |
| EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first); |
| EXPECT_EQ(ItemActionStatus::SUCCESS, |
| this->last_update_result()->item_statuses[0].second); |
| EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first); |
| EXPECT_EQ(ItemActionStatus::SUCCESS, |
| this->last_update_result()->item_statuses[1].second); |
| EXPECT_EQ(2UL, this->last_update_result()->updated_items.size()); |
| EXPECT_EQ(request1, this->last_update_result()->updated_items.at(0)); |
| EXPECT_EQ(request2, this->last_update_result()->updated_items.at(1)); |
| this->ClearResults(); |
| |
| request_ids.clear(); |
| request_ids.push_back(kRequestId); |
| request_ids.push_back(kRequestId3); |
| request_ids.push_back(kRequestId); |
| |
| store->GetRequestsByIds( |
| request_ids, base::BindOnce(&RequestQueueStoreTestBase::UpdateRequestDone, |
| base::Unretained(this))); |
| |
| ASSERT_FALSE(this->last_update_result()); |
| this->PumpLoop(); |
| ASSERT_TRUE(this->last_update_result()); |
| EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size()); |
| EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first); |
| EXPECT_EQ(ItemActionStatus::SUCCESS, |
| this->last_update_result()->item_statuses[0].second); |
| EXPECT_EQ(kRequestId3, this->last_update_result()->item_statuses[1].first); |
| EXPECT_EQ(ItemActionStatus::NOT_FOUND, |
| this->last_update_result()->item_statuses[1].second); |
| EXPECT_EQ(1UL, this->last_update_result()->updated_items.size()); |
| EXPECT_EQ(request1, this->last_update_result()->updated_items.at(0)); |
| } |
| |
| TEST_F(RequestQueueStoreTest, AddRequest) { |
| std::unique_ptr<RequestQueueStore> store(this->BuildStore()); |
| this->InitializeStore(store.get()); |
| |
| base::Time creation_time = OfflineClock()->Now(); |
| SavePageRequest request(kRequestId, kUrl, kClientId, creation_time, |
| kUserRequested); |
| request.set_original_url(kUrl2); |
| |
| store->AddRequest(request, |
| base::BindOnce(&RequestQueueStoreTestBase::AddRequestDone, |
| base::Unretained(this))); |
| ASSERT_EQ(ItemActionStatus::NOT_FOUND, this->last_add_status()); |
| this->PumpLoop(); |
| ASSERT_EQ(ItemActionStatus::SUCCESS, this->last_add_status()); |
| |
| // Verifying get reqeust results after a request was added. |
| this->ClearResults(); |
| store->GetRequests(base::BindOnce(&RequestQueueStoreTestBase::GetRequestsDone, |
| base::Unretained(this))); |
| ASSERT_EQ(LastResult::RESULT_NONE, this->last_result()); |
| this->PumpLoop(); |
| ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result()); |
| ASSERT_EQ(1ul, this->last_requests().size()); |
| ASSERT_EQ(request, *(this->last_requests()[0].get())); |
| |
| // Verify it is not possible to add the same request twice. |
| this->ClearResults(); |
| store->AddRequest(request, |
| base::BindOnce(&RequestQueueStoreTestBase::AddRequestDone, |
| base::Unretained(this))); |
| ASSERT_EQ(ItemActionStatus::NOT_FOUND, this->last_add_status()); |
| this->PumpLoop(); |
| ASSERT_EQ(ItemActionStatus::ALREADY_EXISTS, this->last_add_status()); |
| |
| // Check that there is still only one item in the store. |
| this->ClearResults(); |
| store->GetRequests(base::BindOnce(&RequestQueueStoreTestBase::GetRequestsDone, |
| base::Unretained(this))); |
| ASSERT_EQ(LastResult::RESULT_NONE, this->last_result()); |
| this->PumpLoop(); |
| ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result()); |
| ASSERT_EQ(1ul, this->last_requests().size()); |
| } |
| |
| TEST_F(RequestQueueStoreTest, AddAndGetRequestsMatch) { |
| std::unique_ptr<RequestQueueStore> store(this->BuildStore()); |
| this->InitializeStore(store.get()); |
| const SavePageRequest request = GetTestRequest(); |
| store->AddRequest(request, |
| base::BindOnce(&RequestQueueStoreTestBase::AddRequestDone, |
| base::Unretained(this))); |
| store->GetRequests(base::BindOnce(&RequestQueueStoreTestBase::GetRequestsDone, |
| base::Unretained(this))); |
| this->PumpLoop(); |
| |
| ASSERT_EQ(ItemActionStatus::SUCCESS, this->last_add_status()); |
| ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result()); |
| ASSERT_EQ(1ul, this->last_requests().size()); |
| EXPECT_EQ(request.ToString(), this->last_requests()[0]->ToString()); |
| } |
| |
| TEST_F(RequestQueueStoreTest, UpdateRequest) { |
| std::unique_ptr<RequestQueueStore> store(this->BuildStore()); |
| this->InitializeStore(store.get()); |
| |
| base::Time creation_time = OfflineClock()->Now(); |
| SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time, |
| kUserRequested); |
| store->AddRequest(original_request, |
| base::BindOnce(&RequestQueueStoreTestBase::AddRequestDone, |
| base::Unretained(this))); |
| this->PumpLoop(); |
| this->ClearResults(); |
| |
| base::Time new_creation_time = |
| creation_time + base::TimeDelta::FromMinutes(1); |
| // Try updating an existing request. |
| SavePageRequest updated_request(kRequestId, kUrl, kClientId, |
| new_creation_time, kUserRequested); |
| updated_request.set_original_url(kUrl2); |
| updated_request.set_request_origin(kRequestOrigin); |
| // Try to update a non-existing request. |
| SavePageRequest updated_request2(kRequestId2, kUrl, kClientId, |
| new_creation_time, kUserRequested); |
| std::vector<SavePageRequest> requests_to_update{updated_request, |
| updated_request2}; |
| store->UpdateRequests( |
| requests_to_update, |
| base::BindOnce(&RequestQueueStoreTestBase::UpdateRequestDone, |
| base::Unretained(this))); |
| ASSERT_FALSE(this->last_update_result()); |
| this->PumpLoop(); |
| ASSERT_TRUE(this->last_update_result()); |
| EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size()); |
| EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first); |
| EXPECT_EQ(ItemActionStatus::SUCCESS, |
| this->last_update_result()->item_statuses[0].second); |
| EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first); |
| EXPECT_EQ(ItemActionStatus::NOT_FOUND, |
| this->last_update_result()->item_statuses[1].second); |
| EXPECT_EQ(1UL, this->last_update_result()->updated_items.size()); |
| EXPECT_EQ(updated_request.ToString(), |
| this->last_update_result()->updated_items.begin()->ToString()); |
| EXPECT_EQ(updated_request, |
| *(this->last_update_result()->updated_items.begin())); |
| |
| // Verifying get reqeust results after a request was updated. |
| this->ClearResults(); |
| store->GetRequests(base::BindOnce(&RequestQueueStoreTestBase::GetRequestsDone, |
| base::Unretained(this))); |
| ASSERT_EQ(LastResult::RESULT_NONE, this->last_result()); |
| this->PumpLoop(); |
| ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result()); |
| ASSERT_EQ(1ul, this->last_requests().size()); |
| ASSERT_EQ(updated_request, *(this->last_requests()[0].get())); |
| } |
| |
| TEST_F(RequestQueueStoreTest, RemoveRequests) { |
| std::unique_ptr<RequestQueueStore> store(this->BuildStore()); |
| this->InitializeStore(store.get()); |
| |
| base::Time creation_time = OfflineClock()->Now(); |
| SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time, |
| kUserRequested); |
| store->AddRequest(request1, |
| base::BindOnce(&RequestQueueStoreTestBase::AddRequestDone, |
| base::Unretained(this))); |
| SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time, |
| kUserRequested); |
| store->AddRequest(request2, |
| base::BindOnce(&RequestQueueStoreTestBase::AddRequestDone, |
| base::Unretained(this))); |
| this->PumpLoop(); |
| this->ClearResults(); |
| |
| std::vector<int64_t> request_ids{kRequestId, kRequestId2}; |
| store->RemoveRequests( |
| request_ids, base::BindOnce(&RequestQueueStoreTestBase::UpdateRequestDone, |
| base::Unretained(this))); |
| |
| ASSERT_FALSE(this->last_update_result()); |
| this->PumpLoop(); |
| ASSERT_TRUE(this->last_update_result()); |
| EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size()); |
| EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first); |
| EXPECT_EQ(ItemActionStatus::SUCCESS, |
| this->last_update_result()->item_statuses[0].second); |
| EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first); |
| EXPECT_EQ(ItemActionStatus::SUCCESS, |
| this->last_update_result()->item_statuses[1].second); |
| EXPECT_EQ(2UL, this->last_update_result()->updated_items.size()); |
| EXPECT_EQ(request1, this->last_update_result()->updated_items.at(0)); |
| EXPECT_EQ(request2, this->last_update_result()->updated_items.at(1)); |
| this->ClearResults(); |
| |
| store->GetRequests(base::BindOnce(&RequestQueueStoreTestBase::GetRequestsDone, |
| base::Unretained(this))); |
| this->PumpLoop(); |
| ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result()); |
| ASSERT_TRUE(this->last_requests().empty()); |
| this->ClearResults(); |
| |
| // Try to remove a request that is not in the queue. |
| store->RemoveRequests( |
| request_ids, base::BindOnce(&RequestQueueStoreTestBase::UpdateRequestDone, |
| base::Unretained(this))); |
| ASSERT_FALSE(this->last_update_result()); |
| this->PumpLoop(); |
| ASSERT_TRUE(this->last_update_result()); |
| // When requests are missing, we expect the results to say so, but since they |
| // are missing, no requests should have been returned. |
| EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size()); |
| EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first); |
| EXPECT_EQ(ItemActionStatus::NOT_FOUND, |
| this->last_update_result()->item_statuses[0].second); |
| EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first); |
| EXPECT_EQ(ItemActionStatus::NOT_FOUND, |
| this->last_update_result()->item_statuses[1].second); |
| EXPECT_EQ(0UL, this->last_update_result()->updated_items.size()); |
| } |
| |
| TEST_F(RequestQueueStoreTest, ResetStore) { |
| std::unique_ptr<RequestQueueStore> store(this->BuildStore()); |
| this->InitializeStore(store.get()); |
| |
| base::Time creation_time = OfflineClock()->Now(); |
| SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time, |
| kUserRequested); |
| store->AddRequest(original_request, |
| base::BindOnce(&RequestQueueStoreTestBase::AddRequestDone, |
| base::Unretained(this))); |
| this->PumpLoop(); |
| this->ClearResults(); |
| |
| store->Reset(base::BindOnce(&RequestQueueStoreTestBase::ResetDone, |
| base::Unretained(this))); |
| ASSERT_EQ(LastResult::RESULT_NONE, this->last_result()); |
| this->PumpLoop(); |
| ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result()); |
| this->ClearResults(); |
| |
| this->InitializeStore(store.get()); |
| store->GetRequests(base::BindOnce(&RequestQueueStoreTestBase::GetRequestsDone, |
| base::Unretained(this))); |
| this->PumpLoop(); |
| ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result()); |
| ASSERT_TRUE(this->last_requests().empty()); |
| } |
| |
| // Makes sure that persistent DB is actually persisting requests across store |
| // restarts. |
| TEST_F(RequestQueueStoreTest, SaveCloseReopenRead) { |
| std::unique_ptr<RequestQueueStore> store(BuildStore()); |
| this->InitializeStore(store.get()); |
| |
| base::Time creation_time = OfflineClock()->Now(); |
| SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time, |
| kUserRequested); |
| store->AddRequest(original_request, |
| base::BindOnce(&RequestQueueStoreTestBase::AddRequestDone, |
| base::Unretained(this))); |
| PumpLoop(); |
| ClearResults(); |
| |
| // Resets the store, using the same temp directory. The contents should be |
| // intact. First reset is done separately to release DB lock. |
| store.reset(); |
| store = BuildStore(); |
| this->InitializeStore(store.get()); |
| |
| store->GetRequests(base::BindOnce(&RequestQueueStoreTestBase::GetRequestsDone, |
| base::Unretained(this))); |
| ASSERT_EQ(LastResult::RESULT_NONE, this->last_result()); |
| this->PumpLoop(); |
| ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result()); |
| ASSERT_EQ(1ul, this->last_requests().size()); |
| ASSERT_TRUE(original_request == *(this->last_requests().at(0).get())); |
| } |
| |
| } // namespace |
| } // namespace offline_pages |