blob: c0f2809526a4530ce7445f5a391041ba7b075c90 [file] [log] [blame]
// Copyright 2018 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 "chrome/browser/android/download/available_offline_content_provider.h"
#include "base/strings/string_util.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/testing_profile.h"
#include "components/offline_items_collection/core/offline_content_aggregator.h"
#include "components/offline_items_collection/core/offline_item.h"
#include "components/offline_items_collection/core/test_support/mock_offline_content_provider.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "url/gurl.h"
namespace android {
namespace {
using offline_items_collection::OfflineContentAggregator;
using testing::_;
const char kProviderNamespace[] = "offline_pages";
std::unique_ptr<KeyedService> BuildOfflineContentAggregator(
content::BrowserContext* context) {
return std::make_unique<OfflineContentAggregator>();
}
offline_items_collection::OfflineItem UselessItem() {
offline_items_collection::OfflineItem item;
item.original_url = GURL("https://useless");
item.filter = offline_items_collection::FILTER_IMAGE;
item.id.id = "Useless";
item.id.name_space = kProviderNamespace;
return item;
}
offline_items_collection::OfflineItem OldOfflinePage() {
offline_items_collection::OfflineItem item;
item.original_url = GURL("https://already_read");
item.filter = offline_items_collection::FILTER_PAGE;
item.id.id = "AlreadyRead";
item.id.name_space = kProviderNamespace;
item.is_suggested = true;
item.last_accessed_time = base::Time::Now();
return item;
}
offline_items_collection::OfflineItem SuggestedOfflinePageItem() {
offline_items_collection::OfflineItem item;
item.original_url = GURL("https://page");
item.filter = offline_items_collection::FILTER_PAGE;
item.id.id = "SuggestedPage";
item.id.name_space = kProviderNamespace;
item.is_suggested = true;
item.title = "Page Title";
item.description = "snippet";
// Using Time::Now() isn't ideal, but this should result in "4 hours ago"
// even if the test takes 1 hour to run.
item.creation_time =
base::Time::Now() - base::TimeDelta::FromMinutes(60 * 3.5);
return item;
}
offline_items_collection::OfflineItem VideoItem() {
offline_items_collection::OfflineItem item;
item.original_url = GURL("https://video");
item.filter = offline_items_collection::FILTER_VIDEO;
item.id.id = "VideoItem";
item.id.name_space = kProviderNamespace;
return item;
}
offline_items_collection::OfflineItem AudioItem() {
offline_items_collection::OfflineItem item;
item.original_url = GURL("https://audio");
item.filter = offline_items_collection::FILTER_AUDIO;
item.id.id = "AudioItem";
item.id.name_space = kProviderNamespace;
return item;
}
offline_items_collection::OfflineItemVisuals TestThumbnail() {
offline_items_collection::OfflineItemVisuals visuals;
visuals.icon = gfx::test::CreateImage(2, 4);
return visuals;
}
class AvailableOfflineContentTest : public testing::Test {
protected:
void SetUp() override {
// To control the items in the aggregator, we create it and register a
// single MockOfflineContentProvider.
aggregator_ = static_cast<OfflineContentAggregator*>(
OfflineContentAggregatorFactory::GetInstance()->SetTestingFactoryAndUse(
&profile_, &BuildOfflineContentAggregator));
aggregator_->RegisterProvider(kProviderNamespace, &content_provider_);
content_provider_.SetVisuals({});
}
std::vector<chrome::mojom::AvailableOfflineContentPtr> ListAndWait() {
std::vector<chrome::mojom::AvailableOfflineContentPtr> suggestions;
chrome::mojom::AvailableOfflineContentProviderAsyncWaiter waiter(
&provider_);
waiter.List(&suggestions);
return suggestions;
}
chrome::mojom::AvailableOfflineContentSummaryPtr SummarizeAndWait() {
chrome::mojom::AvailableOfflineContentSummaryPtr summary;
chrome::mojom::AvailableOfflineContentProviderAsyncWaiter waiter(
&provider_);
waiter.Summarize(&summary);
return summary;
}
content::TestBrowserThreadBundle thread_bundle_;
TestingProfile profile_;
base::test::ScopedFeatureList scoped_feature_list_;
OfflineContentAggregator* aggregator_;
offline_items_collection::MockOfflineContentProvider content_provider_;
AvailableOfflineContentProvider provider_{&profile_};
};
TEST_F(AvailableOfflineContentTest, NoContent) {
std::vector<chrome::mojom::AvailableOfflineContentPtr> suggestions =
ListAndWait();
chrome::mojom::AvailableOfflineContentSummaryPtr summary = SummarizeAndWait();
EXPECT_EQ(0u, summary->total_items);
EXPECT_FALSE(summary->has_prefetched_page);
EXPECT_FALSE(summary->has_offline_page);
EXPECT_FALSE(summary->has_video);
EXPECT_FALSE(summary->has_audio);
EXPECT_TRUE(suggestions.empty());
}
TEST_F(AvailableOfflineContentTest, ListAllContentFilteredOut) {
scoped_feature_list_.InitAndEnableFeature(features::kNewNetErrorPageUI);
content_provider_.SetItems({UselessItem(), OldOfflinePage()});
// Call List() and Summary().
std::vector<chrome::mojom::AvailableOfflineContentPtr> suggestions =
ListAndWait();
chrome::mojom::AvailableOfflineContentSummaryPtr summary = SummarizeAndWait();
EXPECT_EQ(2u, summary->total_items);
EXPECT_TRUE(summary->has_prefetched_page);
EXPECT_TRUE(summary->has_offline_page);
EXPECT_FALSE(summary->has_video);
EXPECT_FALSE(summary->has_audio);
EXPECT_TRUE(suggestions.empty());
}
TEST_F(AvailableOfflineContentTest, ListThreeItems) {
scoped_feature_list_.InitAndEnableFeature(features::kNewNetErrorPageUI);
content_provider_.SetItems({
UselessItem(), VideoItem(), SuggestedOfflinePageItem(), AudioItem(),
});
content_provider_.SetVisuals(
{{SuggestedOfflinePageItem().id, TestThumbnail()}});
// Call List() and Summary().
std::vector<chrome::mojom::AvailableOfflineContentPtr> suggestions =
ListAndWait();
chrome::mojom::AvailableOfflineContentSummaryPtr summary = SummarizeAndWait();
// Check summary.
EXPECT_EQ(4u, summary->total_items);
EXPECT_TRUE(summary->has_prefetched_page);
EXPECT_TRUE(summary->has_offline_page);
EXPECT_TRUE(summary->has_video);
EXPECT_TRUE(summary->has_audio);
// Check that the right suggestions have been received in order.
EXPECT_EQ(3ul, suggestions.size());
EXPECT_EQ(SuggestedOfflinePageItem().id.id, suggestions[0]->id);
EXPECT_EQ(VideoItem().id.id, suggestions[1]->id);
EXPECT_EQ(AudioItem().id.id, suggestions[2]->id);
// For a single suggestion, make sure all the fields are correct. We can
// assume the other items match.
const chrome::mojom::AvailableOfflineContentPtr& first = suggestions[0];
const offline_items_collection::OfflineItem page_item =
SuggestedOfflinePageItem();
EXPECT_EQ(page_item.id.id, first->id);
EXPECT_EQ(page_item.id.name_space, first->name_space);
EXPECT_EQ(page_item.title, first->title);
EXPECT_EQ(page_item.description, first->snippet);
EXPECT_EQ("4 hours ago", first->date_modified);
// At the time of writing this test, the output was:
// data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAAECAYAAACk7+45AAAAFk
// lEQVQYlWNk+M/wn4GBgYGJAQowGQBCcgIG00vTRwAAAABJRU5ErkJggg==
// Since other encodings are possible, just check the prefix. PNGs all have
// the same 8 byte header.
EXPECT_TRUE(base::StartsWith(first->thumbnail_data_uri.spec(),
"data:image/png;base64,iVBORw0K",
base::CompareCase::SENSITIVE));
// TODO(crbug.com/852872): Add attribution.
EXPECT_EQ("", first->attribution);
}
TEST_F(AvailableOfflineContentTest, NotEnabled) {
scoped_feature_list_.InitAndDisableFeature(features::kNewNetErrorPageUI);
content_provider_.SetItems({SuggestedOfflinePageItem()});
std::vector<chrome::mojom::AvailableOfflineContentPtr> suggestions =
ListAndWait();
EXPECT_TRUE(suggestions.empty());
}
} // namespace
} // namespace android