blob: a2df42e4b3aaba17f956ed40d87e71ff3598d2ae [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 <algorithm>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "components/bookmarks/browser/bookmark_node.h"
#include "components/offline_pages/offline_page_item.h"
#include "components/offline_pages/offline_page_metadata_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 kTestPageBookmarkId1 = 1234LL;
const GURL kTestUrl2("http://other.page.com");
const GURL kTestUrl3("http://test.xyz");
const GURL kFileUrl("file:///foo");
const int64 kTestPageBookmarkId2 = 5678LL;
const int64 kTestPageBookmarkId3 = 42LL;
const int64 kTestFileSize = 876543LL;
class OfflinePageTestStore : public OfflinePageMetadataStore {
public:
enum class TestScenario {
SUCCESSFUL,
WRITE_FAILED,
LOAD_FAILED,
REMOVE_FAILED,
};
explicit OfflinePageTestStore(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
explicit OfflinePageTestStore(const OfflinePageTestStore& other_store);
~OfflinePageTestStore() override;
// OfflinePageMetadataStore overrides:
void Load(const LoadCallback& callback) override;
void AddOrUpdateOfflinePage(const OfflinePageItem& offline_page,
const UpdateCallback& callback) override;
void RemoveOfflinePages(const std::vector<int64>& bookmark_ids,
const UpdateCallback& callback) override;
void Reset(const ResetCallback& callback) override;
void UpdateLastAccessTime(int64 bookmark_id,
const base::Time& last_access_time);
const OfflinePageItem& last_saved_page() const { return last_saved_page_; }
void set_test_scenario(TestScenario scenario) { scenario_ = scenario; };
const std::vector<OfflinePageItem>& offline_pages() const {
return offline_pages_;
}
private:
OfflinePageItem last_saved_page_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
TestScenario scenario_;
std::vector<OfflinePageItem> offline_pages_;
DISALLOW_ASSIGN(OfflinePageTestStore);
};
OfflinePageTestStore::OfflinePageTestStore(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
: task_runner_(task_runner),
scenario_(TestScenario::SUCCESSFUL) {
}
OfflinePageTestStore::OfflinePageTestStore(
const OfflinePageTestStore& other_store)
: task_runner_(other_store.task_runner_),
scenario_(other_store.scenario_),
offline_pages_(other_store.offline_pages_) {}
OfflinePageTestStore::~OfflinePageTestStore() {
}
void OfflinePageTestStore::Load(const LoadCallback& callback) {
OfflinePageMetadataStore::LoadStatus load_status;
if (scenario_ != TestScenario::LOAD_FAILED) {
load_status = OfflinePageMetadataStore::LOAD_SUCCEEDED;
} else {
load_status = OfflinePageMetadataStore::STORE_LOAD_FAILED;
offline_pages_.clear();
}
task_runner_->PostTask(
FROM_HERE, base::Bind(callback, load_status, offline_pages_));
}
void OfflinePageTestStore::AddOrUpdateOfflinePage(
const OfflinePageItem& offline_page, const UpdateCallback& callback) {
last_saved_page_ = offline_page;
bool result = scenario_ != TestScenario::WRITE_FAILED;
if (result) {
offline_pages_.push_back(offline_page);
}
task_runner_->PostTask(FROM_HERE, base::Bind(callback, result));
}
void OfflinePageTestStore::RemoveOfflinePages(
const std::vector<int64>& bookmark_ids,
const UpdateCallback& callback) {
ASSERT_FALSE(bookmark_ids.empty());
bool result = false;
if (scenario_ != TestScenario::REMOVE_FAILED) {
for (auto iter = offline_pages_.begin();
iter != offline_pages_.end(); ++iter) {
if (iter->bookmark_id == bookmark_ids[0]) {
offline_pages_.erase(iter);
result = true;
break;
}
}
}
task_runner_->PostTask(FROM_HERE, base::Bind(callback, result));
}
void OfflinePageTestStore::Reset(const ResetCallback& callback) {
offline_pages_.clear();
task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
}
void OfflinePageTestStore:: UpdateLastAccessTime(
int64 bookmark_id, const base::Time& last_access_time) {
for (auto& offline_page : offline_pages_) {
if (offline_page.bookmark_id == bookmark_id) {
offline_page.last_access_time = last_access_time;
return;
}
}
}
} // namespace
class OfflinePageModelTest;
class OfflinePageTestArchiver : public OfflinePageArchiver {
public:
OfflinePageTestArchiver(
OfflinePageModelTest* test,
const GURL& url,
ArchiverResult result,
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
~OfflinePageTestArchiver() override;
// OfflinePageArchiver implementation:
void CreateArchive(const base::FilePath& archives_dir,
const CreateArchiveCallback& callback) override;
void CompleteCreateArchive();
void set_delayed(bool delayed) { delayed_ = delayed; }
bool create_archive_called() const { return create_archive_called_; }
private:
OfflinePageModelTest* test_; // Outlive OfflinePageTestArchiver.
GURL url_;
base::FilePath archives_dir_;
ArchiverResult result_;
bool create_archive_called_;
bool delayed_;
CreateArchiveCallback callback_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(OfflinePageTestArchiver);
};
class OfflinePageModelTest
: public testing::Test,
public OfflinePageModel::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 bookmark_id) 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.
void PumpLoop();
void ResetResults();
scoped_refptr<base::SingleThreadTaskRunner> task_runner() {
return message_loop_.task_runner();
}
OfflinePageModel* model() { return model_.get(); }
OfflinePageTestStore* GetStore();
SavePageResult last_save_result() const {
return last_save_result_;
}
DeletePageResult last_delete_result() const {
return last_delete_result_;
}
int64 last_deleted_bookmark_id() const {
return last_deleted_bookmark_id_;
}
const base::FilePath& last_archiver_path() { return last_archiver_path_; }
void set_last_archiver_path(const base::FilePath& last_archiver_path) {
last_archiver_path_ = last_archiver_path;
}
private:
base::MessageLoop message_loop_;
base::ScopedTempDir temp_dir_;
scoped_ptr<OfflinePageModel> model_;
SavePageResult last_save_result_;
DeletePageResult last_delete_result_;
base::FilePath last_archiver_path_;
int64 last_deleted_bookmark_id_;
};
OfflinePageTestArchiver::OfflinePageTestArchiver(
OfflinePageModelTest* test,
const GURL& url,
ArchiverResult result,
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
: test_(test),
url_(url),
result_(result),
create_archive_called_(false),
delayed_(false),
task_runner_(task_runner) {
}
OfflinePageTestArchiver::~OfflinePageTestArchiver() {
EXPECT_TRUE(create_archive_called_);
}
void OfflinePageTestArchiver::CreateArchive(
const base::FilePath& archives_dir,
const CreateArchiveCallback& callback) {
create_archive_called_ = true;
callback_ = callback;
archives_dir_ = archives_dir;
if (!delayed_)
CompleteCreateArchive();
}
void OfflinePageTestArchiver::CompleteCreateArchive() {
DCHECK(!callback_.is_null());
base::FilePath archive_path;
ASSERT_TRUE(base::CreateTemporaryFileInDir(archives_dir_, &archive_path));
test_->set_last_archiver_path(archive_path);
task_runner_->PostTask(FROM_HERE, base::Bind(callback_, this, result_, url_,
archive_path, kTestFileSize));
}
OfflinePageModelTest::OfflinePageModelTest()
: 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().Pass()).Pass();
model_->AddObserver(this);
base::RunLoop().RunUntilIdle();
}
void OfflinePageModelTest::TearDown() {
model_->RemoveObserver(this);
base::RunLoop().RunUntilIdle();
}
void OfflinePageModelTest::OfflinePageModelLoaded(OfflinePageModel* model) {
ASSERT_EQ(model_.get(), model);
}
void OfflinePageModelTest::OfflinePageModelChanged(OfflinePageModel* model) {
ASSERT_EQ(model_.get(), model);
}
void OfflinePageModelTest::OfflinePageDeleted(int64 bookmark_id) {
last_deleted_bookmark_id_ = bookmark_id;
}
void OfflinePageModelTest::OnSavePageDone(
OfflinePageModel::SavePageResult result) {
last_save_result_ = result;
}
void OfflinePageModelTest::OnDeletePageDone(DeletePageResult result) {
last_delete_result_ = result;
}
void OfflinePageModelTest::OnClearAllDone() {
base::RunLoop().RunUntilIdle();
}
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, task_runner()));
}
scoped_ptr<OfflinePageMetadataStore> OfflinePageModelTest::BuildStore() {
return scoped_ptr<OfflinePageMetadataStore>(
new OfflinePageTestStore(task_runner()));
}
scoped_ptr<OfflinePageModel> OfflinePageModelTest::BuildModel(
scoped_ptr<OfflinePageMetadataStore> store) {
return scoped_ptr<OfflinePageModel>(
new OfflinePageModel(store.Pass(), temp_dir_.path(), task_runner()));
}
void OfflinePageModelTest::ResetModel() {
model_->RemoveObserver(this);
OfflinePageTestStore* old_store = GetStore();
scoped_ptr<OfflinePageMetadataStore> new_store(
new OfflinePageTestStore(*old_store));
model_ = BuildModel(new_store.Pass()).Pass();
model_->AddObserver(this);
PumpLoop();
}
void OfflinePageModelTest::PumpLoop() {
base::RunLoop().RunUntilIdle();
}
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());
}
TEST_F(OfflinePageModelTest, SavePageSuccessful) {
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
EXPECT_FALSE(model()->HasOfflinePages());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
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) {
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::ERROR_CANCELED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
EXPECT_EQ(SavePageResult::CANCELLED, last_save_result());
}
TEST_F(OfflinePageModelTest, SavePageOfflineArchiverDeviceFull) {
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::ERROR_DEVICE_FULL)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
EXPECT_EQ(SavePageResult::DEVICE_FULL, last_save_result());
}
TEST_F(OfflinePageModelTest, SavePageOfflineArchiverContentUnavailable) {
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(
kTestUrl,
OfflinePageArchiver::ArchiverResult::ERROR_CONTENT_UNAVAILABLE)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
EXPECT_EQ(SavePageResult::CONTENT_UNAVAILABLE, last_save_result());
}
TEST_F(OfflinePageModelTest, SavePageOfflineCreationFailed) {
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(
kTestUrl,
OfflinePageArchiver::ArchiverResult::ERROR_ARCHIVE_CREATION_FAILED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
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)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
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);
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
EXPECT_EQ(SavePageResult::STORE_FAILURE, last_save_result());
}
TEST_F(OfflinePageModelTest, SavePageLocalFileFailed) {
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)
.Pass());
// 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, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
EXPECT_TRUE(archiver_ptr->create_archive_called());
// Request to save another page.
scoped_ptr<OfflinePageTestArchiver> archiver2(
BuildArchiver(kTestUrl2,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl2, kTestPageBookmarkId2, archiver2.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
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) {
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
// This will increase access_count by one.
model()->MarkPageAccessed(kTestPageBookmarkId1);
base::RunLoop().RunUntilIdle();
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) {
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
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);
base::RunLoop().RunUntilIdle();
// 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, 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.
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
EXPECT_EQ(1u, store->offline_pages().size());
ResetResults();
// Save another page.
scoped_ptr<OfflinePageTestArchiver> archiver2(
BuildArchiver(kTestUrl2,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl2, kTestPageBookmarkId2, archiver2.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
EXPECT_EQ(2u, store->offline_pages().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->offline_pages().size());
EXPECT_EQ(kTestUrl2, store->offline_pages()[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->offline_pages().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.
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
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.
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
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.
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
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, GetPageByBookmarkId) {
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
scoped_ptr<OfflinePageTestArchiver> archiver2(
BuildArchiver(kTestUrl2,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl2, kTestPageBookmarkId2, archiver2.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
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) {
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
OfflinePageTestStore* store = GetStore();
GURL offline_url = store->last_saved_page().GetOfflineURL();
scoped_ptr<OfflinePageTestArchiver> archiver2(
BuildArchiver(kTestUrl2,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl2, kTestPageBookmarkId2, archiver2.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
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) {
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
scoped_ptr<OfflinePageTestArchiver> archiver2(
BuildArchiver(kTestUrl2,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl2, kTestPageBookmarkId2, archiver2.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
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();
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
GetStore()->UpdateLastAccessTime(kTestPageBookmarkId1,
now - base::TimeDelta::FromDays(40));
scoped_ptr<OfflinePageTestArchiver> archiver2(
BuildArchiver(kTestUrl2,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl2, kTestPageBookmarkId2, archiver2.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
GetStore()->UpdateLastAccessTime(kTestPageBookmarkId2,
now - base::TimeDelta::FromDays(31));
scoped_ptr<OfflinePageTestArchiver> archiver3(
BuildArchiver(kTestUrl3,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl3, kTestPageBookmarkId3, archiver3.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
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) {
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
scoped_ptr<OfflinePageTestArchiver> archiver2(
BuildArchiver(kTestUrl2,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl2, kTestPageBookmarkId2, archiver2.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
const std::vector<OfflinePageItem>& offline_pages = model()->GetAllPages();
EXPECT_EQ(2UL, offline_pages.size());
EXPECT_EQ(2UL, GetStore()->offline_pages().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()->offline_pages().size());
EXPECT_FALSE(base::PathExists(archiver_path));
// The model should reload the store after the reset. All model operations
// should continue to work.
scoped_ptr<OfflinePageTestArchiver> archiver3(
BuildArchiver(kTestUrl2,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl2, kTestPageBookmarkId2, archiver3.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
EXPECT_EQ(1UL, model()->GetAllPages().size());
EXPECT_EQ(1UL, GetStore()->offline_pages().size());
}
TEST_F(OfflinePageModelTest, BookmarkNodeChangesUrl) {
scoped_ptr<OfflinePageTestArchiver> archiver(
BuildArchiver(kTestUrl,
OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED)
.Pass());
model()->SavePage(
kTestUrl, kTestPageBookmarkId1, archiver.Pass(),
base::Bind(&OfflinePageModelTest::OnSavePageDone, AsWeakPtr()));
PumpLoop();
EXPECT_EQ(1UL, model()->GetAllPages().size());
bookmarks::BookmarkNode bookmark_node(kTestPageBookmarkId1, kTestUrl2);
model()->BookmarkNodeChanged(nullptr, &bookmark_node);
PumpLoop();
// Offline copy should be removed. Chrome should not crash.
// http://crbug.com/558929
EXPECT_EQ(0UL, model()->GetAllPages().size());
// Chrome should not crash when a bookmark with no offline copy is changed.
// http://crbug.com/560518
bookmark_node.set_url(kTestUrl);
model()->BookmarkNodeChanged(nullptr, &bookmark_node);
PumpLoop();
EXPECT_EQ(0UL, model()->GetAllPages().size());
}
} // namespace offline_pages