blob: 0f5aeb7f4fdee283036120791a8a19ef51c18f4e [file] [log] [blame]
// Copyright 2015 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/offline_page_model.h"
#include <stdint.h>
#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "components/bookmarks/browser/bookmark_client.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/browser/bookmark_node.h"
#include "components/bookmarks/browser/bookmark_storage.h"
#include "components/bookmarks/browser/bookmark_undo_delegate.h"
#include "components/bookmarks/browser/bookmark_undo_provider.h"
#include "components/bookmarks/test/test_bookmark_client.h"
#include "components/offline_pages/offline_page_item.h"
#include "components/offline_pages/offline_page_test_archiver.h"
#include "components/offline_pages/offline_page_test_store.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using SavePageResult = offline_pages::OfflinePageModel::SavePageResult;
using DeletePageResult = offline_pages::OfflinePageModel::DeletePageResult;
namespace offline_pages {
namespace {
const GURL kTestUrl("http://example.com");
const int64_t kTestPageBookmarkId1 = 1234LL;
const GURL kTestUrl2("http://other.page.com");
const GURL kTestUrl3("http://test.xyz");
const GURL kFileUrl("file:///foo");
const int64_t kTestPageBookmarkId2 = 5678LL;
const int64_t kTestPageBookmarkId3 = 42LL;
const int64_t kTestFileSize = 876543LL;
} // namespace
class OfflinePageModelTest
: public testing::Test,
public OfflinePageModel::Observer,
public OfflinePageTestArchiver::Observer,
public base::SupportsWeakPtr<OfflinePageModelTest> {
public:
OfflinePageModelTest();
~OfflinePageModelTest() override;
void SetUp() override;
void TearDown() override;
// OfflinePageModel::Observer implementation.
void OfflinePageModelLoaded(OfflinePageModel* model) override;
void OfflinePageModelChanged(OfflinePageModel* model) override;
void OfflinePageDeleted(int64_t bookmark_id) override;
// OfflinePageTestArchiver::Observer implementation.
void SetLastPathCreatedByArchiver(const base::FilePath& file_path) override;
// OfflinePageModel callbacks.
void OnSavePageDone(SavePageResult result);
void OnDeletePageDone(DeletePageResult result);
void OnClearAllDone();
// OfflinePageMetadataStore callbacks.
void OnStoreUpdateDone(bool /* success */);
scoped_ptr<OfflinePageTestArchiver> BuildArchiver(
const GURL& url,
OfflinePageArchiver::ArchiverResult result);
scoped_ptr<OfflinePageMetadataStore> BuildStore();
scoped_ptr<OfflinePageModel> BuildModel(
scoped_ptr<OfflinePageMetadataStore> store);
void ResetModel();
// Utility methods.
// Runs until all of the tasks that are not delayed are gone from the task
// queue.
void PumpLoop();
// Fast-forwards virtual time by |delta|, causing tasks with a remaining
// delay less than or equal to |delta| to be executed.
void FastForwardBy(base::TimeDelta delta);
void ResetResults();
OfflinePageTestStore* GetStore();
void SavePage(const GURL& url, int64_t bookmark_id);
void SavePageWithArchiverResult(const GURL& url,
int64_t bookmark_id,
OfflinePageArchiver::ArchiverResult result);
OfflinePageModel* model() { return model_.get(); }
SavePageResult last_save_result() const {
return last_save_result_;
}
DeletePageResult last_delete_result() const {
return last_delete_result_;
}
int64_t last_deleted_bookmark_id() const { return last_deleted_bookmark_id_; }
const base::FilePath& last_archiver_path() { return last_archiver_path_; }
private:
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
base::ThreadTaskRunnerHandle task_runner_handle_;
base::ScopedTempDir temp_dir_;
scoped_ptr<OfflinePageModel> model_;
SavePageResult last_save_result_;
DeletePageResult last_delete_result_;
base::FilePath last_archiver_path_;
int64_t last_deleted_bookmark_id_;
};
OfflinePageModelTest::OfflinePageModelTest()
: task_runner_(new base::TestMockTimeTaskRunner),
task_runner_handle_(task_runner_),
last_save_result_(SavePageResult::CANCELLED),
last_delete_result_(DeletePageResult::CANCELLED),
last_deleted_bookmark_id_(-1) {
}
OfflinePageModelTest::~OfflinePageModelTest() {
}
void OfflinePageModelTest::SetUp() {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
model_ = BuildModel(BuildStore());
model_->AddObserver(this);
PumpLoop();
}
void OfflinePageModelTest::TearDown() {
model_->RemoveObserver(this);
model_.reset();
PumpLoop();
}
void OfflinePageModelTest::OfflinePageModelLoaded(OfflinePageModel* model) {
ASSERT_EQ(model_.get(), model);
}
void OfflinePageModelTest::OfflinePageModelChanged(OfflinePageModel* model) {
ASSERT_EQ(model_.get(), model);
}
void OfflinePageModelTest::OfflinePageDeleted(int64_t bookmark_id) {
last_deleted_bookmark_id_ = bookmark_id;
}
void OfflinePageModelTest::SetLastPathCreatedByArchiver(
const base::FilePath& file_path) {
last_archiver_path_ = file_path;
}
void OfflinePageModelTest::OnSavePageDone(
OfflinePageModel::SavePageResult result) {
last_save_result_ = result;
}
void OfflinePageModelTest::OnDeletePageDone(DeletePageResult result) {
last_delete_result_ = result;
}
void OfflinePageModelTest::OnClearAllDone() {
PumpLoop();
}
void OfflinePageModelTest::OnStoreUpdateDone(bool /* success - ignored */) {
}
scoped_ptr<OfflinePageTestArchiver> OfflinePageModelTest::BuildArchiver(
const GURL& url,
OfflinePageArchiver::ArchiverResult result) {
return scoped_ptr<OfflinePageTestArchiver>(new OfflinePageTestArchiver(
this, url, result, kTestFileSize, base::ThreadTaskRunnerHandle::Get()));
}
scoped_ptr<OfflinePageMetadataStore> OfflinePageModelTest::BuildStore() {
return scoped_ptr<OfflinePageMetadataStore>(
new OfflinePageTestStore(base::ThreadTaskRunnerHandle::Get()));
}
scoped_ptr<OfflinePageModel> OfflinePageModelTest::BuildModel(
scoped_ptr<OfflinePageMetadataStore> store) {
return scoped_ptr<OfflinePageModel>(new OfflinePageModel(
std::move(store), temp_dir_.path(), base::ThreadTaskRunnerHandle::Get()));
}
void OfflinePageModelTest::ResetModel() {
model_->RemoveObserver(this);
OfflinePageTestStore* old_store = GetStore();
scoped_ptr<OfflinePageMetadataStore> new_store(
new OfflinePageTestStore(*old_store));
model_ = BuildModel(std::move(new_store));
model_->AddObserver(this);
PumpLoop();
}
void OfflinePageModelTest::PumpLoop() {
task_runner_->RunUntilIdle();
}
void OfflinePageModelTest::FastForwardBy(base::TimeDelta delta) {
task_runner_->FastForwardBy(delta);
}
void OfflinePageModelTest::ResetResults() {
last_save_result_ = SavePageResult::CANCELLED;
last_delete_result_ = DeletePageResult::CANCELLED;
last_archiver_path_.clear();
}
OfflinePageTestStore* OfflinePageModelTest::GetStore() {
return static_cast<OfflinePageTestStore*>(model()->GetStoreForTesting());
}
void OfflinePageModelTest::SavePage(const GURL& url, int64_t bookmark_id) {
SavePageWithArchiverResult(
url,
bookmark_id,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED);
}
void OfflinePageModelTest::SavePageWithArchiverResult(
const GURL& url,
int64_t bookmark_id,
OfflinePageArchiver::ArchiverResult result) {
scoped_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(url, result));
model()->SavePage(
url, bookmark_id, std::move(archiver),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
}
TEST_F(OfflinePageModelTest, SavePageSuccessful) {
EXPECT_FALSE(model()->HasOfflinePages());
SavePage(kTestUrl, kTestPageBookmarkId1);
EXPECT_TRUE(model()->HasOfflinePages());
OfflinePageTestStore* store = GetStore();
EXPECT_EQ(kTestUrl, store->last_saved_page().url);
EXPECT_EQ(kTestPageBookmarkId1, store->last_saved_page().bookmark_id);
// Save last_archiver_path since it will be referred to later.
base::FilePath archiver_path = last_archiver_path();
EXPECT_EQ(archiver_path, store->last_saved_page().file_path);
EXPECT_EQ(kTestFileSize, store->last_saved_page().file_size);
EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
ResetResults();
const std::vector<OfflinePageItem>& offline_pages = model()->GetAllPages();
EXPECT_EQ(1UL, offline_pages.size());
EXPECT_EQ(kTestUrl, offline_pages[0].url);
EXPECT_EQ(kTestPageBookmarkId1, offline_pages[0].bookmark_id);
EXPECT_EQ(archiver_path, offline_pages[0].file_path);
EXPECT_EQ(kTestFileSize, offline_pages[0].file_size);
EXPECT_EQ(0, offline_pages[0].access_count);
EXPECT_EQ(0, offline_pages[0].flags);
}
TEST_F(OfflinePageModelTest, SavePageOfflineArchiverCancelled) {
SavePageWithArchiverResult(
kTestUrl,
kTestPageBookmarkId1,
OfflinePageArchiver::ArchiverResult::ERROR_CANCELED);
EXPECT_EQ(SavePageResult::CANCELLED, last_save_result());
}
TEST_F(OfflinePageModelTest, SavePageOfflineArchiverDeviceFull) {
SavePageWithArchiverResult(
kTestUrl,
kTestPageBookmarkId1,
OfflinePageArchiver::ArchiverResult::ERROR_DEVICE_FULL);
EXPECT_EQ(SavePageResult::DEVICE_FULL, last_save_result());
}
TEST_F(OfflinePageModelTest, SavePageOfflineArchiverContentUnavailable) {
SavePageWithArchiverResult(
kTestUrl,
kTestPageBookmarkId1,
OfflinePageArchiver::ArchiverResult::ERROR_CONTENT_UNAVAILABLE);
EXPECT_EQ(SavePageResult::CONTENT_UNAVAILABLE, last_save_result());
}
TEST_F(OfflinePageModelTest, SavePageOfflineCreationFailed) {
SavePageWithArchiverResult(
kTestUrl,
kTestPageBookmarkId1,
OfflinePageArchiver::ArchiverResult::ERROR_ARCHIVE_CREATION_FAILED);
EXPECT_EQ(SavePageResult::ARCHIVE_CREATION_FAILED, last_save_result());
}
TEST_F(OfflinePageModelTest, SavePageOfflineArchiverReturnedWrongUrl) {
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(GURL("http://other.random.url.com"),
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, std::move(archiver),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
EXPECT_EQ(SavePageResult::ARCHIVE_CREATION_FAILED, last_save_result());
}
TEST_F(OfflinePageModelTest, SavePageOfflineCreationStoreWriteFailure) {
GetStore()->set_test_scenario(
OfflinePageTestStore::TestScenario::WRITE_FAILED);
SavePage(kTestUrl, kTestPageBookmarkId1);
EXPECT_EQ(SavePageResult::STORE_FAILURE, last_save_result());
}
TEST_F(OfflinePageModelTest, SavePageLocalFileFailed) {
// Don't create archiver since it will not be needed for pages that are not
// going to be saved.
model()->SavePage(
kFileUrl, kTestPageBookmarkId1, scoped_ptr<OfflinePageTestArchiver>(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
EXPECT_EQ(SavePageResult::SKIPPED, last_save_result());
}
TEST_F(OfflinePageModelTest, SavePageOfflineArchiverTwoPages) {
scoped_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
// archiver_ptr will be valid until after first PumpLoop() call after
// CompleteCreateArchive() is called.
OfflinePageTestArchiver* archiver_ptr = archiver.get();
archiver_ptr->set_delayed(true);
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, std::move(archiver),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
EXPECT_TRUE(archiver_ptr->create_archive_called());
// Request to save another page.
SavePage(kTestUrl2, kTestPageBookmarkId2);
OfflinePageTestStore* store = GetStore();
EXPECT_EQ(kTestUrl2, store->last_saved_page().url);
EXPECT_EQ(kTestPageBookmarkId2, store->last_saved_page().bookmark_id);
base::FilePath archiver_path2 = last_archiver_path();
EXPECT_EQ(archiver_path2, store->last_saved_page().file_path);
EXPECT_EQ(kTestFileSize, store->last_saved_page().file_size);
EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
ResetResults();
archiver_ptr->CompleteCreateArchive();
// After this pump loop archiver_ptr is invalid.
PumpLoop();
EXPECT_EQ(kTestUrl, store->last_saved_page().url);
EXPECT_EQ(kTestPageBookmarkId1, store->last_saved_page().bookmark_id);
base::FilePath archiver_path = last_archiver_path();
EXPECT_EQ(archiver_path, store->last_saved_page().file_path);
EXPECT_EQ(kTestFileSize, store->last_saved_page().file_size);
EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
ResetResults();
const std::vector<OfflinePageItem>& offline_pages = model()->GetAllPages();
EXPECT_EQ(2UL, offline_pages.size());
EXPECT_EQ(kTestUrl, offline_pages[0].url);
EXPECT_EQ(kTestPageBookmarkId1, offline_pages[0].bookmark_id);
EXPECT_EQ(archiver_path, offline_pages[0].file_path);
EXPECT_EQ(kTestFileSize, offline_pages[0].file_size);
EXPECT_EQ(0, offline_pages[0].access_count);
EXPECT_EQ(0, offline_pages[0].flags);
EXPECT_EQ(kTestUrl2, offline_pages[1].url);
EXPECT_EQ(kTestPageBookmarkId2, offline_pages[1].bookmark_id);
EXPECT_EQ(archiver_path2, offline_pages[1].file_path);
EXPECT_EQ(kTestFileSize, offline_pages[1].file_size);
EXPECT_EQ(0, offline_pages[1].access_count);
EXPECT_EQ(0, offline_pages[1].flags);
}
TEST_F(OfflinePageModelTest, MarkPageAccessed) {
SavePage(kTestUrl, kTestPageBookmarkId1);
// This will increase access_count by one.
model()->MarkPageAccessed(kTestPageBookmarkId1);
PumpLoop();
const std::vector<OfflinePageItem>& offline_pages = model()->GetAllPages();
EXPECT_EQ(1UL, offline_pages.size());
EXPECT_EQ(kTestUrl, offline_pages[0].url);
EXPECT_EQ(kTestPageBookmarkId1, offline_pages[0].bookmark_id);
EXPECT_EQ(kTestFileSize, offline_pages[0].file_size);
EXPECT_EQ(1, offline_pages[0].access_count);
}
TEST_F(OfflinePageModelTest, MarkPageForDeletion) {
SavePage(kTestUrl, kTestPageBookmarkId1);
GURL offline_url = model()->GetAllPages().begin()->GetOfflineURL();
// Delete the page with undo tiggerred.
model()->MarkPageForDeletion(
kTestPageBookmarkId1,
base::Bind(&OfflinePageModelTest::OnDeletePageDone, AsWeakPtr()));
PumpLoop();
// GetAllPages will not return the page that is marked for deletion.
const std::vector<OfflinePageItem>& offline_pages = model()->GetAllPages();
EXPECT_EQ(0UL, offline_pages.size());
EXPECT_FALSE(model()->HasOfflinePages());
EXPECT_EQ(nullptr, model()->GetPageByOnlineURL(kTestUrl));
EXPECT_EQ(nullptr, model()->GetPageByBookmarkId(kTestPageBookmarkId1));
EXPECT_EQ(nullptr, model()->GetPageByOfflineURL(offline_url));
// Undo the deletion.
model()->UndoPageDeletion(kTestPageBookmarkId1);
PumpLoop();
// GetAllPages will now return the restored page.
const std::vector<OfflinePageItem>& offline_pages_after_undo =
model()->GetAllPages();
EXPECT_EQ(1UL, offline_pages_after_undo.size());
}
TEST_F(OfflinePageModelTest, FinalizePageDeletion) {
scoped_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, std::move(archiver),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
// Mark the page for deletion.
model()->MarkPageForDeletion(
kTestPageBookmarkId1,
base::Bind(&OfflinePageModelTest::OnDeletePageDone, AsWeakPtr()));
PumpLoop();
EXPECT_EQ(1UL, GetStore()->GetAllPages().size());
// Fast forward to trigger the page deletion.
FastForwardBy(OfflinePageModel::GetFinalDeletionDelayForTesting());
EXPECT_EQ(0UL, GetStore()->GetAllPages().size());
}
TEST_F(OfflinePageModelTest, GetAllPagesStoreEmpty) {
const std::vector<OfflinePageItem>& offline_pages = model()->GetAllPages();
EXPECT_EQ(0UL, offline_pages.size());
}
TEST_F(OfflinePageModelTest, GetAllPagesStoreFailure) {
GetStore()->set_test_scenario(
OfflinePageTestStore::TestScenario::LOAD_FAILED);
const std::vector<OfflinePageItem>& offline_pages = model()->GetAllPages();
EXPECT_EQ(0UL, offline_pages.size());
}
TEST_F(OfflinePageModelTest, DeletePageSuccessful) {
OfflinePageTestStore* store = GetStore();
// Save one page.
SavePage(kTestUrl, kTestPageBookmarkId1);
EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
EXPECT_EQ(1u, store->GetAllPages().size());
ResetResults();
// Save another page.
SavePage(kTestUrl2, kTestPageBookmarkId2);
EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
EXPECT_EQ(2u, store->GetAllPages().size());
ResetResults();
// Delete one page.
model()->DeletePageByBookmarkId(
kTestPageBookmarkId1, base::Bind(&OfflinePageModelTest::OnDeletePageDone,
AsWeakPtr()));
PumpLoop();
EXPECT_EQ(last_deleted_bookmark_id(), kTestPageBookmarkId1);
EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
ASSERT_EQ(1u, store->GetAllPages().size());
EXPECT_EQ(kTestUrl2, store->GetAllPages()[0].url);
// Delete another page.
model()->DeletePageByBookmarkId(
kTestPageBookmarkId2, base::Bind(&OfflinePageModelTest::OnDeletePageDone,
AsWeakPtr()));
ResetResults();
PumpLoop();
EXPECT_EQ(last_deleted_bookmark_id(), kTestPageBookmarkId2);
EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
EXPECT_EQ(0u, store->GetAllPages().size());
}
TEST_F(OfflinePageModelTest, DeletePageNotFound) {
model()->DeletePageByBookmarkId(
kTestPageBookmarkId1, base::Bind(&OfflinePageModelTest::OnDeletePageDone,
AsWeakPtr()));
EXPECT_EQ(DeletePageResult::NOT_FOUND, last_delete_result());
}
TEST_F(OfflinePageModelTest, DeletePageStoreFailureOnRemove) {
// Save a page.
SavePage(kTestUrl, kTestPageBookmarkId1);
ResetResults();
// Try to delete this page.
GetStore()->set_test_scenario(
OfflinePageTestStore::TestScenario::REMOVE_FAILED);
model()->DeletePageByBookmarkId(
kTestPageBookmarkId1, base::Bind(&OfflinePageModelTest::OnDeletePageDone,
AsWeakPtr()));
PumpLoop();
EXPECT_EQ(DeletePageResult::STORE_FAILURE, last_delete_result());
}
TEST_F(OfflinePageModelTest, DetectThatOfflineCopyIsMissing) {
// Save a page.
SavePage(kTestUrl, kTestPageBookmarkId1);
ResetResults();
const OfflinePageItem* page =
model()->GetPageByBookmarkId(kTestPageBookmarkId1);
// Delete the offline copy of the page and check the metadata.
base::DeleteFile(page->file_path, false);
model()->CheckForExternalFileDeletion();
PumpLoop();
EXPECT_EQ(last_deleted_bookmark_id(), kTestPageBookmarkId1);
EXPECT_EQ(0UL, model()->GetAllPages().size());
}
TEST_F(OfflinePageModelTest, DetectThatOfflineCopyIsMissingAfterLoad) {
// Save a page.
SavePage(kTestUrl, kTestPageBookmarkId1);
ResetResults();
const OfflinePageItem* page =
model()->GetPageByBookmarkId(kTestPageBookmarkId1);
// Delete the offline copy of the page and check the metadata.
base::DeleteFile(page->file_path, false);
// Reseting the model should trigger the metadata consistency check as well.
ResetModel();
PumpLoop();
EXPECT_EQ(last_deleted_bookmark_id(), kTestPageBookmarkId1);
EXPECT_EQ(0UL, model()->GetAllPages().size());
}
TEST_F(OfflinePageModelTest, DeleteMultiplePages) {
OfflinePageTestStore* store = GetStore();
// Save 3 pages.
scoped_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, std::move(archiver),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
scoped_ptr<OfflinePageTestArchiver> archiver2(BuildArchiver(
kTestUrl2, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
model()->SavePage(
kTestUrl2, kTestPageBookmarkId2, std::move(archiver2),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
scoped_ptr<OfflinePageTestArchiver> archiver3(BuildArchiver(
kTestUrl3, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
model()->SavePage(
kTestUrl3, kTestPageBookmarkId3, std::move(archiver3),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
EXPECT_EQ(3u, store->GetAllPages().size());
// Delete multiple pages.
std::vector<int64_t> ids_to_delete;
ids_to_delete.push_back(kTestPageBookmarkId2);
ids_to_delete.push_back(kTestPageBookmarkId1);
ids_to_delete.push_back(23434LL); // Non-existent ID.
model()->DeletePagesByBookmarkId(
ids_to_delete, base::Bind(&OfflinePageModelTest::OnDeletePageDone,
AsWeakPtr()));
PumpLoop();
// Success is expected if at least one page is deleted successfully.
EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
EXPECT_EQ(1u, store->GetAllPages().size());
}
TEST_F(OfflinePageModelTest, GetPageByBookmarkId) {
SavePage(kTestUrl, kTestPageBookmarkId1);
SavePage(kTestUrl2, kTestPageBookmarkId2);
const OfflinePageItem* page =
model()->GetPageByBookmarkId(kTestPageBookmarkId1);
ASSERT_TRUE(page);
EXPECT_EQ(kTestUrl, page->url);
EXPECT_EQ(kTestPageBookmarkId1, page->bookmark_id);
EXPECT_EQ(kTestFileSize, page->file_size);
page = model()->GetPageByBookmarkId(kTestPageBookmarkId2);
ASSERT_TRUE(page);
EXPECT_EQ(kTestUrl2, page->url);
EXPECT_EQ(kTestPageBookmarkId2, page->bookmark_id);
EXPECT_EQ(kTestFileSize, page->file_size);
page = model()->GetPageByBookmarkId(-42);
EXPECT_FALSE(page);
}
TEST_F(OfflinePageModelTest, GetPageByOfflineURL) {
SavePage(kTestUrl, kTestPageBookmarkId1);
OfflinePageTestStore* store = GetStore();
GURL offline_url = store->last_saved_page().GetOfflineURL();
SavePage(kTestUrl2, kTestPageBookmarkId2);
GURL offline_url2 = store->last_saved_page().GetOfflineURL();
const OfflinePageItem* page = model()->GetPageByOfflineURL(offline_url2);
EXPECT_TRUE(page);
EXPECT_EQ(kTestUrl2, page->url);
EXPECT_EQ(kTestPageBookmarkId2, page->bookmark_id);
page = model()->GetPageByOfflineURL(offline_url);
EXPECT_TRUE(page);
EXPECT_EQ(kTestUrl, page->url);
EXPECT_EQ(kTestPageBookmarkId1, page->bookmark_id);
page = model()->GetPageByOfflineURL(GURL("http://foo"));
EXPECT_FALSE(page);
}
TEST_F(OfflinePageModelTest, GetPageByOnlineURL) {
SavePage(kTestUrl, kTestPageBookmarkId1);
SavePage(kTestUrl2, kTestPageBookmarkId2);
const OfflinePageItem* page = model()->GetPageByOnlineURL(kTestUrl2);
EXPECT_TRUE(page);
EXPECT_EQ(kTestUrl2, page->url);
EXPECT_EQ(kTestPageBookmarkId2, page->bookmark_id);
page = model()->GetPageByOnlineURL(kTestUrl);
EXPECT_TRUE(page);
EXPECT_EQ(kTestUrl, page->url);
EXPECT_EQ(kTestPageBookmarkId1, page->bookmark_id);
page = model()->GetPageByOnlineURL(GURL("http://foo"));
EXPECT_FALSE(page);
}
// Test that model returns pages that are older than 30 days as candidates for
// clean up, hence the numbers in time delta.
TEST_F(OfflinePageModelTest, GetPagesToCleanUp) {
base::Time now = base::Time::Now();
SavePage(kTestUrl, kTestPageBookmarkId1);
GetStore()->UpdateLastAccessTime(kTestPageBookmarkId1,
now - base::TimeDelta::FromDays(40));
SavePage(kTestUrl2, kTestPageBookmarkId2);
GetStore()->UpdateLastAccessTime(kTestPageBookmarkId2,
now - base::TimeDelta::FromDays(31));
SavePage(kTestUrl3, kTestPageBookmarkId3);
GetStore()->UpdateLastAccessTime(kTestPageBookmarkId3,
now - base::TimeDelta::FromDays(29));
ResetModel();
// Only page_1 and page_2 are expected to be picked up by the model as page_3
// has not been in the store long enough.
std::vector<OfflinePageItem> pages_to_clean_up = model()->GetPagesToCleanUp();
EXPECT_EQ(2UL, pages_to_clean_up.size());
EXPECT_EQ(kTestUrl, pages_to_clean_up[0].url);
EXPECT_EQ(kTestPageBookmarkId1, pages_to_clean_up[0].bookmark_id);
EXPECT_EQ(kTestUrl2, pages_to_clean_up[1].url);
EXPECT_EQ(kTestPageBookmarkId2, pages_to_clean_up[1].bookmark_id);
}
TEST_F(OfflinePageModelTest, CanSavePage) {
EXPECT_TRUE(OfflinePageModel::CanSavePage(GURL("http://foo")));
EXPECT_TRUE(OfflinePageModel::CanSavePage(GURL("https://foo")));
EXPECT_FALSE(OfflinePageModel::CanSavePage(GURL("file:///foo")));
EXPECT_FALSE(OfflinePageModel::CanSavePage(GURL("data:image/png;base64,ab")));
EXPECT_FALSE(OfflinePageModel::CanSavePage(GURL("chrome://version")));
EXPECT_FALSE(OfflinePageModel::CanSavePage(GURL("chrome-native://newtab/")));
}
TEST_F(OfflinePageModelTest, ClearAll) {
SavePage(kTestUrl, kTestPageBookmarkId1);
SavePage(kTestUrl2, kTestPageBookmarkId2);
const std::vector<OfflinePageItem>& offline_pages = model()->GetAllPages();
EXPECT_EQ(2UL, offline_pages.size());
EXPECT_EQ(2UL, GetStore()->GetAllPages().size());
base::FilePath archiver_path = offline_pages[0].file_path;
EXPECT_TRUE(base::PathExists(archiver_path));
// ClearAll should delete all the files and wipe out both cache and store.
model()->ClearAll(
base::Bind(&OfflinePageModelTest::OnClearAllDone, AsWeakPtr()));
PumpLoop();
EXPECT_EQ(0UL, model()->GetAllPages().size());
EXPECT_EQ(0UL, GetStore()->GetAllPages().size());
EXPECT_FALSE(base::PathExists(archiver_path));
// The model should reload the store after the reset. All model operations
// should continue to work.
SavePage(kTestUrl2, kTestPageBookmarkId2);
EXPECT_EQ(1UL, model()->GetAllPages().size());
EXPECT_EQ(1UL, GetStore()->GetAllPages().size());
}
class OfflinePageModelBookmarkChangeTest :
public OfflinePageModelTest,
public bookmarks::BookmarkUndoDelegate {
public:
OfflinePageModelBookmarkChangeTest();
~OfflinePageModelBookmarkChangeTest() override;
void SetUp() override;
void TearDown() override;
// bookmarks::BookmarkUndoDelegate implementation.
void SetUndoProvider(bookmarks::BookmarkUndoProvider* provider) override;
void OnBookmarkNodeRemoved(bookmarks::BookmarkModel* model,
const bookmarks::BookmarkNode* parent,
int index,
scoped_ptr<bookmarks::BookmarkNode> node) override;
const bookmarks::BookmarkNode* CreateBookmarkNode(const GURL& url);
void UndoBookmarkRemoval();
bookmarks::BookmarkModel* bookmark_model() const {
return bookmark_model_.get();
}
private:
bookmarks::TestBookmarkClient bookmark_client_;
scoped_ptr<bookmarks::BookmarkModel> bookmark_model_;
bookmarks::BookmarkUndoProvider* bookmark_undo_provider_;
const bookmarks::BookmarkNode* removed_bookmark_parent_;
int removed_bookmark_index_;
scoped_ptr<bookmarks::BookmarkNode> removed_bookmark_node_;
};
OfflinePageModelBookmarkChangeTest::OfflinePageModelBookmarkChangeTest()
: OfflinePageModelTest(),
bookmark_model_(bookmark_client_.CreateModel()),
bookmark_undo_provider_(nullptr),
removed_bookmark_parent_(nullptr),
removed_bookmark_index_(-1) {
}
OfflinePageModelBookmarkChangeTest::~OfflinePageModelBookmarkChangeTest() {
}
void OfflinePageModelBookmarkChangeTest::SetUp() {
OfflinePageModelTest::SetUp();
model()->Start(bookmark_model_.get());
}
void OfflinePageModelBookmarkChangeTest::TearDown() {
OfflinePageModelTest::TearDown();
bookmark_model_.reset();
}
void OfflinePageModelBookmarkChangeTest::SetUndoProvider(
bookmarks::BookmarkUndoProvider* provider) {
bookmark_undo_provider_ = provider;
}
void OfflinePageModelBookmarkChangeTest::OnBookmarkNodeRemoved(
bookmarks::BookmarkModel* model,
const bookmarks::BookmarkNode* parent,
int index,
scoped_ptr<bookmarks::BookmarkNode> node) {
removed_bookmark_parent_ = parent;
removed_bookmark_index_ = index;
removed_bookmark_node_ = std::move(node);
}
const bookmarks::BookmarkNode*
OfflinePageModelBookmarkChangeTest::CreateBookmarkNode(const GURL& url) {
const bookmarks::BookmarkNode* bookmark_root =
bookmark_model()->bookmark_bar_node();
return bookmark_model()->AddURL(bookmark_root, 0, base::string16(), url);
}
void OfflinePageModelBookmarkChangeTest::UndoBookmarkRemoval() {
bookmark_undo_provider_->RestoreRemovedNode(
removed_bookmark_parent_, removed_bookmark_index_,
std::move(removed_bookmark_node_));
removed_bookmark_parent_ = nullptr;
removed_bookmark_index_ = -1;
}
TEST_F(OfflinePageModelBookmarkChangeTest, ChangeBookmakeTitle) {
// Creates a bookmark without offline copy.
const bookmarks::BookmarkNode* bookmark_node = CreateBookmarkNode(kTestUrl);
EXPECT_EQ(0UL, model()->GetAllPages().size());
// Changing the bookmark title should have no effect.
bookmark_model()->SetTitle(
bookmark_node, base::string16(base::ASCIIToUTF16("foo")));
PumpLoop();
EXPECT_EQ(0UL, model()->GetAllPages().size());
// Adds an offline copy for the bookmark.
SavePage(kTestUrl, bookmark_node->id());
EXPECT_EQ(1UL, model()->GetAllPages().size());
// Changes the bookmark title. The offline copy was not affected.
bookmark_model()->SetTitle(
bookmark_node, base::string16(base::ASCIIToUTF16("bar")));
PumpLoop();
EXPECT_EQ(1UL, model()->GetAllPages().size());
}
TEST_F(OfflinePageModelBookmarkChangeTest, ChangeBookmakeURL) {
// Creates a bookmark without offline copy.
const bookmarks::BookmarkNode* bookmark_node = CreateBookmarkNode(kTestUrl2);
EXPECT_EQ(0UL, model()->GetAllPages().size());
// Changing the bookmark URL should have no effect. Chrome should not crash.
// (http://crbug.com/560518)
bookmark_model()->SetURL(bookmark_node, kTestUrl);
PumpLoop();
EXPECT_EQ(0UL, model()->GetAllPages().size());
// Adds an offline copy for the bookmark.
SavePage(kTestUrl, bookmark_node->id());
EXPECT_EQ(1UL, model()->GetAllPages().size());
// The offline copy should be removed upon the bookmark URL change.
// (http://crbug.com/558929)
bookmark_model()->SetURL(bookmark_node, kTestUrl2);
PumpLoop();
EXPECT_EQ(0UL, model()->GetAllPages().size());
}
TEST_F(OfflinePageModelBookmarkChangeTest, RemoveBookmark) {
// Creates a bookmark without offline copy.
const bookmarks::BookmarkNode* bookmark_node = CreateBookmarkNode(kTestUrl2);
EXPECT_EQ(0UL, model()->GetAllPages().size());
// Removing the bookmark should have no effect.
bookmark_model()->Remove(bookmark_node);
PumpLoop();
EXPECT_EQ(0UL, model()->GetAllPages().size());
// Creates a bookmark with offline copy.
bookmark_node = CreateBookmarkNode(kTestUrl);
SavePage(kTestUrl, bookmark_node->id());
EXPECT_EQ(1UL, model()->GetAllPages().size());
// The offline copy should also be removed upon the bookmark removal.
bookmark_model()->Remove(bookmark_node);
PumpLoop();
EXPECT_EQ(0UL, model()->GetAllPages().size());
}
TEST_F(OfflinePageModelBookmarkChangeTest, UndoBookmarkRemoval) {
// Enables undo support.
bookmark_model()->SetUndoDelegate(this);
// Creates a bookmark without offline copy.
const bookmarks::BookmarkNode* bookmark_node = CreateBookmarkNode(kTestUrl2);
EXPECT_EQ(0UL, model()->GetAllPages().size());
// Removing the bookmark and undoing the removal should have no effect.
bookmark_model()->Remove(bookmark_node);
PumpLoop();
UndoBookmarkRemoval();
PumpLoop();
EXPECT_EQ(0UL, model()->GetAllPages().size());
// Creates a bookmark with offline copy.
bookmark_node = CreateBookmarkNode(kTestUrl);
SavePage(kTestUrl, bookmark_node->id());
EXPECT_EQ(1UL, model()->GetAllPages().size());
// The offline copy should also be removed upon the bookmark removal.
bookmark_model()->Remove(bookmark_node);
PumpLoop();
EXPECT_EQ(0UL, model()->GetAllPages().size());
// The offline copy should be restored upon the bookmark restore.
UndoBookmarkRemoval();
PumpLoop();
EXPECT_EQ(1UL, model()->GetAllPages().size());
}
} // namespace offline_pages