Index: components/offline_pages/core/downloads/download_ui_adapter_unittest.cc |
diff --git a/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc b/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc |
index c65dc37d075320fa2d36345da55abcef813f3af7..f69153205aa23b2196065b70158756624ca5edd2 100644 |
--- a/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc |
+++ b/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc |
@@ -16,10 +16,13 @@ |
#include "base/files/file_path.h" |
#include "base/run_loop.h" |
#include "base/single_thread_task_runner.h" |
+#include "base/strings/string_number_conversions.h" |
#include "base/strings/utf_string_conversions.h" |
#include "base/test/test_mock_time_task_runner.h" |
#include "base/threading/thread_task_runner_handle.h" |
#include "base/time/time.h" |
+#include "components/offline_pages/core/background/offliner_stub.h" |
+#include "components/offline_pages/core/background/request_coordinator_stub_taco.h" |
#include "components/offline_pages/core/client_namespace_constants.h" |
#include "components/offline_pages/core/client_policy_controller.h" |
#include "components/offline_pages/core/stub_offline_page_model.h" |
@@ -31,7 +34,6 @@ namespace { |
// Constants for a test OfflinePageItem. |
static const int kTestOfflineId1 = 1; |
static const int kTestOfflineId2 = 2; |
-static const int kTestOfflineId3 = 3; |
static const char kTestUrl[] = "http://foo.com/bar.mhtml"; |
static const char kTestGuid1[] = "cccccccc-cccc-4ccc-0ccc-ccccccccccc1"; |
static const char kTestGuid2[] = "cccccccc-cccc-4ccc-0ccc-ccccccccccc2"; |
@@ -47,23 +49,39 @@ static const base::Time kTestCreationTime = base::Time::Now(); |
static const base::string16 kTestTitle = base::ASCIIToUTF16("test title"); |
} // namespace |
+// Mock DownloadUIAdapter::Delegate |
+class DownloadUIAdapterDelegate : public DownloadUIAdapter::Delegate { |
+ public: |
+ DownloadUIAdapterDelegate() {} |
+ ~DownloadUIAdapterDelegate() override {} |
+ |
+ // DownloadUIAdapter::Delegate |
+ bool IsVisibleInUI(const ClientId& client_id) override { return is_visible; } |
+ bool IsTemporarilyHiddenInUI(const ClientId& client_id) override { |
+ return is_temporarily_hidden; |
+ } |
+ |
+ bool is_visible = true; |
+ bool is_temporarily_hidden = false; |
+}; |
+ |
// Mock OfflinePageModel for testing the SavePage calls. |
class MockOfflinePageModel : public StubOfflinePageModel { |
public: |
MockOfflinePageModel(base::TestMockTimeTaskRunner* task_runner) |
: observer_(nullptr), |
task_runner_(task_runner), |
- policy_controller_(new ClientPolicyController()) { |
- adapter.reset(new DownloadUIAdapter(this)); |
- // Add one page. |
+ policy_controller_(new ClientPolicyController()) {} |
+ |
+ ~MockOfflinePageModel() override {} |
+ |
+ void AddInitialPage() { |
OfflinePageItem page(GURL(kTestUrl), kTestOfflineId1, kTestClientId1, |
kTestFilePath, kFileSize, kTestCreationTime); |
page.title = kTestTitle; |
pages[kTestOfflineId1] = page; |
} |
- ~MockOfflinePageModel() override {} |
- |
// OfflinePageModel overrides. |
void AddObserver(Observer* observer) override { |
EXPECT_TRUE(observer != nullptr); |
@@ -111,9 +129,6 @@ class MockOfflinePageModel : public StubOfflinePageModel { |
return policy_controller_.get(); |
} |
- // Normally, OfflinePageModel owns this adapter, so lets test it this way. |
- std::unique_ptr<DownloadUIAdapter> adapter; |
- |
std::map<int64_t, OfflinePageItem> pages; |
private: |
@@ -125,6 +140,11 @@ class MockOfflinePageModel : public StubOfflinePageModel { |
DISALLOW_COPY_AND_ASSIGN(MockOfflinePageModel); |
}; |
+// Creates mock versions for OfflinePageModel, RequestCoordinator and their |
+// dependencies, then passes them to DownloadUIAdapter for testing. |
+// Note that initially the OfflienPageModel is not "loaded". PumpLoop() will |
+// load it, firing ItemsLoaded callback to the Adapter. Hence some tests |
+// start from PumpLoop() right away if they don't need to test this. |
class DownloadUIAdapterTest : public testing::Test, |
public DownloadUIAdapter::Observer { |
public: |
@@ -144,22 +164,49 @@ class DownloadUIAdapterTest : public testing::Test, |
// queue. |
void PumpLoop(); |
+ int64_t AddRequest(const GURL& url, const ClientId& client_id); |
+ |
+ RequestCoordinator* request_coordinator() { |
+ return request_coordinator_taco_->request_coordinator(); |
+ } |
+ |
bool items_loaded; |
std::vector<std::string> added_guids, updated_guids, deleted_guids; |
std::unique_ptr<MockOfflinePageModel> model; |
+ DownloadUIAdapterDelegate* adapter_delegate; |
+ std::unique_ptr<DownloadUIAdapter> adapter; |
+ OfflinerStub* offliner_stub; |
private: |
+ std::unique_ptr<RequestCoordinatorStubTaco> request_coordinator_taco_; |
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; |
+ base::ThreadTaskRunnerHandle task_runner_handle_; |
}; |
DownloadUIAdapterTest::DownloadUIAdapterTest() |
- : items_loaded(false), task_runner_(new base::TestMockTimeTaskRunner) {} |
+ : items_loaded(false), |
+ task_runner_(new base::TestMockTimeTaskRunner), |
+ task_runner_handle_(task_runner_) {} |
DownloadUIAdapterTest::~DownloadUIAdapterTest() {} |
void DownloadUIAdapterTest::SetUp() { |
- model.reset(new MockOfflinePageModel(task_runner_.get())); |
- model->adapter->AddObserver(this); |
+ model = base::MakeUnique<MockOfflinePageModel>(task_runner_.get()); |
+ std::unique_ptr<DownloadUIAdapterDelegate> delegate = |
+ base::MakeUnique<DownloadUIAdapterDelegate>(); |
+ adapter_delegate = delegate.get(); |
+ request_coordinator_taco_ = base::MakeUnique<RequestCoordinatorStubTaco>(); |
+ |
+ std::unique_ptr<OfflinerStub> offliner = base::MakeUnique<OfflinerStub>(); |
+ offliner_stub = offliner.get(); |
+ request_coordinator_taco_->SetOffliner(std::move(offliner)); |
+ |
+ request_coordinator_taco_->CreateRequestCoordinator(); |
+ adapter = base::MakeUnique<DownloadUIAdapter>( |
+ model.get(), request_coordinator_taco_->request_coordinator(), |
+ std::move(delegate)); |
+ |
+ adapter->AddObserver(this); |
} |
void DownloadUIAdapterTest::ItemsLoaded() { |
@@ -182,22 +229,33 @@ void DownloadUIAdapterTest::PumpLoop() { |
task_runner_->RunUntilIdle(); |
} |
+int64_t DownloadUIAdapterTest::AddRequest(const GURL& url, |
+ const ClientId& client_id) { |
+ return request_coordinator()->SavePageLater( |
+ url, client_id, /* user_requested */ true, |
+ RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER); |
+} |
+ |
TEST_F(DownloadUIAdapterTest, InitialLoad) { |
- EXPECT_NE(nullptr, model->adapter); |
+ EXPECT_NE(nullptr, adapter.get()); |
+ model->AddInitialPage(); |
EXPECT_FALSE(items_loaded); |
PumpLoop(); |
EXPECT_TRUE(items_loaded); |
- const DownloadUIItem* item = model->adapter->GetItem(kTestGuid1); |
+ const DownloadUIItem* item = adapter->GetItem(kTestGuid1); |
EXPECT_NE(nullptr, item); |
} |
TEST_F(DownloadUIAdapterTest, InitialItemConversion) { |
+ model->AddInitialPage(); |
EXPECT_EQ(1UL, model->pages.size()); |
EXPECT_EQ(kTestGuid1, model->pages[kTestOfflineId1].client_id.id); |
PumpLoop(); |
- const DownloadUIItem* item = model->adapter->GetItem(kTestGuid1); |
+ const DownloadUIItem* item = adapter->GetItem(kTestGuid1); |
EXPECT_EQ(kTestGuid1, item->guid); |
EXPECT_EQ(kTestUrl, item->url.spec()); |
+ EXPECT_EQ(DownloadUIItem::DownloadState::COMPLETE, item->download_state); |
+ EXPECT_EQ(0, item->download_progress_bytes); |
EXPECT_EQ(kTestFilePath, item->target_path); |
EXPECT_EQ(kTestCreationTime, item->start_time); |
EXPECT_EQ(kFileSize, item->total_bytes); |
@@ -205,6 +263,7 @@ TEST_F(DownloadUIAdapterTest, InitialItemConversion) { |
} |
TEST_F(DownloadUIAdapterTest, ItemDeletedAdded) { |
+ model->AddInitialPage(); |
PumpLoop(); |
// Add page, notify adapter. |
OfflinePageItem page(GURL(kTestUrl), kTestOfflineId2, kTestClientId2, |
@@ -223,26 +282,54 @@ TEST_F(DownloadUIAdapterTest, ItemDeletedAdded) { |
EXPECT_EQ(kTestGuid2, deleted_guids[1]); |
} |
-TEST_F(DownloadUIAdapterTest, ItemWithWrongNamespace) { |
+TEST_F(DownloadUIAdapterTest, NotVisibleItem) { |
+ model->AddInitialPage(); |
PumpLoop(); |
+ adapter_delegate->is_visible = false; |
OfflinePageItem page1( |
GURL(kTestUrl), kTestOfflineId2, kTestClientIdOtherNamespace, |
base::FilePath(kTestFilePath), kFileSize, kTestCreationTime); |
model->AddPageAndNotifyAdapter(page1); |
PumpLoop(); |
- // Should not add the page with wrong namespace. |
+ // Should not add the page. |
EXPECT_EQ(0UL, added_guids.size()); |
+} |
- OfflinePageItem page2(GURL(kTestUrl), kTestOfflineId3, kTestClientIdOtherGuid, |
- base::FilePath(kTestFilePath), kFileSize, |
- kTestCreationTime); |
- model->AddPageAndNotifyAdapter(page2); |
+TEST_F(DownloadUIAdapterTest, TemporarilyNotVisibleItem) { |
+ adapter_delegate->is_temporarily_hidden = true; |
+ model->AddInitialPage(); |
PumpLoop(); |
- // Should not add the page with wrong guid. |
+ // Initial Item should be invisible in the collection now. |
+ EXPECT_EQ(nullptr, adapter->GetItem(kTestGuid1)); |
+ EXPECT_EQ(0UL, adapter->GetAllItems().size()); |
EXPECT_EQ(0UL, added_guids.size()); |
+ EXPECT_EQ(0UL, deleted_guids.size()); |
+ |
+ adapter_delegate->is_temporarily_hidden = false; |
+ // Notify adapter about visibility change for the clientId of initial page. |
+ adapter->TemporaryHiddenStatusChanged(kTestClientId1); |
+ PumpLoop(); |
+ |
+ // There should be OnAdded simulated. |
+ EXPECT_EQ(1UL, added_guids.size()); |
+ EXPECT_EQ(0UL, deleted_guids.size()); |
+ // Also the item should be visible in the collection of items now. |
+ EXPECT_NE(nullptr, adapter->GetItem(kTestGuid1)); |
+ EXPECT_EQ(1UL, adapter->GetAllItems().size()); |
+ |
+ // Switch visibility back to hidden |
+ adapter_delegate->is_temporarily_hidden = true; |
+ adapter->TemporaryHiddenStatusChanged(kTestClientId1); |
+ // There should be OnDeleted fired. |
+ EXPECT_EQ(1UL, added_guids.size()); |
+ EXPECT_EQ(1UL, deleted_guids.size()); |
+ // Also the item should be visible in the collection of items now. |
+ EXPECT_EQ(nullptr, adapter->GetItem(kTestGuid1)); |
+ EXPECT_EQ(0UL, adapter->GetAllItems().size()); |
} |
TEST_F(DownloadUIAdapterTest, ItemAdded) { |
+ model->AddInitialPage(); |
PumpLoop(); |
// Clear the initial page and replace it with updated one. |
model->pages.clear(); |
@@ -261,16 +348,95 @@ TEST_F(DownloadUIAdapterTest, ItemAdded) { |
} |
TEST_F(DownloadUIAdapterTest, NoHangingLoad) { |
- EXPECT_NE(nullptr, model->adapter); |
+ model->AddInitialPage(); |
+ EXPECT_NE(nullptr, adapter.get()); |
EXPECT_FALSE(items_loaded); |
// Removal of last observer causes cache unload of not-yet-loaded cache. |
- model->adapter->RemoveObserver(this); |
+ adapter->RemoveObserver(this); |
// This will complete async fetch of items, but... |
PumpLoop(); |
// items should not be loaded when there is no observers! |
EXPECT_FALSE(items_loaded); |
// This should not crash. |
- model->adapter->AddObserver(this); |
+ adapter->AddObserver(this); |
+} |
+ |
+TEST_F(DownloadUIAdapterTest, LoadExistingRequest) { |
+ AddRequest(GURL(kTestUrl), kTestClientId1); |
+ PumpLoop(); |
+ EXPECT_TRUE(items_loaded); |
+ const DownloadUIItem* item = adapter->GetItem(kTestGuid1); |
+ EXPECT_NE(nullptr, item); |
+} |
+ |
+TEST_F(DownloadUIAdapterTest, AddRequest) { |
+ PumpLoop(); |
+ EXPECT_TRUE(items_loaded); |
+ EXPECT_EQ(0UL, added_guids.size()); |
+ AddRequest(GURL(kTestUrl), kTestClientId1); |
+ PumpLoop(); |
+ EXPECT_EQ(1UL, added_guids.size()); |
+ EXPECT_EQ(kTestClientId1.id, added_guids[0]); |
+ const DownloadUIItem* item = adapter->GetItem(kTestGuid1); |
+ EXPECT_NE(nullptr, item); |
+} |
+ |
+TEST_F(DownloadUIAdapterTest, RemoveRequest) { |
+ int64_t id = AddRequest(GURL(kTestUrl), kTestClientId1); |
+ PumpLoop(); |
+ // No added requests, the initial one is loaded. |
+ EXPECT_EQ(0UL, added_guids.size()); |
+ EXPECT_NE(nullptr, adapter->GetItem(kTestGuid1)); |
+ EXPECT_EQ(0UL, deleted_guids.size()); |
+ |
+ std::vector<int64_t> requests_to_remove = {id}; |
+ request_coordinator()->RemoveRequests( |
+ requests_to_remove, |
+ base::Bind( |
+ [](int64_t id, const MultipleItemStatuses& statuses) { |
+ EXPECT_EQ(1UL, statuses.size()); |
+ EXPECT_EQ(id, statuses[0].first); |
+ EXPECT_EQ(ItemActionStatus::SUCCESS, statuses[0].second); |
+ }, |
+ id)); |
+ PumpLoop(); |
+ |
+ EXPECT_EQ(0UL, added_guids.size()); |
+ EXPECT_EQ(1UL, deleted_guids.size()); |
+ EXPECT_EQ(kTestClientId1.id, deleted_guids[0]); |
+ EXPECT_EQ(nullptr, adapter->GetItem(kTestGuid1)); |
+} |
+ |
+TEST_F(DownloadUIAdapterTest, RequestBecomesPage) { |
+ // This will cause requests to be 'offlined' all the way and removed. |
+ offliner_stub->enable_callback(true); |
+ AddRequest(GURL(kTestUrl), kTestClientId1); |
+ PumpLoop(); |
+ |
+ const DownloadUIItem* item = adapter->GetItem(kTestGuid1); |
+ EXPECT_NE(nullptr, item); |
+ // The item is still IN_PROGRESS, since we did not delete it when |
+ // request is competed successfully, waiting for the page with the |
+ // same client_id to come in. |
+ EXPECT_EQ(DownloadUIItem::DownloadState::IN_PROGRESS, item->download_state); |
+ // Add a new saved page with the same client id. |
+ // This simulates what happens when the request is completed. |
+ // It should not fire and OnAdded or OnDeleted, just OnUpdated. |
+ OfflinePageItem page(GURL(kTestUrl), kTestOfflineId1, kTestClientId1, |
+ base::FilePath(kTestFilePath), kFileSize, |
+ kTestCreationTime); |
+ model->AddPageAndNotifyAdapter(page); |
+ PumpLoop(); |
+ |
+ // No added or deleted items, the one existing item should be 'updated'. |
+ EXPECT_EQ(0UL, added_guids.size()); |
+ EXPECT_EQ(0UL, deleted_guids.size()); |
+ |
+ EXPECT_GE(updated_guids.size(), 1UL); |
+ std::string last_updated_guid = updated_guids[updated_guids.size() - 1]; |
+ item = adapter->GetItem(last_updated_guid); |
+ EXPECT_NE(nullptr, item); |
+ EXPECT_EQ(DownloadUIItem::DownloadState::COMPLETE, item->download_state); |
} |
} // namespace offline_pages |