blob: 4e034e7f4538add30c4f84604920a6ce3b7d3581 [file] [log] [blame]
// Copyright 2019 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 "ios/chrome/browser/reading_list/offline_page_tab_helper.h"
#include <memory>
#include "base/path_service.h"
#include "base/run_loop.h"
#import "base/test/ios/wait_util.h"
#include "base/time/default_clock.h"
#include "components/reading_list/core/reading_list_model_impl.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#import "ios/web/public/test/fakes/fake_navigation_context.h"
#import "ios/web/public/test/fakes/test_navigation_manager.h"
#import "ios/web/public/test/fakes/test_web_state.h"
#include "ios/web/public/test/web_test.h"
#include "testing/gtest/include/gtest/gtest.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
const char kTestURL[] = "http://foo.test";
const char kTestSecondURL[] = "http://bar.test";
const char kTestTitle[] = "title";
const char kTestDistilledPath[] = "distilled.html";
const char kTestDistilledURL[] = "http://foo.bar/distilled";
const char kTestDirectory[] = "ios/testing/data/";
// A simple implementation of ReadingListModel that only support functions
// needed to load an offline page.
class FakeReadingListModel : public ReadingListModel {
public:
~FakeReadingListModel() override {}
bool loaded() const override { return loaded_; }
syncer::ModelTypeSyncBridge* GetModelTypeSyncBridge() override {
NOTREACHED();
return nullptr;
}
const std::vector<GURL> Keys() const override {
NOTREACHED();
return std::vector<GURL>();
}
size_t size() const override {
DCHECK(loaded_);
return 0;
}
size_t unread_size() const override {
NOTREACHED();
return 0;
}
size_t unseen_size() const override {
NOTREACHED();
return 0;
}
void MarkAllSeen() override { NOTREACHED(); }
bool DeleteAllEntries() override {
NOTREACHED();
return false;
}
bool GetLocalUnseenFlag() const override {
NOTREACHED();
return false;
}
void ResetLocalUnseenFlag() override { NOTREACHED(); }
const ReadingListEntry* GetEntryByURL(const GURL& gurl) const override {
DCHECK(loaded_);
if (entry_->URL() == gurl) {
return entry_;
}
return nullptr;
}
const ReadingListEntry* GetFirstUnreadEntry(bool distilled) const override {
NOTREACHED();
return nullptr;
}
const ReadingListEntry& AddEntry(const GURL& url,
const std::string& title,
reading_list::EntrySource source) override {
NOTREACHED();
return *entry_;
}
void RemoveEntryByURL(const GURL& url) override { NOTREACHED(); }
void SetReadStatus(const GURL& url, bool read) override {
if (entry_->URL() == url) {
entry_->SetRead(true, base::Time());
}
}
void SetEntryTitle(const GURL& url, const std::string& title) override {
NOTREACHED();
}
void SetEntryDistilledState(
const GURL& url,
ReadingListEntry::DistillationState state) override {
NOTREACHED();
}
void SetEntryDistilledInfo(const GURL& url,
const base::FilePath& distilled_path,
const GURL& distilled_url,
int64_t distilation_size,
const base::Time& distilation_time) override {
NOTREACHED();
}
void SetContentSuggestionsExtra(
const GURL& url,
const reading_list::ContentSuggestionsExtra& extra) override {
NOTREACHED();
}
void SetEntry(ReadingListEntry* entry) { entry_ = entry; }
void SetLoaded() {
loaded_ = true;
for (auto& observer : observers_) {
observer.ReadingListModelLoaded(this);
}
}
private:
ReadingListEntry* entry_ = nullptr;
bool loaded_ = false;
};
}
// Test fixture to test loading of Reading list offline pages.
class OfflinePageTabHelperTest : public web::WebTest {
public:
void SetUp() override {
web::WebTest::SetUp();
TestChromeBrowserState::Builder test_cbs_builder;
base::FilePath test_data_dir;
ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
test_data_dir = test_data_dir.AppendASCII(kTestDirectory);
test_cbs_builder.SetPath(test_data_dir);
chrome_browser_state_ = test_cbs_builder.Build();
test_web_state_.SetBrowserState(chrome_browser_state_.get());
test_web_state_.SetNavigationManager(
std::make_unique<web::TestNavigationManager>());
reading_list_model_ = std::make_unique<ReadingListModelImpl>(
/*storage_layer*/ nullptr, /*pref_service*/ nullptr,
base::DefaultClock::GetInstance());
reading_list_model_->AddEntry(GURL(kTestURL), kTestTitle,
reading_list::ADDED_VIA_CURRENT_APP);
OfflinePageTabHelper::CreateForWebState(&test_web_state_,
reading_list_model_.get());
}
protected:
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
std::unique_ptr<ReadingListModelImpl> reading_list_model_;
web::TestWebState test_web_state_;
};
// Test fixture to test loading of Reading list offline pages with a delayed
// ReadingListModel.
class OfflinePageTabHelperDelayedModelTest : public web::WebTest {
void SetUp() override {
web::WebTest::SetUp();
TestChromeBrowserState::Builder test_cbs_builder;
base::FilePath test_data_dir;
ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
test_data_dir = test_data_dir.AppendASCII(kTestDirectory);
test_cbs_builder.SetPath(test_data_dir);
chrome_browser_state_ = test_cbs_builder.Build();
test_web_state_.SetBrowserState(chrome_browser_state_.get());
test_web_state_.SetNavigationManager(
std::make_unique<web::TestNavigationManager>());
fake_reading_list_model_ = std::make_unique<FakeReadingListModel>();
GURL url(kTestURL);
entry_ = std::make_unique<ReadingListEntry>(url, kTestTitle, base::Time());
std::string distilled_path = kTestDistilledPath;
entry_->SetDistilledInfo(base::FilePath(distilled_path),
GURL(kTestDistilledURL), 50,
base::Time::FromTimeT(100));
fake_reading_list_model_->SetEntry(entry_.get());
OfflinePageTabHelper::CreateForWebState(&test_web_state_,
fake_reading_list_model_.get());
}
protected:
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
std::unique_ptr<FakeReadingListModel> fake_reading_list_model_;
web::TestWebState test_web_state_;
std::unique_ptr<ReadingListEntry> entry_;
};
// Tests that loading an online version does mark it read.
TEST_F(OfflinePageTabHelperTest, TestLoadReadingListSuccess) {
GURL url(kTestURL);
const ReadingListEntry* entry = reading_list_model_->GetEntryByURL(url);
test_web_state_.SetCurrentURL(url);
web::FakeNavigationContext context;
context.SetUrl(url);
context.SetHasCommitted(true);
test_web_state_.OnNavigationStarted(&context);
test_web_state_.OnNavigationFinished(&context);
test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS);
EXPECT_FALSE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForFileOperationTimeout, ^bool {
base::RunLoop().RunUntilIdle();
return test_web_state_.GetLastLoadedData();
}));
EXPECT_FALSE(test_web_state_.GetLastLoadedData());
EXPECT_TRUE(entry->IsRead());
EXPECT_FALSE(OfflinePageTabHelper::FromWebState(&test_web_state_)
->presenting_offline_page());
}
// Tests that failing loading an online version does not mark it read.
TEST_F(OfflinePageTabHelperTest, TestLoadReadingListFailure) {
GURL url(kTestURL);
const ReadingListEntry* entry = reading_list_model_->GetEntryByURL(url);
web::FakeNavigationContext context;
context.SetUrl(url);
context.SetHasCommitted(true);
test_web_state_.OnNavigationStarted(&context);
test_web_state_.OnNavigationFinished(&context);
test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::FAILURE);
EXPECT_FALSE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForFileOperationTimeout, ^bool {
base::RunLoop().RunUntilIdle();
return test_web_state_.GetLastLoadedData();
}));
EXPECT_FALSE(test_web_state_.GetLastLoadedData());
EXPECT_FALSE(entry->IsRead());
EXPECT_FALSE(OfflinePageTabHelper::FromWebState(&test_web_state_)
->presenting_offline_page());
}
// Tests that failing loading an online version will load the distilled version
// and mark it read.
TEST_F(OfflinePageTabHelperTest, TestLoadReadingListDistilled) {
GURL url(kTestURL);
std::string distilled_path = kTestDistilledPath;
reading_list_model_->SetEntryDistilledInfo(
url, base::FilePath(distilled_path), GURL(kTestDistilledURL), 50,
base::Time::FromTimeT(100));
const ReadingListEntry* entry = reading_list_model_->GetEntryByURL(url);
test_web_state_.SetCurrentURL(url);
web::FakeNavigationContext context;
context.SetHasCommitted(true);
std::unique_ptr<web::NavigationItem> item = web::NavigationItem::Create();
static_cast<web::TestNavigationManager*>(
test_web_state_.GetNavigationManager())
->SetLastCommittedItem(item.get());
context.SetUrl(url);
test_web_state_.OnNavigationStarted(&context);
test_web_state_.OnNavigationFinished(&context);
test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::FAILURE);
EXPECT_FALSE(test_web_state_.GetLastLoadedData());
EXPECT_FALSE(entry->IsRead());
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForFileOperationTimeout, ^bool {
base::RunLoop().RunUntilIdle();
return test_web_state_.GetLastLoadedData();
}));
EXPECT_TRUE(entry->IsRead());
EXPECT_TRUE(OfflinePageTabHelper::FromWebState(&test_web_state_)
->presenting_offline_page());
}
// Tests that failing loading an online version does not load distilled
// version if another navigation started.
TEST_F(OfflinePageTabHelperTest, TestLoadReadingListFailureThenNavigate) {
GURL url(kTestURL);
GURL second_url(kTestSecondURL);
const ReadingListEntry* entry = reading_list_model_->GetEntryByURL(url);
web::FakeNavigationContext context;
context.SetHasCommitted(true);
context.SetUrl(url);
test_web_state_.OnNavigationStarted(&context);
test_web_state_.OnNavigationFinished(&context);
test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::FAILURE);
web::FakeNavigationContext second_context;
second_context.SetUrl(second_url);
second_context.SetHasCommitted(true);
test_web_state_.OnNavigationStarted(&second_context);
EXPECT_FALSE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForFileOperationTimeout, ^bool {
base::RunLoop().RunUntilIdle();
return test_web_state_.GetLastLoadedData();
}));
EXPECT_FALSE(test_web_state_.GetLastLoadedData());
EXPECT_FALSE(entry->IsRead());
EXPECT_FALSE(OfflinePageTabHelper::FromWebState(&test_web_state_)
->presenting_offline_page());
}
// Tests that OfflinePageTabHelper correctly reports existence of a distilled
// version.
TEST_F(OfflinePageTabHelperTest, TestHasDistilledVersionForOnlineUrl) {
OfflinePageTabHelper* offline_page_tab_helper =
OfflinePageTabHelper::FromWebState(&test_web_state_);
GURL url(kTestURL);
EXPECT_FALSE(offline_page_tab_helper->HasDistilledVersionForOnlineUrl(url));
GURL second_url(kTestSecondURL);
EXPECT_FALSE(
offline_page_tab_helper->HasDistilledVersionForOnlineUrl(second_url));
std::string distilled_path = kTestDistilledPath;
reading_list_model_->SetEntryDistilledInfo(
url, base::FilePath(distilled_path), GURL(kTestDistilledURL), 50,
base::Time::FromTimeT(100));
EXPECT_TRUE(offline_page_tab_helper->HasDistilledVersionForOnlineUrl(url));
}
// Tests that OfflinePageTabHelper correctly shows Offline page if model takes
// a long time to load.
TEST_F(OfflinePageTabHelperDelayedModelTest, TestLateReadingListModelLoading) {
OfflinePageTabHelper* offline_page_tab_helper =
OfflinePageTabHelper::FromWebState(&test_web_state_);
GURL url(kTestURL);
EXPECT_FALSE(offline_page_tab_helper->HasDistilledVersionForOnlineUrl(url));
web::FakeNavigationContext context;
context.SetHasCommitted(true);
std::unique_ptr<web::NavigationItem> item = web::NavigationItem::Create();
static_cast<web::TestNavigationManager*>(
test_web_state_.GetNavigationManager())
->SetLastCommittedItem(item.get());
context.SetUrl(url);
test_web_state_.OnNavigationStarted(&context);
test_web_state_.OnNavigationFinished(&context);
test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::FAILURE);
EXPECT_FALSE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForFileOperationTimeout, ^bool {
base::RunLoop().RunUntilIdle();
return test_web_state_.GetLastLoadedData();
}));
EXPECT_FALSE(entry_->IsRead());
EXPECT_FALSE(offline_page_tab_helper->presenting_offline_page());
fake_reading_list_model_->SetLoaded();
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForFileOperationTimeout, ^bool {
base::RunLoop().RunUntilIdle();
return test_web_state_.GetLastLoadedData();
}));
EXPECT_TRUE(entry_->IsRead());
EXPECT_TRUE(offline_page_tab_helper->presenting_offline_page());
}