blob: 3714d4b9a63da7c36edeb61f6dd3b53f52d4f24d [file] [log] [blame]
// Copyright 2020 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/media/feeds/media_feeds_service.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time_to_iso8601.h"
#include "chrome/browser/media/feeds/media_feeds_service_factory.h"
#include "chrome/browser/media/feeds/media_feeds_store.mojom-shared.h"
#include "chrome/browser/media/history/media_history_keyed_service.h"
#include "chrome/browser/media/history/media_history_test_utils.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "components/prefs/pref_service.h"
#include "components/safe_search_api/stub_url_checker.h"
#include "components/safe_search_api/url_checker.h"
#include "content/public/browser/storage_partition.h"
#include "media/base/media_switches.h"
#include "net/base/load_flags.h"
#include "net/cookies/cookie_access_result.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "services/network/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media_feeds {
using SafeSearchCheckedType =
media_history::MediaHistoryKeyedService::SafeSearchCheckedType;
namespace {
constexpr size_t kCacheSize = 2;
constexpr base::FilePath::CharType kMediaFeedsTestSpecDir[] =
FILE_PATH_LITERAL("chrome/test/data/media/feeds/spec");
const char kTestData[] = R"END({
"@context": "https://schema.org",
"@type": "CompleteDataFeed",
"dataFeedElement": [
{
"@context": "https://schema.org/",
"@type": "VideoObject",
"@id": "https://www.youtube.com/watch?v=lXm6jOQLe1Y",
"author": {
"@type": "Person",
"name": "Google Chrome Developers",
"url": "https://www.youtube.com/user/ChromeDevelopers"
},
"datePublished": "2019-05-09",
"duration": "PT34M41S",
"isFamilyFriendly": "https://schema.org/True",
"name": "Anatomy of a Web Media Experience",
"potentialAction": {
"@type": "WatchAction",
"target": "https://www.youtube.com/watch?v=lXm6jOQLe1Y"
},
"image": {
"@type": "ImageObject",
"width": 336,
"height": 188,
"url": "https://beccahughes.github.io/media/media-feeds/video1.webp"
}
},
{
"@context": "https://schema.org/",
"@type": "TVSeries",
"@id": "https://beccahughes.github.io/media/media-feeds/chrome-release",
"datePublished": "2019-11-10",
"isFamilyFriendly": "https://schema.org/True",
"name": "Chrome Releases",
"containsSeason": {
"@type": "TVSeason",
"numberOfEpisodes": 80,
"episode": {
"@type": "TVEpisode",
"@id": "https://www.youtube.com/watch?v=L0OB0_bO5I0",
"duration": "PT4M16S",
"episodeNumber": 79,
"potentialAction": {
"@type": "WatchAction",
"actionStatus": "https://schema.org/ActiveActionStatus",
"startTime": "00:04:14",
"target": "https://www.youtube.com/watch?v=L0OB0_bO5I0?t=254"
},
"image": {
"@type": "ImageObject",
"width": 1874,
"height": 970,
"url": "https://beccahughes.github.io/media/media-feeds/chrome79_current.png"
},
"name": "New in Chrome 79"
},
"seasonNumber": 1
},
"image": {
"@type": "ImageObject",
"width": 336,
"height": 188,
"url": "https://beccahughes.github.io/media/media-feeds/chromerel.webp"
}
},
{
"@context": "https://schema.org/",
"@type": "Movie",
"@id": "https://beccahughes.github.io/media/media-feeds/big-buck-bunny",
"datePublished": "2008-01-01",
"duration": "PT12M",
"isFamilyFriendly": "https://schema.org/False",
"name": "Big Buck Bunny",
"potentialAction": {
"@type": "WatchAction",
"target": "https://mounirlamouri.github.io/sandbox/media/dynamic-controls.html"
},
"image": {
"@type": "ImageObject",
"width": 1392,
"height": 749,
"url": "https://beccahughes.github.io/media/media-feeds/big_buck_bunny.jpg"
}
}
],
"provider": {
"@type": "Organization",
"name": "Chromium Developers",
"logo": [{
"@type": "ImageObject",
"width": 1113,
"height": 245,
"url": "https://beccahughes.github.io/media/media-feeds/chromium_logo_white.png",
"additionalProperty": {
"@type": "PropertyValue",
"name": "contentAttributes",
"value": ["forDarkBackground", "hasTitle", "transparentBackground"]
}
}, {
"@type": "ImageObject",
"width": 600,
"height": 315,
"url": "https://beccahughes.github.io/media/media-feeds/chromium_card.png",
"additionalProperty": {
"@type": "PropertyValue",
"name": "contentAttributes",
"value": ["forLightBackground", "hasTitle", "centered"]
}
}]
}
})END";
const char kFirstItemActionURL[] = "https://www.example.com/action";
const char kFirstItemPlayNextActionURL[] = "https://www.example.com/next";
} // namespace
class MediaFeedsServiceTest : public ChromeRenderViewHostTestHarness {
public:
MediaFeedsServiceTest()
: ChromeRenderViewHostTestHarness(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
void SetUp() override {
features_.InitWithFeatures(
{media::kMediaFeeds, media::kMediaFeedsSafeSearch,
media::kMediaFeedsBackgroundFetching},
{});
ChromeRenderViewHostTestHarness::SetUp();
stub_url_checker_ = std::make_unique<safe_search_api::StubURLChecker>();
test_clock_.SetNow(base::Time::Now());
GetMediaFeedsService()->SetClockForTesting(&test_clock_);
GetMediaFeedsService()->SetSafeSearchURLCheckerForTest(
stub_url_checker_->BuildURLChecker(kCacheSize));
GetMediaFeedsService()->test_url_loader_factory_for_fetcher_ =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&url_loader_factory_);
}
void AdvanceTime(base::TimeDelta time_delta) {
test_clock_.SetNow(test_clock_.Now() + time_delta);
task_environment()->FastForwardBy(time_delta);
}
base::Time Now() { return test_clock_.Now(); }
void WaitForDB() {
base::RunLoop run_loop;
GetMediaHistoryService()->PostTaskToDBForTest(run_loop.QuitClosure());
run_loop.Run();
}
void SimulateOnCheckURLDone(
const media_history::MediaHistoryKeyedService::SafeSearchID id,
const GURL& url,
safe_search_api::Classification classification,
bool uncertain) {
GetMediaFeedsService()->OnCheckURLDone(id, url, url, classification,
uncertain);
}
bool AddInflightSafeSearchCheck(
const media_history::MediaHistoryKeyedService::SafeSearchID id,
const std::set<GURL>& urls) {
return GetMediaFeedsService()->AddInflightSafeSearchCheck(id, urls);
}
media_history::MediaHistoryKeyedService::MediaFeedFetchResult
SuccessfulResultWithItems(
std::vector<media_feeds::mojom::MediaFeedItemPtr> items,
const int64_t feed_id) {
media_history::MediaHistoryKeyedService::MediaFeedFetchResult result;
result.feed_id = feed_id;
result.items = std::move(items);
result.status = media_feeds::mojom::FetchResult::kSuccess;
result.display_name = "test";
result.reset_token = media_history::test::GetResetTokenSync(
GetMediaHistoryService(), feed_id);
return result;
}
bool GetCurrentRequestHasBypassCacheFlag() {
return GetCurrentRequest().load_flags & net::LOAD_BYPASS_CACHE;
}
media_history::MediaHistoryKeyedService::PendingSafeSearchCheckList
GetPendingSafeSearchCheckMediaFeedItemsSync() {
base::RunLoop run_loop;
media_history::MediaHistoryKeyedService::PendingSafeSearchCheckList out;
GetMediaHistoryService()->GetPendingSafeSearchCheckMediaFeedItems(
base::BindLambdaForTesting([&](media_history::MediaHistoryKeyedService::
PendingSafeSearchCheckList rows) {
out = std::move(rows);
run_loop.Quit();
}));
run_loop.Run();
return out;
}
void DiscoverFeedAndPerformSafeSearchCheck(const GURL& feed_url) {
SetSafeSearchEnabled(true);
safe_search_checker()->SetUpValidResponse(/* is_porn= */ false);
base::RunLoop run_loop;
GetMediaFeedsService()->SetSafeSearchCompletionCallbackForTest(
run_loop.QuitClosure());
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Wait for the service and DB to finish.
run_loop.Run();
WaitForDB();
// Return to the default state.
safe_search_checker()->ClearResponses();
SetSafeSearchEnabled(false);
}
std::vector<media_feeds::mojom::MediaFeedItemPtr> GetItemsForMediaFeedSync(
const int64_t feed_id) {
base::RunLoop run_loop;
std::vector<media_feeds::mojom::MediaFeedItemPtr> out;
GetMediaHistoryService()->GetMediaFeedItems(
media_history::MediaHistoryKeyedService::GetMediaFeedItemsRequest::
CreateItemsForDebug(feed_id),
base::BindLambdaForTesting(
[&](std::vector<media_feeds::mojom::MediaFeedItemPtr> rows) {
out = std::move(rows);
run_loop.Quit();
}));
run_loop.Run();
return out;
}
std::vector<media_feeds::mojom::MediaFeedPtr> GetMediaFeedsSync(
const media_history::MediaHistoryKeyedService::GetMediaFeedsRequest&
request =
media_history::MediaHistoryKeyedService::GetMediaFeedsRequest()) {
base::RunLoop run_loop;
std::vector<media_feeds::mojom::MediaFeedPtr> out;
GetMediaHistoryService()->GetMediaFeeds(
request, base::BindLambdaForTesting(
[&](std::vector<media_feeds::mojom::MediaFeedPtr> rows) {
out = std::move(rows);
run_loop.Quit();
}));
run_loop.Run();
return out;
}
void SetSafeSearchEnabled(bool enabled) {
profile()->GetPrefs()->SetBoolean(prefs::kMediaFeedsSafeSearchEnabled,
enabled);
}
void SetBackgroundFetchingEnabled(bool enabled) {
profile()->GetPrefs()->SetBoolean(prefs::kMediaFeedsBackgroundFetching,
enabled);
}
bool RespondToPendingFeedFetch(const GURL& feed_url,
bool from_cache = false) {
return RespondToPendingFeedFetchWithData(feed_url, kTestData, from_cache);
}
bool RespondToPendingFeedFetchWithData(const GURL& feed_url,
const std::string& data,
bool from_cache = false) {
auto response_head =
::network::CreateURLResponseHead(net::HttpStatusCode::HTTP_OK);
response_head->was_fetched_via_cache = from_cache;
return url_loader_factory_.SimulateResponseForPendingRequest(
feed_url, network::URLLoaderCompletionStatus(net::OK),
std::move(response_head), data);
}
bool RespondToPendingFeedFetchWithStatus(const GURL& feed_url,
net::HttpStatusCode code) {
bool rv = url_loader_factory_.SimulateResponseForPendingRequest(
feed_url, network::URLLoaderCompletionStatus(net::OK),
network::CreateURLResponseHead(code), std::string());
return rv;
}
void SetAutomaticSelectionEnabled() {
profile()->GetPrefs()->SetBoolean(prefs::kMediaFeedsAutoSelectEnabled,
true);
}
safe_search_api::StubURLChecker* safe_search_checker() {
return stub_url_checker_.get();
}
MediaFeedsService* GetMediaFeedsService() {
return MediaFeedsServiceFactory::GetInstance()->GetForProfile(profile());
}
media_history::MediaHistoryKeyedService* GetMediaHistoryService() {
return media_history::MediaHistoryKeyedService::Get(profile());
}
static media_feeds::mojom::MediaFeedItemPtr GetSingleExpectedItem(
int id_start = 0) {
auto item = media_feeds::mojom::MediaFeedItem::New();
item->id = ++id_start;
item->name = base::ASCIIToUTF16("The Movie");
item->type = media_feeds::mojom::MediaFeedItemType::kMovie;
item->date_published = base::Time::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromMinutes(10));
item->action_status =
media_feeds::mojom::MediaFeedItemActionStatus::kPotential;
item->action = media_feeds::mojom::Action::New();
item->action->url = GURL(kFirstItemActionURL);
item->play_next_candidate = media_feeds::mojom::PlayNextCandidate::New();
item->play_next_candidate->action = media_feeds::mojom::Action::New();
item->play_next_candidate->action->url = GURL(kFirstItemPlayNextActionURL);
return item;
}
static std::vector<media_feeds::mojom::MediaFeedItemPtr> GetExpectedItems(
int id_start = 0) {
std::vector<media_feeds::mojom::MediaFeedItemPtr> items;
items.push_back(GetSingleExpectedItem(id_start));
id_start++;
{
auto item = media_feeds::mojom::MediaFeedItem::New();
item->id = ++id_start;
item->type = media_feeds::mojom::MediaFeedItemType::kTVSeries;
item->name = base::ASCIIToUTF16("The TV Series");
item->action_status =
media_feeds::mojom::MediaFeedItemActionStatus::kActive;
item->action = media_feeds::mojom::Action::New();
item->action->url = GURL("https://www.example.com/action2");
item->author = media_feeds::mojom::Author::New();
item->author->name = "Media Site";
items.push_back(std::move(item));
}
{
auto item = media_feeds::mojom::MediaFeedItem::New();
item->id = ++id_start;
item->type = media_feeds::mojom::MediaFeedItemType::kTVSeries;
item->name = base::ASCIIToUTF16("The Live TV Series");
item->action_status =
media_feeds::mojom::MediaFeedItemActionStatus::kPotential;
item->action = media_feeds::mojom::Action::New();
item->action->url = GURL("https://www.example.com/action3");
item->live = media_feeds::mojom::LiveDetails::New();
items.push_back(std::move(item));
}
return items;
}
void CreateCookies(const std::vector<GURL>& urls,
bool domain_cookies = false,
bool expired = false) {
const base::Time creation_time = base::Time::Now();
base::RunLoop run_loop;
int tasks = urls.size();
for (auto& url : urls) {
std::vector<std::string> cookie_line;
cookie_line.push_back("A=1");
if (url.SchemeIsCryptographic())
cookie_line.push_back("Secure");
if (domain_cookies)
cookie_line.push_back("Domain=" + url.host());
if (expired)
cookie_line.push_back("Expires=Wed, 31 Dec 1969 07:28:00 GMT");
std::unique_ptr<net::CanonicalCookie> cookie =
net::CanonicalCookie::Create(url, base::JoinString(cookie_line, ";"),
creation_time, creation_time);
EXPECT_EQ(domain_cookies, cookie->IsDomainCookie());
EXPECT_EQ(!domain_cookies, cookie->IsHostCookie());
GetCookieManager()->SetCanonicalCookie(
*cookie, url, net::CookieOptions::MakeAllInclusive(),
base::BindLambdaForTesting(
[&](net::CookieAccessResult access_result) {
if (--tasks == 0)
run_loop.Quit();
}));
}
run_loop.Run();
}
uint32_t DeleteCookies(network::mojom::CookieDeletionFilterPtr filter) {
base::RunLoop run_loop;
uint32_t result_out = 0u;
GetCookieManager()->DeleteCookies(
std::move(filter),
base::BindLambdaForTesting([&run_loop, &result_out](uint32_t result) {
result_out = result;
run_loop.Quit();
}));
run_loop.Run();
return result_out;
}
network::mojom::CookieManager* GetCookieManager() {
auto* partition =
content::BrowserContext::GetDefaultStoragePartition(profile());
return partition->GetCookieManagerForBrowserProcess();
}
bool IsCookieObserverEnabled() const { return true; }
private:
const ::network::ResourceRequest& GetCurrentRequest() {
return url_loader_factory_.pending_requests()->front().request;
}
base::test::ScopedFeatureList features_;
network::TestURLLoaderFactory url_loader_factory_;
std::unique_ptr<safe_search_api::StubURLChecker> stub_url_checker_;
data_decoder::test::InProcessDataDecoder data_decoder_;
base::SimpleTestClock test_clock_;
};
TEST_F(MediaFeedsServiceTest, GetForProfile) {
EXPECT_NE(nullptr, MediaFeedsServiceFactory::GetForProfile(profile()));
Profile* otr_profile = profile()->GetPrimaryOTRProfile();
EXPECT_EQ(nullptr, MediaFeedsServiceFactory::GetForProfile(otr_profile));
}
TEST_F(MediaFeedsServiceTest, FetchFeed_Success) {
base::HistogramTester histogram_tester;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Fetch the Media Feed.
base::RunLoop run_loop;
GetMediaFeedsService()->FetchMediaFeed(
1, base::BindLambdaForTesting(
[&](const std::string& ignored) { run_loop.Quit(); }));
WaitForDB();
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
run_loop.Run();
auto feeds = GetMediaFeedsSync();
EXPECT_EQ(1u, feeds.size());
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit);
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
histogram_tester.ExpectUniqueSample(
MediaFeedsFetcher::kFetchSizeKbHistogramName, 15, 1);
}
TEST_F(MediaFeedsServiceTest, FetchFeed_SuccessFromCache) {
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Fetch the Media Feed.
base::RunLoop run_loop;
GetMediaFeedsService()->FetchMediaFeed(
1, base::BindLambdaForTesting(
[&](const std::string& ignored) { run_loop.Quit(); }));
WaitForDB();
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url, true));
run_loop.Run();
auto feeds = GetMediaFeedsSync();
EXPECT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
// This should not be set yet because the only fetch for this feed was from
// the cache.
EXPECT_FALSE(feeds[0]->last_fetch_time_not_cache_hit);
}
TEST_F(MediaFeedsServiceTest, FetchFeed_BackendError) {
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Fetch the Media Feed.
base::RunLoop run_loop;
GetMediaFeedsService()->FetchMediaFeed(
1, base::BindLambdaForTesting(
[&](const std::string& ignored) { run_loop.Quit(); }));
WaitForDB();
ASSERT_TRUE(RespondToPendingFeedFetchWithStatus(
feed_url, net::HTTP_INTERNAL_SERVER_ERROR));
run_loop.Run();
auto feeds = GetMediaFeedsSync();
EXPECT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::FetchResult::kFailedBackendError,
feeds[0]->last_fetch_result);
}
TEST_F(MediaFeedsServiceTest, FetchFeed_NotFoundError) {
const GURL feed_url("https://www.google.com/feed");
SetSafeSearchEnabled(true);
safe_search_checker()->SetUpValidResponse(/* is_porn= */ false);
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Fetch the Media Feed.
base::RunLoop run_loop;
GetMediaFeedsService()->FetchMediaFeed(
1, base::BindLambdaForTesting(
[&](const std::string& ignored) { run_loop.Quit(); }));
WaitForDB();
ASSERT_TRUE(RespondToPendingFeedFetchWithStatus(feed_url, net::HTTP_OK));
run_loop.Run();
auto feeds = GetMediaFeedsSync();
EXPECT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::FetchResult::kFailedNetworkError,
feeds[0]->last_fetch_result);
}
TEST_F(MediaFeedsServiceTest, SafeSearch_AllSafe) {
DiscoverFeedAndPerformSafeSearchCheck(GURL("https://www.google.com/feed"));
base::HistogramTester histogram_tester;
SetSafeSearchEnabled(true);
safe_search_checker()->SetUpValidResponse(/* is_porn= */ false);
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
base::RunLoop run_loop;
GetMediaFeedsService()->SetSafeSearchCompletionCallbackForTest(
run_loop.QuitClosure());
{
// Get the pending items and check them against Safe Search.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(3u, pending_items.size());
GetMediaFeedsService()->CheckItemsAgainstSafeSearch(
std::move(pending_items));
}
// Wait for the service and DB to finish.
run_loop.Run();
WaitForDB();
{
// The pending items should be empty.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_TRUE(pending_items.empty());
}
// Check the items were updated.
auto items = GetItemsForMediaFeedSync(1);
EXPECT_EQ(3u, items.size());
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kSafe,
items[0]->safe_search_result);
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kSafe,
items[1]->safe_search_result);
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kSafe,
items[2]->safe_search_result);
histogram_tester.ExpectUniqueSample(
MediaFeedsService::kSafeSearchResultHistogramName,
media_feeds::mojom::SafeSearchResult::kSafe, 3);
}
TEST_F(MediaFeedsServiceTest, SafeSearch_AllUnsafe) {
DiscoverFeedAndPerformSafeSearchCheck(GURL("https://www.google.com/feed"));
base::HistogramTester histogram_tester;
SetSafeSearchEnabled(true);
safe_search_checker()->SetUpValidResponse(/* is_porn= */ true);
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
base::RunLoop run_loop;
GetMediaFeedsService()->SetSafeSearchCompletionCallbackForTest(
run_loop.QuitClosure());
{
// Get the pending items and check them against Safe Search.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(3u, pending_items.size());
GetMediaFeedsService()->CheckItemsAgainstSafeSearch(
std::move(pending_items));
}
// Wait for the service and DB to finish.
run_loop.Run();
WaitForDB();
{
// The pending items should be empty.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_TRUE(pending_items.empty());
}
// Check the items were updated.
auto items = GetItemsForMediaFeedSync(1);
EXPECT_EQ(3u, items.size());
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnsafe,
items[0]->safe_search_result);
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnsafe,
items[1]->safe_search_result);
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnsafe,
items[2]->safe_search_result);
histogram_tester.ExpectUniqueSample(
MediaFeedsService::kSafeSearchResultHistogramName,
media_feeds::mojom::SafeSearchResult::kUnsafe, 3);
}
TEST_F(MediaFeedsServiceTest, SafeSearch_Failed_Request) {
DiscoverFeedAndPerformSafeSearchCheck(GURL("https://www.google.com/feed"));
base::HistogramTester histogram_tester;
SetSafeSearchEnabled(true);
safe_search_checker()->SetUpFailedResponse();
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
base::RunLoop run_loop;
GetMediaFeedsService()->SetSafeSearchCompletionCallbackForTest(
run_loop.QuitClosure());
{
// Get the pending items and check them against Safe Search.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(3u, pending_items.size());
GetMediaFeedsService()->CheckItemsAgainstSafeSearch(
std::move(pending_items));
}
// Wait for the service and DB to finish.
run_loop.Run();
WaitForDB();
{
// The pending items should still be 3.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(3u, pending_items.size());
}
// Check the items were updated.
auto items = GetItemsForMediaFeedSync(1);
EXPECT_EQ(3u, items.size());
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnknown,
items[0]->safe_search_result);
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnknown,
items[1]->safe_search_result);
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnknown,
items[2]->safe_search_result);
histogram_tester.ExpectUniqueSample(
MediaFeedsService::kSafeSearchResultHistogramName,
media_feeds::mojom::SafeSearchResult::kUnknown, 3);
}
TEST_F(MediaFeedsServiceTest, SafeSearch_Failed_Pref) {
DiscoverFeedAndPerformSafeSearchCheck(GURL("https://www.google.com/feed"));
base::HistogramTester histogram_tester;
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
base::RunLoop run_loop;
GetMediaFeedsService()->SetSafeSearchCompletionCallbackForTest(
run_loop.QuitClosure());
{
// Get the pending items and check them against Safe Search.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(3u, pending_items.size());
GetMediaFeedsService()->CheckItemsAgainstSafeSearch(
std::move(pending_items));
}
// Wait for the service and DB to finish.
run_loop.Run();
WaitForDB();
{
// The pending items should still be 3.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(3u, pending_items.size());
}
// Check the items were updated.
auto items = GetItemsForMediaFeedSync(1);
EXPECT_EQ(3u, items.size());
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnknown,
items[0]->safe_search_result);
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnknown,
items[1]->safe_search_result);
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnknown,
items[2]->safe_search_result);
histogram_tester.ExpectTotalCount(
MediaFeedsService::kSafeSearchResultHistogramName, 0);
}
TEST_F(MediaFeedsServiceTest, SafeSearch_CheckTwice_Inflight) {
DiscoverFeedAndPerformSafeSearchCheck(GURL("https://www.google.com/feed"));
SetSafeSearchEnabled(true);
safe_search_checker()->SetUpValidResponse(/* is_porn= */ false);
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
base::RunLoop run_loop;
GetMediaFeedsService()->SetSafeSearchCompletionCallbackForTest(
run_loop.QuitClosure());
{
// Get the pending items and check them against Safe Search.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(3u, pending_items.size());
GetMediaFeedsService()->CheckItemsAgainstSafeSearch(
std::move(pending_items));
}
{
// This checks we ignore the duplicate items for inflight checks.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(3u, pending_items.size());
GetMediaFeedsService()->CheckItemsAgainstSafeSearch(
std::move(pending_items));
}
// Wait for the service and DB to finish.
run_loop.Run();
WaitForDB();
{
// The pending items should be empty.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_TRUE(pending_items.empty());
}
}
TEST_F(MediaFeedsServiceTest, SafeSearch_CheckTwice_Committed) {
DiscoverFeedAndPerformSafeSearchCheck(GURL("https://www.google.com/feed"));
SetSafeSearchEnabled(true);
safe_search_checker()->SetUpValidResponse(/* is_porn= */ false);
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
auto pending_items_a = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(3u, pending_items_a.size());
auto pending_items_b = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(3u, pending_items_b.size());
{
base::RunLoop run_loop;
GetMediaFeedsService()->SetSafeSearchCompletionCallbackForTest(
run_loop.QuitClosure());
GetMediaFeedsService()->CheckItemsAgainstSafeSearch(
std::move(pending_items_a));
// Wait for the service and DB to finish.
run_loop.Run();
WaitForDB();
}
{
base::RunLoop run_loop;
GetMediaFeedsService()->SetSafeSearchCompletionCallbackForTest(
run_loop.QuitClosure());
GetMediaFeedsService()->CheckItemsAgainstSafeSearch(
std::move(pending_items_b));
// Wait for the service and DB to finish.
run_loop.Run();
WaitForDB();
}
{
// The pending items should be empty.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_TRUE(pending_items.empty());
}
}
TEST_F(MediaFeedsServiceTest, SafeSearch_Mixed_SafeUnsafe) {
DiscoverFeedAndPerformSafeSearchCheck(GURL("https://www.google.com/feed"));
SetSafeSearchEnabled(true);
base::HistogramTester histogram_tester;
// Store some media feed items.
std::vector<media_feeds::mojom::MediaFeedItemPtr> items;
items.push_back(GetSingleExpectedItem());
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(std::move(items), 1), base::DoNothing());
WaitForDB();
{
// Get the pending items and check them against Safe Search.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(1u, pending_items.size());
EXPECT_TRUE(AddInflightSafeSearchCheck(pending_items[0]->id,
pending_items[0]->urls));
}
SimulateOnCheckURLDone(std::make_pair(SafeSearchCheckedType::kFeedItem, 1),
GURL(kFirstItemActionURL),
safe_search_api::Classification::SAFE,
/*uncertain=*/false);
SimulateOnCheckURLDone(std::make_pair(SafeSearchCheckedType::kFeedItem, 1),
GURL(kFirstItemPlayNextActionURL),
safe_search_api::Classification::UNSAFE,
/*uncertain=*/false);
WaitForDB();
{
// Check the result of the item we stored.
auto items = GetItemsForMediaFeedSync(1);
EXPECT_EQ(1u, items.size());
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnsafe,
items[0]->safe_search_result);
}
histogram_tester.ExpectUniqueSample(
MediaFeedsService::kSafeSearchResultHistogramName,
media_feeds::mojom::SafeSearchResult::kUnsafe, 1);
}
TEST_F(MediaFeedsServiceTest, SafeSearch_Mixed_SafeUncertain) {
DiscoverFeedAndPerformSafeSearchCheck(GURL("https://www.google.com/feed"));
SetSafeSearchEnabled(true);
base::HistogramTester histogram_tester;
// Store some media feed items.
std::vector<media_feeds::mojom::MediaFeedItemPtr> items;
items.push_back(GetSingleExpectedItem());
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(std::move(items), 1), base::DoNothing());
WaitForDB();
{
// Get the pending items and check them against Safe Search.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(1u, pending_items.size());
EXPECT_TRUE(AddInflightSafeSearchCheck(pending_items[0]->id,
pending_items[0]->urls));
}
SimulateOnCheckURLDone(std::make_pair(SafeSearchCheckedType::kFeedItem, 1),
GURL(kFirstItemActionURL),
safe_search_api::Classification::SAFE,
/*uncertain=*/false);
SimulateOnCheckURLDone(std::make_pair(SafeSearchCheckedType::kFeedItem, 1),
GURL(kFirstItemPlayNextActionURL),
safe_search_api::Classification::SAFE,
/*uncertain=*/true);
WaitForDB();
{
// Check the result of the item we stored.
auto items = GetItemsForMediaFeedSync(1);
EXPECT_EQ(1u, items.size());
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnknown,
items[0]->safe_search_result);
}
histogram_tester.ExpectUniqueSample(
MediaFeedsService::kSafeSearchResultHistogramName,
media_feeds::mojom::SafeSearchResult::kUnknown, 1);
}
TEST_F(MediaFeedsServiceTest, SafeSearch_Mixed_UnsafeUncertain) {
DiscoverFeedAndPerformSafeSearchCheck(GURL("https://www.google.com/feed"));
SetSafeSearchEnabled(true);
base::HistogramTester histogram_tester;
// Store some media feed items.
std::vector<media_feeds::mojom::MediaFeedItemPtr> items;
items.push_back(GetSingleExpectedItem());
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(std::move(items), 1), base::DoNothing());
WaitForDB();
{
// Get the pending items and check them against Safe Search.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(1u, pending_items.size());
EXPECT_TRUE(AddInflightSafeSearchCheck(pending_items[0]->id,
pending_items[0]->urls));
}
SimulateOnCheckURLDone(std::make_pair(SafeSearchCheckedType::kFeedItem, 1),
GURL(kFirstItemActionURL),
safe_search_api::Classification::UNSAFE,
/*uncertain=*/false);
SimulateOnCheckURLDone(std::make_pair(SafeSearchCheckedType::kFeedItem, 1),
GURL(kFirstItemPlayNextActionURL),
safe_search_api::Classification::SAFE,
/*uncertain=*/true);
WaitForDB();
{
// Check the result of the item we stored.
auto items = GetItemsForMediaFeedSync(1);
EXPECT_EQ(1u, items.size());
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnsafe,
items[0]->safe_search_result);
}
histogram_tester.ExpectUniqueSample(
MediaFeedsService::kSafeSearchResultHistogramName,
media_feeds::mojom::SafeSearchResult::kUnsafe, 1);
}
TEST_F(MediaFeedsServiceTest, SafeSearch_Failed_Feature) {
DiscoverFeedAndPerformSafeSearchCheck(GURL("https://www.google.com/feed"));
SetSafeSearchEnabled(true);
base::test::ScopedFeatureList features;
features.InitAndDisableFeature(media::kMediaFeedsSafeSearch);
base::HistogramTester histogram_tester;
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
base::RunLoop run_loop;
GetMediaFeedsService()->SetSafeSearchCompletionCallbackForTest(
run_loop.QuitClosure());
{
// Get the pending items and check them against Safe Search.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(3u, pending_items.size());
GetMediaFeedsService()->CheckItemsAgainstSafeSearch(
std::move(pending_items));
}
// Wait for the service and DB to finish.
run_loop.Run();
WaitForDB();
{
// The pending items should still be 3.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(3u, pending_items.size());
}
// Check the items were updated.
auto items = GetItemsForMediaFeedSync(1);
EXPECT_EQ(3u, items.size());
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnknown,
items[0]->safe_search_result);
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnknown,
items[1]->safe_search_result);
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnknown,
items[2]->safe_search_result);
histogram_tester.ExpectTotalCount(
MediaFeedsService::kSafeSearchResultHistogramName, 0);
}
TEST_F(MediaFeedsServiceTest, FetcherShouldTriggerSafeSearch) {
const GURL feed_url("https://www.google.com/feed");
DiscoverFeedAndPerformSafeSearchCheck(feed_url);
SetSafeSearchEnabled(true);
safe_search_checker()->SetUpValidResponse(/* is_porn= */ false);
base::RunLoop run_loop;
GetMediaFeedsService()->SetSafeSearchCompletionCallbackForTest(
run_loop.QuitClosure());
{
// Fetch the Media Feed.
base::RunLoop run_loop;
GetMediaFeedsService()->FetchMediaFeed(
1, base::BindLambdaForTesting(
[&](const std::string& ignored) { run_loop.Quit(); }));
WaitForDB();
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
run_loop.Run();
}
// Wait for the items to be checked against safe search
run_loop.Run();
WaitForDB();
{
// The pending items should be empty.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_TRUE(pending_items.empty());
}
// Check the items were updated.
auto items = GetItemsForMediaFeedSync(1);
EXPECT_EQ(3u, items.size());
for (auto& item : items) {
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kSafe,
item->safe_search_result);
}
}
TEST_F(MediaFeedsServiceTest, FetcherShouldDeleteFeedIfGone) {
const GURL feed_url("https://www.google.com/feed");
DiscoverFeedAndPerformSafeSearchCheck(feed_url);
safe_search_checker()->SetUpValidResponse(/* is_porn= */ false);
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
base::RunLoop run_loop;
GetMediaFeedsService()->SetSafeSearchCompletionCallbackForTest(
run_loop.QuitClosure());
{
// Check there are pending items.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(3u, pending_items.size());
}
// Enable the safe search pref. This should trigger a refetch.
SetSafeSearchEnabled(true);
// Wait for the service and DB to finish.
run_loop.Run();
WaitForDB();
{
// The pending items should be empty.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_TRUE(pending_items.empty());
}
{
// Check the items were updated.
auto items = GetItemsForMediaFeedSync(1);
EXPECT_EQ(3u, items.size());
for (auto& item : items) {
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kSafe,
item->safe_search_result);
}
}
// Store some new media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
{
// Check there are pending items.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync();
EXPECT_EQ(3u, pending_items.size());
}
}
TEST_F(MediaFeedsServiceTest, FetcherShouldSupportMultipleFetchesForSameFeed) {
const GURL feed_url("https://www.google.com/feed");
SetSafeSearchEnabled(true);
safe_search_checker()->SetUpValidResponse(/* is_porn= */ false);
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Fetch the same feed twice.
base::RunLoop run_loop;
GetMediaFeedsService()->FetchMediaFeed(
1, base::BindLambdaForTesting(
[&](const std::string& ignored) { run_loop.Quit(); }));
WaitForDB();
base::RunLoop run_loop_alt;
GetMediaFeedsService()->FetchMediaFeed(
1, base::BindLambdaForTesting(
[&](const std::string& ignored) { run_loop_alt.Quit(); }));
WaitForDB();
// Respond and make sure both run loop quit closures were called.
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
run_loop.Run();
run_loop_alt.Run();
}
TEST_F(MediaFeedsServiceTest, FetcherShouldHandleReset) {
const GURL feed_url("https://www.google.com/feed");
SetSafeSearchEnabled(true);
safe_search_checker()->SetUpValidResponse(/* is_porn= */ false);
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
{
// Check the feed and items are stored correctly.
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
auto items = GetItemsForMediaFeedSync(1);
EXPECT_EQ(GetExpectedItems(), items);
}
// Start fetching the feed but do not resolve the request.
base::RunLoop run_loop;
GetMediaFeedsService()->FetchMediaFeed(
1, base::BindLambdaForTesting(
[&](const std::string& ignored) { run_loop.Quit(); }));
WaitForDB();
// The last request was successful so we can hit the cache.
EXPECT_FALSE(GetCurrentRequestHasBypassCacheFlag());
// Reset the feed.
GetMediaFeedsService()->ResetMediaFeed(
url::Origin::Create(feed_url), media_feeds::mojom::ResetReason::kVisit);
WaitForDB();
{
// Check the feed was reset.
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kVisit, feeds[0]->reset_reason);
EXPECT_EQ(media_feeds::mojom::FetchResult::kNone,
feeds[0]->last_fetch_result);
auto items = GetItemsForMediaFeedSync(1);
EXPECT_TRUE(items.empty());
}
// Respond to the pending fetch.
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
run_loop.Run();
WaitForDB();
{
// The feed should have still been reset since the fetch was started with
// outdated information.
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kVisit, feeds[0]->reset_reason);
EXPECT_EQ(media_feeds::mojom::FetchResult::kFailedDueToResetWhileInflight,
feeds[0]->last_fetch_result);
auto items = GetItemsForMediaFeedSync(1);
EXPECT_TRUE(items.empty());
}
// Start fetching the feed but do not resolve the request.
base::RunLoop run_loop_alt;
GetMediaFeedsService()->FetchMediaFeed(
1, base::BindLambdaForTesting(
[&](const std::string& ignored) { run_loop_alt.Quit(); }));
WaitForDB();
// The last request failed so we should not hit the cache.
EXPECT_TRUE(GetCurrentRequestHasBypassCacheFlag());
// Respond to the pending fetch.
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
run_loop_alt.Run();
WaitForDB();
{
// The feed should have been successfully stored.
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
auto items = GetItemsForMediaFeedSync(1);
EXPECT_FALSE(items.empty());
}
}
TEST_F(MediaFeedsServiceTest, ResetOnCookieChange_ExplicitDeletion_All) {
if (!GetMediaFeedsService()->HasCookieObserverForTest())
return;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
base::RunLoop run_loop;
GetMediaFeedsService()->SetCookieChangeCallbackForTest(
run_loop.QuitClosure());
{
// Store some cookies on the feed URL and another URL.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(feed_url);
cookie_urls.push_back(GURL("https://www.example.com"));
CreateCookies(cookie_urls);
}
EXPECT_EQ(2u, DeleteCookies(network::mojom::CookieDeletionFilter::New()));
run_loop.Run();
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kCookies,
feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest,
ResetOnCookieChange_ExplicitDeletion_SingleHostMatch) {
if (!GetMediaFeedsService()->HasCookieObserverForTest())
return;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
base::RunLoop run_loop;
GetMediaFeedsService()->SetCookieChangeCallbackForTest(
run_loop.QuitClosure());
{
// Store some cookies on the feed URL and another URL.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(feed_url);
cookie_urls.push_back(GURL("https://www.example.com"));
CreateCookies(cookie_urls);
}
auto filter = network::mojom::CookieDeletionFilter::New();
filter->url = feed_url;
EXPECT_EQ(1u, DeleteCookies(std::move(filter)));
run_loop.Run();
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kCookies,
feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest,
ResetOnCookieChange_ExplicitDeletion_SingleHostMatch_Insecure) {
if (!GetMediaFeedsService()->HasCookieObserverForTest())
return;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
base::RunLoop run_loop;
GetMediaFeedsService()->SetCookieChangeCallbackForTest(
run_loop.QuitClosure());
{
// Store some cookies on the feed URL and another URL.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(GURL("http://www.google.com"));
cookie_urls.push_back(GURL("https://www.example.com"));
CreateCookies(cookie_urls);
}
auto filter = network::mojom::CookieDeletionFilter::New();
filter->url = feed_url;
EXPECT_EQ(1u, DeleteCookies(std::move(filter)));
run_loop.Run();
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kCookies,
feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest, ResetOnCookieChange_Expired) {
if (!GetMediaFeedsService()->HasCookieObserverForTest())
return;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
base::RunLoop run_loop;
GetMediaFeedsService()->SetCookieChangeCallbackForTest(
run_loop.QuitClosure());
{
// Store some cookies on the feed URL and another URL.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(feed_url);
cookie_urls.push_back(GURL("https://www.example.com"));
CreateCookies(cookie_urls);
CreateCookies(cookie_urls, false, true);
}
run_loop.Run();
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kCookies,
feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest,
ResetOnCookieChange_ExplicitDeletion_SingleHostNoMatch) {
if (!GetMediaFeedsService()->HasCookieObserverForTest())
return;
const GURL feed_url("https://www.google.com/feed");
const GURL alt_url("https://www.example.com");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
{
// Store some cookies on the feed URL and another URL.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(feed_url);
cookie_urls.push_back(alt_url);
CreateCookies(cookie_urls);
}
auto filter = network::mojom::CookieDeletionFilter::New();
filter->url = alt_url;
EXPECT_EQ(1u, DeleteCookies(std::move(filter)));
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest, ResetOnCookieChange_Overwrite) {
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
{
// Store some cookies on the feed URL and another URL.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(feed_url);
cookie_urls.push_back(GURL("https://www.example.com"));
CreateCookies(cookie_urls);
CreateCookies(cookie_urls);
WaitForDB();
}
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest,
ResetOnCookieChange_ExplicitDeletion_DomainMatch) {
if (!GetMediaFeedsService()->HasCookieObserverForTest())
return;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
base::RunLoop run_loop;
GetMediaFeedsService()->SetCookieChangeCallbackForTest(
run_loop.QuitClosure());
{
// Store some domain cookies.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(GURL("https://google.com"));
CreateCookies(cookie_urls, true);
}
EXPECT_EQ(1u, DeleteCookies(network::mojom::CookieDeletionFilter::New()));
run_loop.Run();
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kCookies,
feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest,
ResetOnCookieChange_ExplicitDeletion_DomainMatch_FullDomain) {
if (!GetMediaFeedsService()->HasCookieObserverForTest())
return;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
base::RunLoop run_loop;
GetMediaFeedsService()->SetCookieChangeCallbackForTest(
run_loop.QuitClosure());
{
// Store some domain cookies.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(GURL("https://www.google.com"));
CreateCookies(cookie_urls, true);
}
EXPECT_EQ(1u, DeleteCookies(network::mojom::CookieDeletionFilter::New()));
run_loop.Run();
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kCookies,
feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest,
ResetOnCookieChange_ExplicitDeletion_DomainMatch_Insecure) {
if (!GetMediaFeedsService()->HasCookieObserverForTest())
return;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
base::RunLoop run_loop;
GetMediaFeedsService()->SetCookieChangeCallbackForTest(
run_loop.QuitClosure());
{
// Store some domain cookies.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(GURL("http://google.com"));
CreateCookies(cookie_urls, true);
}
EXPECT_EQ(1u, DeleteCookies(network::mojom::CookieDeletionFilter::New()));
run_loop.Run();
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kCookies,
feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest,
ResetOnCookieChange_ExplicitDeletion_DomainMatch_NoMatch) {
if (!GetMediaFeedsService()->HasCookieObserverForTest())
return;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
base::RunLoop run_loop;
GetMediaFeedsService()->SetCookieChangeCallbackForTest(
run_loop.QuitClosure());
{
// Store some domain cookies.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(GURL("https://example.com"));
CreateCookies(cookie_urls, true);
}
EXPECT_EQ(1u, DeleteCookies(network::mojom::CookieDeletionFilter::New()));
run_loop.Run();
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest,
ResetOnCookieChange_ExplicitDeletion_WithCookieFilter_Match) {
if (!GetMediaFeedsService()->HasCookieObserverForTest())
return;
const GURL feed_url("https://www.google.com/feed");
{
// Store some cookies on the feed URL and another URL.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(feed_url);
cookie_urls.push_back(GURL("https://www.example.com"));
CreateCookies(cookie_urls);
}
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
auto result = SuccessfulResultWithItems(GetExpectedItems(), 1);
result.cookie_name_filter = "A";
GetMediaHistoryService()->StoreMediaFeedFetchResult(std::move(result),
base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
base::RunLoop run_loop;
GetMediaFeedsService()->SetCookieChangeCallbackForTest(
run_loop.QuitClosure());
EXPECT_EQ(2u, DeleteCookies(network::mojom::CookieDeletionFilter::New()));
run_loop.Run();
WaitForDB();
{
// The feed should have been reset because we have a cookie filter that
// matches.
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kCookies,
feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest,
ResetOnCookieChange_ExplicitDeletion_WithCookieFilter_NoMatch) {
if (!GetMediaFeedsService()->HasCookieObserverForTest())
return;
const GURL feed_url("https://www.google.com/feed");
{
// Store some cookies on the feed URL and another URL.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(feed_url);
cookie_urls.push_back(GURL("https://www.example.com"));
CreateCookies(cookie_urls);
}
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
auto result = SuccessfulResultWithItems(GetExpectedItems(), 1);
result.cookie_name_filter = "B";
GetMediaHistoryService()->StoreMediaFeedFetchResult(std::move(result),
base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
base::RunLoop run_loop;
GetMediaFeedsService()->SetCookieChangeCallbackForTest(
run_loop.QuitClosure());
EXPECT_EQ(2u, DeleteCookies(network::mojom::CookieDeletionFilter::New()));
run_loop.Run();
WaitForDB();
{
// The feed should not have been reset because the cookie filter does not
// match.
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest,
ResetOnCookieChange_Creation_WithCookieFilter_Match) {
if (!GetMediaFeedsService()->HasCookieObserverForTest())
return;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
auto result = SuccessfulResultWithItems(GetExpectedItems(), 1);
result.cookie_name_filter = "A";
GetMediaHistoryService()->StoreMediaFeedFetchResult(std::move(result),
base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
base::RunLoop run_loop;
GetMediaFeedsService()->SetCookieChangeCallbackForTest(
run_loop.QuitClosure());
{
// Store some cookies on the feed URL.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(feed_url);
CreateCookies(cookie_urls);
}
run_loop.Run();
WaitForDB();
{
// The feed should have been reset because we have a cookie filter that
// matches.
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kCookies,
feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest,
ResetOnCookieChange_Creation_WithCookieFilter_NoMatch) {
if (!GetMediaFeedsService()->HasCookieObserverForTest())
return;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
auto result = SuccessfulResultWithItems(GetExpectedItems(), 1);
result.cookie_name_filter = "B";
GetMediaHistoryService()->StoreMediaFeedFetchResult(std::move(result),
base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
base::RunLoop run_loop;
GetMediaFeedsService()->SetCookieChangeCallbackForTest(
run_loop.QuitClosure());
{
// Store some cookies on the feed URL.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(feed_url);
CreateCookies(cookie_urls);
}
run_loop.Run();
WaitForDB();
{
// The feed should not have been reset because the cookie filter does not
// match.
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest,
ResetOnCookieChange_Creation_WithoutCookieFilter) {
if (!GetMediaFeedsService()->HasCookieObserverForTest())
return;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Store some media feed items.
GetMediaHistoryService()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(GetExpectedItems(), 1), base::DoNothing());
WaitForDB();
{
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
base::RunLoop run_loop;
GetMediaFeedsService()->SetCookieChangeCallbackForTest(
run_loop.QuitClosure());
{
// Store some cookies on the feed URL.
std::vector<GURL> cookie_urls;
cookie_urls.push_back(feed_url);
CreateCookies(cookie_urls);
}
run_loop.Run();
WaitForDB();
{
// The feed should not have been reset because the cookie filter was not
// present and therefore we only reset on deletion/expiration.
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
}
}
TEST_F(MediaFeedsServiceTest, DiscoverFeed_SafeSearch_Enabled) {
const GURL feed_url("https://www.google.com/feed");
SetSafeSearchEnabled(true);
safe_search_checker()->SetUpValidResponse(/* is_porn= */ false);
base::RunLoop run_loop;
GetMediaFeedsService()->SetSafeSearchCompletionCallbackForTest(
run_loop.QuitClosure());
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Wait for the service and DB to finish.
run_loop.Run();
WaitForDB();
// The feed should have been updated to be safe.
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kSafe,
feeds[0]->safe_search_result);
}
TEST_F(MediaFeedsServiceTest, DiscoverFeed_SafeSearch_Disabled) {
const GURL feed_url("https://www.google.com/feed");
SetSafeSearchEnabled(false);
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnknown,
feeds[0]->safe_search_result);
}
// Runs the Media Feeds tests from the spec. The file names start with success
// if the feed is valid and bad if they are not.
class MediaFeedsSpecTest : public MediaFeedsServiceTest,
public testing::WithParamInterface<std::string> {};
INSTANTIATE_TEST_SUITE_P(
All,
MediaFeedsSpecTest,
testing::Values("bad-member.json",
"bad-missing-logo-content-attributes.json",
"bad-provider-missing-name.json",
"success-allow-http-schema.org.json",
"success-empty.json",
"success-ignore-bad-content-attribute.json",
"success-member-opt-image-and-email.json",
"bad-image-as-url.json",
"bad-missing-logo.json",
"success-associated-origin-backwards-compat.json",
"success-full-feed.json",
"success-ignore-bad-element.json",
"success-minimal-feed.json"));
TEST_P(MediaFeedsSpecTest, RunOpenSourceTest) {
const GURL feed_url("https://www.google.com/feed");
// Get the path of the test data.
base::FilePath file;
ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &file));
file = file.Append(kMediaFeedsTestSpecDir).AppendASCII(GetParam());
// Read the test file to a string.
std::string test_data;
base::ReadFileToString(file, &test_data);
ASSERT_TRUE(test_data.size());
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Fetch the Media Feed.
base::RunLoop run_loop;
GetMediaFeedsService()->FetchMediaFeed(
1, base::BindLambdaForTesting(
[&](const std::string& ignored) { run_loop.Quit(); }));
WaitForDB();
ASSERT_TRUE(RespondToPendingFeedFetchWithData(feed_url, test_data));
run_loop.Run();
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(1u, feeds.size());
if (base::Contains(GetParam(), "success")) {
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
} else {
EXPECT_EQ(media_feeds::mojom::FetchResult::kInvalidFeed,
feeds[0]->last_fetch_result);
}
}
// FetchTopMediaFeeds should fetch a feed with enough watchtime on that origin
// even if it hasn't been fetched before.
TEST_F(MediaFeedsServiceTest, FetchTopMediaFeeds_SuccessNewFetch) {
SetAutomaticSelectionEnabled();
base::HistogramTester histogram_tester;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
SetBackgroundFetchingEnabled(true);
task_environment()->RunUntilIdle();
// FetchTopMediaFeeds should ignore the feed, as the origin does not have
// enough watchtime.
ASSERT_FALSE(RespondToPendingFeedFetch(feed_url));
// Set the watchtime higher than the minimum threshold for top feeds.
auto watchtime = base::TimeDelta::FromMinutes(45);
content::MediaPlayerWatchTime watch_time(
feed_url, feed_url.GetOrigin(), watchtime, base::TimeDelta(), true, true);
GetMediaHistoryService()->SavePlayback(watch_time);
WaitForDB();
// Now that there is high watchtime, the fetch should occur the next time
// background fetching happens.
AdvanceTime(MediaFeedsService::kTimeBetweenBackgroundFetches);
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
auto feeds = GetMediaFeedsSync();
EXPECT_EQ(1u, feeds.size());
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit);
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
histogram_tester.ExpectUniqueSample(
MediaFeedsFetcher::kFetchSizeKbHistogramName, 15, 1);
}
// Fetch top feeds should periodically fetch the feed from cache if available.
TEST_F(MediaFeedsServiceTest, FetchTopMediaFeeds_SuccessFromCache) {
SetAutomaticSelectionEnabled();
base::HistogramTester histogram_tester;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Set the watchtime higher than the minimum threshold for top feeds.
auto watchtime = base::TimeDelta::FromMinutes(45);
content::MediaPlayerWatchTime watch_time(
feed_url, feed_url.GetOrigin(), watchtime, base::TimeDelta(), true, true);
GetMediaHistoryService()->SavePlayback(watch_time);
WaitForDB();
// Fetch the Media Feed an initial time.
base::RunLoop run_loop;
GetMediaFeedsService()->FetchMediaFeed(
1, base::BindLambdaForTesting(
[&](const std::string& ignored) { run_loop.Quit(); }));
WaitForDB();
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
run_loop.Run();
// After some time, background fetching should refresh the feed from the
// cached initial fetch.
AdvanceTime(MediaFeedsService::kTimeBetweenBackgroundFetches);
SetBackgroundFetchingEnabled(true);
task_environment()->RunUntilIdle();
EXPECT_FALSE(GetCurrentRequestHasBypassCacheFlag());
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
auto feeds = GetMediaFeedsSync();
EXPECT_EQ(1u, feeds.size());
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit);
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
histogram_tester.ExpectUniqueSample(
MediaFeedsFetcher::kFetchSizeKbHistogramName, 15, 2);
}
// FetchTopMediaFeeds should back off if the feed fails to fetch. But after 24
// hours, it should fetch regardless of failures, bypassing the cache.
TEST_F(MediaFeedsServiceTest, FetchTopMediaFeeds_BacksOffFailedFetches) {
SetAutomaticSelectionEnabled();
base::HistogramTester histogram_tester;
const int times_to_fail = 10;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Set the watchtime higher than the minimum threshold for top feeds.
auto watchtime = base::TimeDelta::FromMinutes(45);
content::MediaPlayerWatchTime watch_time(
feed_url, feed_url.GetOrigin(), watchtime, base::TimeDelta(), true, true);
GetMediaHistoryService()->SavePlayback(watch_time);
WaitForDB();
// Fetch the Media Feed unsuccessfully several times.
for (int i = 0; i < times_to_fail; i++) {
base::RunLoop run_loop;
GetMediaFeedsService()->FetchMediaFeed(
1, base::BindLambdaForTesting(
[&](const std::string& ignored) { run_loop.Quit(); }));
WaitForDB();
ASSERT_TRUE(RespondToPendingFeedFetchWithStatus(
feed_url, net::HTTP_INTERNAL_SERVER_ERROR));
run_loop.Run();
}
AdvanceTime(base::TimeDelta::FromHours(1));
SetBackgroundFetchingEnabled(true);
task_environment()->RunUntilIdle();
// No fetch should happen because of the backoff from failures.
ASSERT_FALSE(RespondToPendingFeedFetch(feed_url));
// After 24 hours, the feed should be fetched regardless of failure count.
AdvanceTime(MediaFeedsService::kTimeBetweenNonCachedBackgroundFetches);
task_environment()->RunUntilIdle();
// If we bypass failure count, we should also bypass cache.
EXPECT_TRUE(GetCurrentRequestHasBypassCacheFlag());
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
auto feeds = GetMediaFeedsSync();
EXPECT_EQ(1u, feeds.size());
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit);
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
histogram_tester.ExpectUniqueSample(
MediaFeedsFetcher::kFetchSizeKbHistogramName, 15, 1);
}
// After 24 hours, FetchTopMediaFeeds should fetch the feed and bypass the
// cache.
TEST_F(MediaFeedsServiceTest, FetchTopMediaFeeds_SuccessBypassCache) {
SetAutomaticSelectionEnabled();
base::HistogramTester histogram_tester;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Set the watchtime higher than the minimum threshold for top feeds.
auto watchtime = base::TimeDelta::FromMinutes(45);
content::MediaPlayerWatchTime watch_time(
feed_url, feed_url.GetOrigin(), watchtime, base::TimeDelta(), true, true);
GetMediaHistoryService()->SavePlayback(watch_time);
WaitForDB();
// Fetch the Media Feed an initial time.
base::RunLoop run_loop;
GetMediaFeedsService()->FetchMediaFeed(
1, base::BindLambdaForTesting(
[&](const std::string& ignored) { run_loop.Quit(); }));
WaitForDB();
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
run_loop.Run();
AdvanceTime(base::TimeDelta::FromHours(24));
SetBackgroundFetchingEnabled(true);
task_environment()->RunUntilIdle();
// After a long time between fetches, we should bypass the cache.
EXPECT_TRUE(GetCurrentRequestHasBypassCacheFlag());
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
auto feeds = GetMediaFeedsSync();
EXPECT_EQ(1u, feeds.size());
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit);
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
histogram_tester.ExpectUniqueSample(
MediaFeedsFetcher::kFetchSizeKbHistogramName, 15, 2);
}
// After a feed reset, FetchTopMediaFeeds should fetch anyway.
TEST_F(MediaFeedsServiceTest, FetchTopMediaFeeds_SuccessResetFeed) {
SetAutomaticSelectionEnabled();
base::HistogramTester histogram_tester;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
auto watchtime = base::TimeDelta::FromMinutes(45);
content::MediaPlayerWatchTime watch_time(
feed_url, feed_url.GetOrigin(), watchtime, base::TimeDelta(), true, true);
GetMediaHistoryService()->SavePlayback(watch_time);
WaitForDB();
// Fetch the Media Feed.
base::RunLoop run_loop;
GetMediaFeedsService()->FetchMediaFeed(
1, base::BindLambdaForTesting(
[&](const std::string& ignored) { run_loop.Quit(); }));
WaitForDB();
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
run_loop.Run();
GetMediaFeedsService()->ResetMediaFeed(url::Origin::Create(feed_url),
mojom::ResetReason::kVisit);
WaitForDB();
SetBackgroundFetchingEnabled(true);
task_environment()->RunUntilIdle();
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
auto feeds = GetMediaFeedsSync();
EXPECT_EQ(1u, feeds.size());
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit);
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
histogram_tester.ExpectUniqueSample(
MediaFeedsFetcher::kFetchSizeKbHistogramName, 15, 2);
}
// After enabling the pref, top feeds should fetch immediately and then again
// after 15 minutes.
TEST_F(MediaFeedsServiceTest, FetchTopMediaFeeds_SuccessRepeatsPeriodically) {
SetAutomaticSelectionEnabled();
base::HistogramTester histogram_tester;
const GURL feed_url("https://www.google.com/feed");
// Store a Media Feed.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
// Set the watchtime higher than the minimum threshold for top feeds.
auto watchtime = base::TimeDelta::FromMinutes(45);
content::MediaPlayerWatchTime watch_time(
feed_url, feed_url.GetOrigin(), watchtime, base::TimeDelta(), true, true);
GetMediaHistoryService()->SavePlayback(watch_time);
WaitForDB();
// Once we set this, background fetching should start automatically.
SetBackgroundFetchingEnabled(true);
task_environment()->RunUntilIdle();
// There should be only one fetch ready.
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
ASSERT_FALSE(RespondToPendingFeedFetch(feed_url));
// Wait 15 minutes and the next fetch should be queued up.
AdvanceTime(MediaFeedsService::kTimeBetweenBackgroundFetches);
task_environment()->RunUntilIdle();
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url));
auto feeds = GetMediaFeedsSync();
EXPECT_EQ(1u, feeds.size());
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit);
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
histogram_tester.ExpectUniqueSample(
MediaFeedsFetcher::kFetchSizeKbHistogramName, 15, 2);
}
// FetchTopMediaFeeds should fetch a feed with enough watchtime on that origin
// even if it hasn't been fetched before.
TEST_F(MediaFeedsServiceTest, FetchTopMediaFeeds_DisableAutoSelection) {
base::HistogramTester histogram_tester;
const GURL feed_url_a("https://www.google.com/feed");
const GURL feed_url_b("https://www.google.co.uk/feed");
// Store a couple of Media Feeds.
GetMediaFeedsService()->DiscoverMediaFeed(feed_url_a);
GetMediaFeedsService()->DiscoverMediaFeed(feed_url_b);
WaitForDB();
// The first feed we should opt into.
GetMediaHistoryService()->UpdateFeedUserStatus(
1, media_feeds::mojom::FeedUserStatus::kEnabled);
WaitForDB();
SetBackgroundFetchingEnabled(true);
task_environment()->RunUntilIdle();
// The first feed should be fetched and the second one should be ignored since
// the user has not enabled it.
ASSERT_TRUE(RespondToPendingFeedFetch(feed_url_a));
ASSERT_FALSE(RespondToPendingFeedFetch(feed_url_b));
auto feeds = GetMediaFeedsSync();
ASSERT_EQ(2u, feeds.size());
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit);
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
EXPECT_EQ(media_feeds::mojom::FetchResult::kNone,
feeds[1]->last_fetch_result);
histogram_tester.ExpectUniqueSample(
MediaFeedsFetcher::kFetchSizeKbHistogramName, 15, 1);
}
TEST_F(MediaFeedsServiceTest, AggregateWatchtimeHistogram) {
base::HistogramTester histogram_tester;
task_environment()->RunUntilIdle();
const GURL feed_url("https://www.google.com/feed");
GetMediaFeedsService()->DiscoverMediaFeed(feed_url);
WaitForDB();
content::MediaPlayerWatchTime watch_time(feed_url, feed_url.GetOrigin(),
base::TimeDelta::FromMinutes(30),
base::TimeDelta(), true, true);
GetMediaHistoryService()->SavePlayback(watch_time);
WaitForDB();
GetMediaFeedsService()->RecordFeedWatchtimes();
WaitForDB();
histogram_tester.ExpectUniqueTimeSample(
MediaFeedsService::kAggregateWatchtimeHistogramName,
base::TimeDelta::FromMinutes(30), 1);
}
} // namespace media_feeds