blob: 3eaf0f65545bd9f562063d2b7c9ed6a52b2bb5e7 [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 "chrome/browser/media/history/media_history_store.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/pooled_sequenced_task_runner.h"
#include "base/test/bind_test_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_timeouts.h"
#include "build/build_config.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/media/feeds/media_feeds_store.mojom-forward.h"
#include "chrome/browser/media/feeds/media_feeds_store.mojom.h"
#include "chrome/browser/media/history/media_history_feed_items_table.h"
#include "chrome/browser/media/history/media_history_feeds_table.h"
#include "chrome/browser/media/history/media_history_images_table.h"
#include "chrome/browser/media/history/media_history_keyed_service.h"
#include "chrome/browser/media/history/media_history_origin_table.h"
#include "chrome/browser/media/history/media_history_playback_table.h"
#include "chrome/browser/media/history/media_history_session_images_table.h"
#include "chrome/browser/media/history/media_history_session_table.h"
#include "chrome/browser/media/history/media_history_test_utils.h"
#include "chrome/test/base/testing_profile.h"
#include "components/history/core/browser/history_database_params.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/common/pref_names.h"
#include "components/history/core/test/test_history_database.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/media_player_watch_time.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_utils.h"
#include "media/base/media_switches.h"
#include "services/media_session/public/cpp/media_image.h"
#include "services/media_session/public/cpp/media_metadata.h"
#include "services/media_session/public/cpp/media_position.h"
#include "sql/statement.h"
#include "testing/gtest/include/gtest/gtest.h"
#if !defined(OS_ANDROID)
#include "chrome/browser/media/feeds/media_feeds_service.h"
#include "chrome/browser/media/feeds/media_feeds_service_factory.h"
#endif
namespace media_history {
namespace {
// The error margin for double time comparison. It is 10 seconds because it
// might be equal but it might be close too.
const int kTimeErrorMargin = 10000;
#if !defined(OS_ANDROID)
// The expected display name for the fetched media feed.
const char kExpectedDisplayName[] = "Test Feed";
// The expected counts and content types for the test feed.
const int kExpectedFetchItemCount = 3;
const int kExpectedFetchPlayNextCount = 2;
const int kExpectedFetchContentTypes =
static_cast<int>(media_feeds::mojom::MediaFeedItemType::kMovie) |
static_cast<int>(media_feeds::mojom::MediaFeedItemType::kTVSeries);
// The expected counts and content types for the alternate test feed.
const int kExpectedAltFetchItemCount = 1;
const int kExpectedAltFetchPlayNextCount = 1;
const int kExpectedAltFetchContentTypes =
static_cast<int>(media_feeds::mojom::MediaFeedItemType::kVideo);
#endif // !defined(OS_ANDROID)
base::FilePath g_temp_history_dir;
std::unique_ptr<KeyedService> BuildTestHistoryService(
content::BrowserContext* context) {
std::unique_ptr<history::HistoryService> service(
new history::HistoryService());
service->Init(history::TestHistoryDatabaseParamsForPath(g_temp_history_dir));
return service;
}
enum class TestState {
kNormal,
// Runs the test in incognito mode.
kIncognito,
// Runs the test with the "SavingBrowserHistoryDisabled" policy enabled.
kSavingBrowserHistoryDisabled,
};
} // namespace
// Runs the test with a param to signify the profile being incognito if true.
class MediaHistoryStoreUnitTest
: public testing::Test,
public testing::WithParamInterface<TestState> {
public:
MediaHistoryStoreUnitTest() = default;
void SetUp() override {
base::HistogramTester histogram_tester;
// Set up the profile.
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
TestingProfile::Builder profile_builder;
profile_builder.SetPath(temp_dir_.GetPath());
g_temp_history_dir = temp_dir_.GetPath();
profile_ = profile_builder.Build();
if (GetParam() == TestState::kSavingBrowserHistoryDisabled) {
profile_->GetPrefs()->SetBoolean(prefs::kSavingBrowserHistoryDisabled,
true);
}
HistoryServiceFactory::GetInstance()->SetTestingFactory(
profile_.get(), base::BindRepeating(&BuildTestHistoryService));
// Sleep the thread to allow the media history store to asynchronously
// create the database and tables before proceeding with the tests and
// tearing down the temporary directory.
WaitForDB();
histogram_tester.ExpectBucketCount(
MediaHistoryStore::kInitResultHistogramName,
MediaHistoryStore::InitResult::kSuccess, 1);
// Set up the media history store for OTR.
otr_service_ = std::make_unique<MediaHistoryKeyedService>(
profile_->GetPrimaryOTRProfile());
}
void TearDown() override { WaitForDB(); }
void WaitForDB() {
base::RunLoop run_loop;
MediaHistoryKeyedService::Get(profile_.get())
->PostTaskToDBForTest(run_loop.QuitClosure());
run_loop.Run();
}
mojom::MediaHistoryStatsPtr GetStatsSync(MediaHistoryKeyedService* service) {
base::RunLoop run_loop;
mojom::MediaHistoryStatsPtr stats_out;
service->GetMediaHistoryStats(
base::BindLambdaForTesting([&](mojom::MediaHistoryStatsPtr stats) {
stats_out = std::move(stats);
run_loop.Quit();
}));
run_loop.Run();
return stats_out;
}
std::vector<mojom::MediaHistoryOriginRowPtr> GetOriginRowsSync(
MediaHistoryKeyedService* service) {
base::RunLoop run_loop;
std::vector<mojom::MediaHistoryOriginRowPtr> out;
service->GetOriginRowsForDebug(base::BindLambdaForTesting(
[&](std::vector<mojom::MediaHistoryOriginRowPtr> rows) {
out = std::move(rows);
run_loop.Quit();
}));
run_loop.Run();
return out;
}
std::vector<mojom::MediaHistoryPlaybackRowPtr> GetPlaybackRowsSync(
MediaHistoryKeyedService* service) {
base::RunLoop run_loop;
std::vector<mojom::MediaHistoryPlaybackRowPtr> out;
service->GetMediaHistoryPlaybackRowsForDebug(base::BindLambdaForTesting(
[&](std::vector<mojom::MediaHistoryPlaybackRowPtr> rows) {
out = std::move(rows);
run_loop.Quit();
}));
run_loop.Run();
return out;
}
std::vector<media_feeds::mojom::MediaFeedPtr> GetMediaFeedsSync(
MediaHistoryKeyedService* service,
const MediaHistoryKeyedService::GetMediaFeedsRequest& request =
MediaHistoryKeyedService::GetMediaFeedsRequest()) {
base::RunLoop run_loop;
std::vector<media_feeds::mojom::MediaFeedPtr> out;
service->GetMediaFeeds(
request, base::BindLambdaForTesting(
[&](std::vector<media_feeds::mojom::MediaFeedPtr> rows) {
out = std::move(rows);
run_loop.Quit();
}));
run_loop.Run();
return out;
}
static std::vector<mojom::MediaHistoryPlaybackSessionRowPtr>
GetPlaybackSessionsSync(MediaHistoryKeyedService* service, int max_sessions) {
base::RunLoop run_loop;
std::vector<mojom::MediaHistoryPlaybackSessionRowPtr> out;
service->GetPlaybackSessions(
max_sessions,
base::BindRepeating(
[](const base::TimeDelta& duration,
const base::TimeDelta& position) { return true; }),
base::BindLambdaForTesting(
[&](std::vector<mojom::MediaHistoryPlaybackSessionRowPtr>
sessions) {
out = std::move(sessions);
run_loop.Quit();
}));
run_loop.Run();
return out;
}
media_history::MediaHistoryKeyedService::MediaFeedFetchResult ResultWithItems(
const int64_t feed_id,
std::vector<media_feeds::mojom::MediaFeedItemPtr> items,
media_feeds::mojom::FetchResult fetch_result) {
media_history::MediaHistoryKeyedService::MediaFeedFetchResult result;
result.feed_id = feed_id;
result.items = std::move(items);
result.status = fetch_result;
result.reset_token = test::GetResetTokenSync(service(), feed_id);
return result;
}
media_history::MediaHistoryKeyedService::MediaFeedFetchResult
SuccessfulResultWithItems(
const int64_t feed_id,
std::vector<media_feeds::mojom::MediaFeedItemPtr> items) {
return ResultWithItems(feed_id, std::move(items),
media_feeds::mojom::FetchResult::kSuccess);
}
MediaHistoryKeyedService* service() const {
// If the param is true then we use the OTR service to simulate being in
// incognito.
if (GetParam() == TestState::kIncognito)
return otr_service();
return MediaHistoryKeyedService::Get(profile_.get());
}
MediaHistoryKeyedService* otr_service() const { return otr_service_.get(); }
bool IsReadOnly() const { return GetParam() != TestState::kNormal; }
Profile* GetProfile() { return profile_.get(); }
private:
base::ScopedTempDir temp_dir_;
protected:
content::BrowserTaskEnvironment task_environment_;
private:
std::unique_ptr<MediaHistoryKeyedService> otr_service_;
std::unique_ptr<TestingProfile> profile_;
};
INSTANTIATE_TEST_SUITE_P(
All,
MediaHistoryStoreUnitTest,
testing::Values(TestState::kNormal,
TestState::kIncognito,
TestState::kSavingBrowserHistoryDisabled));
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_SavePlayback DISABLED_SavePlayback
#else
#define MAYBE_SavePlayback SavePlayback
#endif
TEST_P(MediaHistoryStoreUnitTest, MAYBE_SavePlayback) {
base::HistogramTester histogram_tester;
const auto now_before =
(base::Time::Now() - base::TimeDelta::FromMinutes(1)).ToJsTime();
// Create a media player watch time and save it to the playbacks table.
GURL url("http://google.com/test");
content::MediaPlayerWatchTime watch_time(url, url.GetOrigin(),
base::TimeDelta::FromSeconds(60),
base::TimeDelta(), true, false);
service()->SavePlayback(watch_time);
const auto now_after_a = base::Time::Now().ToJsTime();
// Save the watch time a second time.
service()->SavePlayback(watch_time);
// Wait until the playbacks have finished saving.
WaitForDB();
const auto now_after_b = base::Time::Now().ToJsTime();
// Verify that the playback table contains the expected number of items.
std::vector<mojom::MediaHistoryPlaybackRowPtr> playbacks =
GetPlaybackRowsSync(service());
if (IsReadOnly()) {
EXPECT_TRUE(playbacks.empty());
} else {
EXPECT_EQ(2u, playbacks.size());
EXPECT_EQ("http://google.com/test", playbacks[0]->url.spec());
EXPECT_FALSE(playbacks[0]->has_audio);
EXPECT_TRUE(playbacks[0]->has_video);
EXPECT_EQ(base::TimeDelta::FromSeconds(60), playbacks[0]->watchtime);
EXPECT_LE(now_before, playbacks[0]->last_updated_time);
EXPECT_GE(now_after_a, playbacks[0]->last_updated_time);
EXPECT_EQ("http://google.com/test", playbacks[1]->url.spec());
EXPECT_FALSE(playbacks[1]->has_audio);
EXPECT_TRUE(playbacks[1]->has_video);
EXPECT_EQ(base::TimeDelta::FromSeconds(60), playbacks[1]->watchtime);
EXPECT_LE(now_before, playbacks[1]->last_updated_time);
EXPECT_GE(now_after_b, playbacks[1]->last_updated_time);
}
// Verify that the origin table contains the expected number of items.
std::vector<mojom::MediaHistoryOriginRowPtr> origins =
GetOriginRowsSync(service());
if (IsReadOnly()) {
EXPECT_TRUE(origins.empty());
} else {
EXPECT_EQ(1u, origins.size());
EXPECT_EQ("http://google.com", origins[0]->origin.Serialize());
EXPECT_LE(now_before, origins[0]->last_updated_time);
EXPECT_GE(now_after_b, origins[0]->last_updated_time);
}
// The OTR service should have the same data.
EXPECT_EQ(origins, GetOriginRowsSync(otr_service()));
EXPECT_EQ(playbacks, GetPlaybackRowsSync(otr_service()));
histogram_tester.ExpectBucketCount(
MediaHistoryStore::kPlaybackWriteResultHistogramName,
MediaHistoryStore::PlaybackWriteResult::kSuccess, IsReadOnly() ? 0 : 2);
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_SavePlayback_BadOrigin DISABLED_SavePlayback_BadOrigin
#else
#define MAYBE_SavePlayback_BadOrigin SavePlayback_BadOrigin
#endif
TEST_P(MediaHistoryStoreUnitTest, MAYBE_SavePlayback_BadOrigin) {
GURL url("http://google.com/test");
GURL url2("http://google.co.uk/test");
content::MediaPlayerWatchTime watch_time(url, url2.GetOrigin(),
base::TimeDelta::FromSeconds(60),
base::TimeDelta(), true, false);
service()->SavePlayback(watch_time);
// Verify that the origin and playbacks table are empty.
auto origins = GetOriginRowsSync(service());
auto playbacks = GetPlaybackRowsSync(service());
EXPECT_TRUE(playbacks.empty());
EXPECT_TRUE(origins.empty());
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_GetStats DISABLED_GetStats
#else
#define MAYBE_GetStats GetStats
#endif
TEST_P(MediaHistoryStoreUnitTest, MAYBE_GetStats) {
{
// Check all the tables are empty.
mojom::MediaHistoryStatsPtr stats = GetStatsSync(service());
EXPECT_EQ(0, stats->table_row_counts[MediaHistoryOriginTable::kTableName]);
EXPECT_EQ(0,
stats->table_row_counts[MediaHistoryPlaybackTable::kTableName]);
EXPECT_EQ(0, stats->table_row_counts[MediaHistorySessionTable::kTableName]);
EXPECT_EQ(
0, stats->table_row_counts[MediaHistorySessionImagesTable::kTableName]);
EXPECT_EQ(0, stats->table_row_counts[MediaHistoryImagesTable::kTableName]);
// The OTR service should have the same data.
EXPECT_EQ(stats, GetStatsSync(otr_service()));
}
{
// Create a media player watch time and save it to the playbacks table.
GURL url("http://google.com/test");
content::MediaPlayerWatchTime watch_time(
url, url.GetOrigin(), base::TimeDelta::FromMilliseconds(123),
base::TimeDelta::FromMilliseconds(321), true, false);
service()->SavePlayback(watch_time);
}
{
// Check the tables have records in them.
mojom::MediaHistoryStatsPtr stats = GetStatsSync(service());
if (IsReadOnly()) {
EXPECT_EQ(0,
stats->table_row_counts[MediaHistoryOriginTable::kTableName]);
EXPECT_EQ(0,
stats->table_row_counts[MediaHistoryPlaybackTable::kTableName]);
EXPECT_EQ(0,
stats->table_row_counts[MediaHistorySessionTable::kTableName]);
EXPECT_EQ(
0,
stats->table_row_counts[MediaHistorySessionImagesTable::kTableName]);
EXPECT_EQ(0,
stats->table_row_counts[MediaHistoryImagesTable::kTableName]);
} else {
EXPECT_EQ(1,
stats->table_row_counts[MediaHistoryOriginTable::kTableName]);
EXPECT_EQ(1,
stats->table_row_counts[MediaHistoryPlaybackTable::kTableName]);
EXPECT_EQ(0,
stats->table_row_counts[MediaHistorySessionTable::kTableName]);
EXPECT_EQ(
0,
stats->table_row_counts[MediaHistorySessionImagesTable::kTableName]);
EXPECT_EQ(0,
stats->table_row_counts[MediaHistoryImagesTable::kTableName]);
}
// The OTR service should have the same data.
EXPECT_EQ(stats, GetStatsSync(otr_service()));
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_UrlShouldBeUniqueForSessions DISABLED_UrlShouldBeUniqueForSessions
#else
#define MAYBE_UrlShouldBeUniqueForSessions UrlShouldBeUniqueForSessions
#endif
TEST_P(MediaHistoryStoreUnitTest, MAYBE_UrlShouldBeUniqueForSessions) {
base::HistogramTester histogram_tester;
GURL url_a("https://www.google.com");
GURL url_b("https://www.example.org");
{
mojom::MediaHistoryStatsPtr stats = GetStatsSync(service());
EXPECT_EQ(0, stats->table_row_counts[MediaHistorySessionTable::kTableName]);
// The OTR service should have the same data.
EXPECT_EQ(stats, GetStatsSync(otr_service()));
}
// Save a couple of sessions on different URLs.
service()->SavePlaybackSession(url_a, media_session::MediaMetadata(),
base::nullopt,
std::vector<media_session::MediaImage>());
service()->SavePlaybackSession(url_b, media_session::MediaMetadata(),
base::nullopt,
std::vector<media_session::MediaImage>());
// Wait until the sessions have finished saving.
WaitForDB();
{
auto sessions = GetPlaybackSessionsSync(service(), 5);
if (IsReadOnly()) {
EXPECT_TRUE(sessions.empty());
} else {
EXPECT_EQ(2u, sessions.size());
for (auto& session : sessions) {
if (session->url == url_a)
EXPECT_EQ(1, session->id);
}
}
// The OTR service should have the same data.
EXPECT_EQ(sessions, GetPlaybackSessionsSync(otr_service(), 5));
}
// Save a session on the first URL.
service()->SavePlaybackSession(url_a, media_session::MediaMetadata(),
base::nullopt,
std::vector<media_session::MediaImage>());
// Wait until the sessions have finished saving.
WaitForDB();
{
auto sessions = GetPlaybackSessionsSync(service(), 5);
if (IsReadOnly()) {
EXPECT_TRUE(sessions.empty());
} else {
EXPECT_EQ(2u, sessions.size());
for (auto& session : sessions) {
if (session->url == url_a)
EXPECT_EQ(3, session->id);
}
}
// The OTR service should have the same data.
EXPECT_EQ(sessions, GetPlaybackSessionsSync(otr_service(), 5));
}
histogram_tester.ExpectBucketCount(
MediaHistoryStore::kSessionWriteResultHistogramName,
MediaHistoryStore::SessionWriteResult::kSuccess, IsReadOnly() ? 0 : 3);
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_SavePlayback_IncrementAggregateWatchtime \
DISABLED_SavePlayback_IncrementAggregateWatchtime
#else
#define MAYBE_SavePlayback_IncrementAggregateWatchtime \
SavePlayback_IncrementAggregateWatchtime
#endif
TEST_P(MediaHistoryStoreUnitTest,
MAYBE_SavePlayback_IncrementAggregateWatchtime) {
GURL url("http://google.com/test");
GURL url_alt("http://example.org/test");
const auto url_now_before = base::Time::Now().ToJsTime();
{
// Record a watchtime for audio/video for 30 seconds.
content::MediaPlayerWatchTime watch_time(
url, url.GetOrigin(), base::TimeDelta::FromSeconds(30),
base::TimeDelta(), true /* has_video */, true /* has_audio */);
service()->SavePlayback(watch_time);
WaitForDB();
}
{
// Record a watchtime for audio/video for 60 seconds.
content::MediaPlayerWatchTime watch_time(
url, url.GetOrigin(), base::TimeDelta::FromSeconds(60),
base::TimeDelta(), true /* has_video */, true /* has_audio */);
service()->SavePlayback(watch_time);
WaitForDB();
}
{
// Record an audio-only watchtime for 30 seconds.
content::MediaPlayerWatchTime watch_time(
url, url.GetOrigin(), base::TimeDelta::FromSeconds(30),
base::TimeDelta(), false /* has_video */, true /* has_audio */);
service()->SavePlayback(watch_time);
WaitForDB();
}
{
// Record a video-only watchtime for 30 seconds.
content::MediaPlayerWatchTime watch_time(
url, url.GetOrigin(), base::TimeDelta::FromSeconds(30),
base::TimeDelta(), true /* has_video */, false /* has_audio */);
service()->SavePlayback(watch_time);
WaitForDB();
}
const auto url_now_after = base::Time::Now().ToJsTime();
{
// Record a watchtime for audio/video for 60 seconds on a different origin.
content::MediaPlayerWatchTime watch_time(
url_alt, url_alt.GetOrigin(), base::TimeDelta::FromSeconds(30),
base::TimeDelta(), true /* has_video */, true /* has_audio */);
service()->SavePlayback(watch_time);
WaitForDB();
}
const auto url_alt_after = base::Time::Now().ToJsTime();
{
// Check the playbacks were recorded.
mojom::MediaHistoryStatsPtr stats = GetStatsSync(service());
if (IsReadOnly()) {
EXPECT_EQ(0,
stats->table_row_counts[MediaHistoryOriginTable::kTableName]);
EXPECT_EQ(0,
stats->table_row_counts[MediaHistoryPlaybackTable::kTableName]);
} else {
EXPECT_EQ(2,
stats->table_row_counts[MediaHistoryOriginTable::kTableName]);
EXPECT_EQ(5,
stats->table_row_counts[MediaHistoryPlaybackTable::kTableName]);
}
// The OTR service should have the same data.
EXPECT_EQ(stats, GetStatsSync(otr_service()));
}
std::vector<mojom::MediaHistoryOriginRowPtr> origins =
GetOriginRowsSync(service());
if (IsReadOnly()) {
EXPECT_TRUE(origins.empty());
} else {
EXPECT_EQ(2u, origins.size());
EXPECT_EQ("http://google.com", origins[0]->origin.Serialize());
EXPECT_EQ(base::TimeDelta::FromSeconds(90),
origins[0]->cached_audio_video_watchtime);
EXPECT_NEAR(url_now_before, origins[0]->last_updated_time,
kTimeErrorMargin);
EXPECT_GE(url_now_after, origins[0]->last_updated_time);
EXPECT_EQ(origins[0]->cached_audio_video_watchtime,
origins[0]->actual_audio_video_watchtime);
EXPECT_EQ("http://example.org", origins[1]->origin.Serialize());
EXPECT_EQ(base::TimeDelta::FromSeconds(30),
origins[1]->cached_audio_video_watchtime);
EXPECT_NEAR(url_now_before, origins[1]->last_updated_time,
kTimeErrorMargin);
EXPECT_GE(url_alt_after, origins[1]->last_updated_time);
EXPECT_EQ(origins[1]->cached_audio_video_watchtime,
origins[1]->actual_audio_video_watchtime);
}
// The OTR service should have the same data.
EXPECT_EQ(origins, GetOriginRowsSync(otr_service()));
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_GetOriginsWithHighWatchTime DISABLED_GetOriginsWithHighWatchTime
#else
#define MAYBE_GetOriginsWithHighWatchTime GetOriginsWithHighWatchTime
#endif
TEST_P(MediaHistoryStoreUnitTest, MAYBE_GetOriginsWithHighWatchTime) {
const GURL url("http://google.com/test");
const GURL url_alt("http://example.org/test");
const base::TimeDelta min_watch_time = base::TimeDelta::FromMinutes(30);
{
// Record a watch time that isn't high enough to get with our request.
content::MediaPlayerWatchTime watch_time(
url, url.GetOrigin(), min_watch_time - base::TimeDelta::FromSeconds(1),
base::TimeDelta(), true /* has_video */, true /* has_audio */);
service()->SavePlayback(watch_time);
WaitForDB();
}
{
// Record a watchtime that we should get with our request.
content::MediaPlayerWatchTime watch_time(
url_alt, url_alt.GetOrigin(), min_watch_time, base::TimeDelta(),
true /* has_video */, true /* has_audio */);
service()->SavePlayback(watch_time);
WaitForDB();
}
base::RunLoop run_loop;
std::vector<url::Origin> out;
service()->GetHighWatchTimeOrigins(
min_watch_time,
base::BindLambdaForTesting([&](const std::vector<url::Origin>& origins) {
out = std::move(origins);
run_loop.Quit();
}));
run_loop.Run();
if (IsReadOnly()) {
EXPECT_TRUE(out.empty());
} else {
std::vector<url::Origin> expected = {url::Origin::Create(url_alt)};
EXPECT_EQ(out, expected);
}
}
#if !defined(OS_ANDROID)
// Runs the tests with the media feeds feature enabled.
class MediaHistoryStoreFeedsTest : public MediaHistoryStoreUnitTest {
public:
void SetUp() override {
features_.InitAndEnableFeature(media::kMediaFeeds);
MediaHistoryStoreUnitTest::SetUp();
}
void DiscoverMediaFeed(const GURL& url) {
if (auto* service = GetMediaFeedsService())
service->DiscoverMediaFeed(url);
}
media_feeds::MediaFeedsService* GetMediaFeedsService() {
Profile* profile = GetProfile();
if (GetParam() == TestState::kIncognito)
profile = profile->GetPrimaryOTRProfile();
return media_feeds::MediaFeedsServiceFactory::GetInstance()->GetForProfile(
profile);
}
std::vector<media_feeds::mojom::MediaFeedItemPtr> GetItemsForMediaFeedSync(
MediaHistoryKeyedService* service,
const int64_t feed_id) {
return GetItemsForMediaFeedSync(
service,
MediaHistoryKeyedService::GetMediaFeedItemsRequest::CreateItemsForDebug(
feed_id));
}
std::vector<media_feeds::mojom::MediaFeedItemPtr> GetItemsForMediaFeedSync(
MediaHistoryKeyedService* service,
MediaHistoryKeyedService::GetMediaFeedItemsRequest request) {
base::RunLoop run_loop;
std::vector<media_feeds::mojom::MediaFeedItemPtr> out;
service->GetMediaFeedItems(
request,
base::BindLambdaForTesting(
[&](std::vector<media_feeds::mojom::MediaFeedItemPtr> rows) {
out = std::move(rows);
run_loop.Quit();
}));
run_loop.Run();
return out;
}
MediaHistoryKeyedService::PendingSafeSearchCheckList
GetPendingSafeSearchCheckMediaFeedItemsSync(
MediaHistoryKeyedService* service) {
base::RunLoop run_loop;
MediaHistoryKeyedService::PendingSafeSearchCheckList out;
service->GetPendingSafeSearchCheckMediaFeedItems(base::BindLambdaForTesting(
[&](MediaHistoryKeyedService::PendingSafeSearchCheckList rows) {
out = std::move(rows);
run_loop.Quit();
}));
run_loop.Run();
return out;
}
static media_feeds::mojom::ContentRatingPtr CreateRating(
const std::string& agency,
const std::string& value) {
auto rating = media_feeds::mojom::ContentRating::New();
rating->agency = agency;
rating->value = value;
return rating;
}
static media_feeds::mojom::IdentifierPtr CreateIdentifier(
const media_feeds::mojom::Identifier::Type& type,
const std::string& value) {
auto identifier = media_feeds::mojom::Identifier::New();
identifier->type = type;
identifier->value = value;
return identifier;
}
static std::vector<media_feeds::mojom::MediaFeedItemPtr> GetExpectedItems(
int id_start = 0) {
std::vector<media_feeds::mojom::MediaFeedItemPtr> items;
{
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->is_family_friendly = media_feeds::mojom::IsFamilyFriendly::kYes;
item->action_status =
media_feeds::mojom::MediaFeedItemActionStatus::kPotential;
item->genre.push_back("test");
item->duration = base::TimeDelta::FromSeconds(30);
item->live = media_feeds::mojom::LiveDetails::New();
item->live->start_time = base::Time::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromMinutes(20));
item->live->end_time = base::Time::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromMinutes(30));
item->shown_count = 3;
item->clicked = true;
item->author = media_feeds::mojom::Author::New();
item->author->name = "Media Site";
item->author->url = GURL("https://www.example.com/author");
item->action = media_feeds::mojom::Action::New();
item->action->start_time = base::TimeDelta::FromSeconds(3);
item->action->url = GURL("https://www.example.com/action");
item->interaction_counters.emplace(
media_feeds::mojom::InteractionCounterType::kLike, 10000);
item->interaction_counters.emplace(
media_feeds::mojom::InteractionCounterType::kDislike, 20000);
item->interaction_counters.emplace(
media_feeds::mojom::InteractionCounterType::kWatch, 30000);
item->content_ratings.push_back(CreateRating("MPAA", "PG-13"));
item->content_ratings.push_back(CreateRating("agency", "TEST2"));
item->identifiers.push_back(CreateIdentifier(
media_feeds::mojom::Identifier::Type::kPartnerId, "TEST1"));
item->identifiers.push_back(CreateIdentifier(
media_feeds::mojom::Identifier::Type::kTMSId, "TEST2"));
item->tv_episode = media_feeds::mojom::TVEpisode::New();
item->tv_episode->name = "TV Episode Name";
item->tv_episode->season_number = 1;
item->tv_episode->episode_number = 2;
item->tv_episode->identifiers.push_back(CreateIdentifier(
media_feeds::mojom::Identifier::Type::kTMSId, "TEST3"));
item->tv_episode->duration = base::TimeDelta::FromMinutes(40);
item->tv_episode->live = media_feeds::mojom::LiveDetails::New();
item->tv_episode->live->start_time =
base::Time::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromSeconds(15));
item->play_next_candidate = media_feeds::mojom::PlayNextCandidate::New();
item->play_next_candidate->name = "Next TV Episode Name";
item->play_next_candidate->season_number = 1;
item->play_next_candidate->episode_number = 3;
item->play_next_candidate->duration = base::TimeDelta::FromMinutes(20);
item->play_next_candidate->action = media_feeds::mojom::Action::New();
item->play_next_candidate->action->start_time =
base::TimeDelta::FromSeconds(3);
item->play_next_candidate->action->url =
GURL("https://www.example.com/next");
item->play_next_candidate->identifiers.push_back(CreateIdentifier(
media_feeds::mojom::Identifier::Type::kTMSId, "TEST4"));
item->safe_search_result = media_feeds::mojom::SafeSearchResult::kUnknown;
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image1.png");
item->images.push_back(std::move(image));
}
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image2.png");
image->size = gfx::Size(10, 10);
item->images.push_back(std::move(image));
}
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/episode-image.png");
item->tv_episode->images.push_back(std::move(image));
}
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/next-image.png");
item->play_next_candidate->images.push_back(std::move(image));
}
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 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";
item->safe_search_result = media_feeds::mojom::SafeSearchResult::kSafe;
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->live = media_feeds::mojom::LiveDetails::New();
item->live->start_time = base::Time::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromSeconds(30));
item->safe_search_result = media_feeds::mojom::SafeSearchResult::kUnsafe;
items.push_back(std::move(item));
}
return items;
}
static std::vector<media_feeds::mojom::MediaFeedItemPtr> GetAltExpectedItems(
int id_start = 0) {
std::vector<media_feeds::mojom::MediaFeedItemPtr> items;
{
auto item = media_feeds::mojom::MediaFeedItem::New();
item->id = ++id_start;
item->type = media_feeds::mojom::MediaFeedItemType::kVideo;
item->name = base::ASCIIToUTF16("The Video");
item->date_published = base::Time::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromMinutes(20));
item->is_family_friendly = media_feeds::mojom::IsFamilyFriendly::kNo;
item->action_status =
media_feeds::mojom::MediaFeedItemActionStatus::kActive;
item->action = media_feeds::mojom::Action::New();
item->action->url = GURL("https://www.example.com/action-alt");
item->safe_search_result = media_feeds::mojom::SafeSearchResult::kUnknown;
items.push_back(std::move(item));
}
return items;
}
static std::vector<media_feeds::mojom::MediaImagePtr> GetExpectedLogos() {
std::vector<media_feeds::mojom::MediaImagePtr> logos;
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image1.png");
image->size = gfx::Size(10, 10);
logos.push_back(std::move(image));
}
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image2.png");
logos.push_back(std::move(image));
}
return logos;
}
static media_feeds::mojom::UserIdentifierPtr GetExpectedUserIdentifier() {
auto identifier = media_feeds::mojom::UserIdentifier::New();
identifier->name = "Becca Hughes";
identifier->email = "test@chromium.org";
identifier->image = media_feeds::mojom::MediaImage::New();
identifier->image->src = GURL("https://www.example.org/image1.png");
identifier->image->size = gfx::Size(10, 10);
return identifier;
}
base::Optional<media_history::MediaHistoryKeyedService::MediaFeedFetchDetails>
GetFetchDetailsSync(const int64_t feed_id) {
base::RunLoop run_loop;
base::Optional<
media_history::MediaHistoryKeyedService::MediaFeedFetchDetails>
out;
service()->GetMediaFeedFetchDetails(
feed_id,
base::BindLambdaForTesting(
[&](base::Optional<
media_history::MediaHistoryKeyedService::MediaFeedFetchDetails>
details) {
out = std::move(details);
run_loop.Quit();
}));
run_loop.Run();
return out;
}
private:
base::test::ScopedFeatureList features_;
};
INSTANTIATE_TEST_SUITE_P(All,
MediaHistoryStoreFeedsTest,
testing::Values(TestState::kNormal,
TestState::kIncognito));
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_DiscoverMediaFeed DISABLED_DiscoverMediaFeed
#else
#define MAYBE_DiscoverMediaFeed DiscoverMediaFeed
#endif
TEST_P(MediaHistoryStoreFeedsTest, MAYBE_DiscoverMediaFeed) {
GURL url_a("https://www.google.com/feed");
GURL url_b("https://www.google.co.uk/feed");
GURL url_c("https://www.google.com/feed2");
DiscoverMediaFeed(url_a);
DiscoverMediaFeed(url_b);
WaitForDB();
{
// Check the feeds were recorded.
std::vector<media_feeds::mojom::MediaFeedPtr> feeds =
GetMediaFeedsSync(service());
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
EXPECT_EQ(2u, feeds.size());
EXPECT_EQ(1, feeds[0]->id);
EXPECT_EQ(url_a, feeds[0]->url);
EXPECT_FALSE(feeds[0]->last_fetch_time.has_value());
EXPECT_EQ(media_feeds::mojom::FetchResult::kNone,
feeds[0]->last_fetch_result);
EXPECT_EQ(0, feeds[0]->fetch_failed_count);
EXPECT_FALSE(feeds[0]->last_fetch_time_not_cache_hit.has_value());
EXPECT_EQ(0, feeds[0]->last_fetch_item_count);
EXPECT_EQ(0, feeds[0]->last_fetch_play_next_count);
EXPECT_EQ(0, feeds[0]->last_fetch_content_types);
EXPECT_TRUE(feeds[0]->logos.empty());
EXPECT_TRUE(feeds[0]->display_name.empty());
EXPECT_TRUE(feeds[0]->user_identifier.is_null());
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnknown,
feeds[0]->safe_search_result);
EXPECT_EQ(2, feeds[1]->id);
EXPECT_EQ(url_b, feeds[1]->url);
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
}
DiscoverMediaFeed(url_c);
WaitForDB();
{
// Check the feeds were recorded.
std::vector<media_feeds::mojom::MediaFeedPtr> feeds =
GetMediaFeedsSync(service());
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
EXPECT_EQ(2u, feeds.size());
EXPECT_EQ(2, feeds[0]->id);
EXPECT_EQ(url_b, feeds[0]->url);
EXPECT_EQ(3, feeds[1]->id);
EXPECT_EQ(url_c, feeds[1]->url);
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_StoreMediaFeedFetchResult DISABLED_StoreMediaFeedFetchResult
#else
#define MAYBE_StoreMediaFeedFetchResult StoreMediaFeedFetchResult
#endif
TEST_P(MediaHistoryStoreFeedsTest, MAYBE_StoreMediaFeedFetchResult) {
const GURL feed_url("https://www.google.com/feed");
DiscoverMediaFeed(feed_url);
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
{
auto result = SuccessfulResultWithItems(feed_id, GetExpectedItems());
result.logos = GetExpectedLogos();
result.display_name = kExpectedDisplayName;
result.user_identifier = GetExpectedUserIdentifier();
result.cookie_name_filter = "test";
result.items[0]->id = 9;
service()->StoreMediaFeedFetchResult(std::move(result), base::DoNothing());
WaitForDB();
// The media items should be stored and the feed should be updated.
auto feeds = GetMediaFeedsSync(service());
auto items = GetItemsForMediaFeedSync(service(), feed_id);
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
EXPECT_TRUE(items.empty());
} else {
EXPECT_EQ(feed_id, feeds[0]->id);
EXPECT_TRUE(feeds[0]->last_fetch_time.has_value());
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
EXPECT_EQ(0, feeds[0]->fetch_failed_count);
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit.has_value());
EXPECT_EQ(kExpectedFetchItemCount, feeds[0]->last_fetch_item_count);
EXPECT_EQ(kExpectedFetchPlayNextCount,
feeds[0]->last_fetch_play_next_count);
EXPECT_EQ(kExpectedFetchContentTypes, feeds[0]->last_fetch_content_types);
EXPECT_EQ(GetExpectedLogos(), feeds[0]->logos);
EXPECT_EQ(kExpectedDisplayName, feeds[0]->display_name);
EXPECT_EQ(GetExpectedUserIdentifier(), feeds[0]->user_identifier);
EXPECT_FALSE(feeds[0]->last_display_time.has_value());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
EXPECT_EQ("test", feeds[0]->cookie_name_filter);
EXPECT_EQ(GetExpectedItems(), items);
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
}
base::Optional<base::Time> last_fetch_time_not_cache_hit;
{
auto result = SuccessfulResultWithItems(feed_id, GetAltExpectedItems());
result.display_name = kExpectedDisplayName;
service()->StoreMediaFeedFetchResult(std::move(result), base::DoNothing());
WaitForDB();
// The media items should be stored and the feed should be updated.
auto feeds = GetMediaFeedsSync(service());
auto items = GetItemsForMediaFeedSync(service(), feed_id);
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
EXPECT_TRUE(items.empty());
} else {
EXPECT_EQ(feed_id, feeds[0]->id);
EXPECT_TRUE(feeds[0]->last_fetch_time.has_value());
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
EXPECT_EQ(0, feeds[0]->fetch_failed_count);
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit.has_value());
EXPECT_EQ(kExpectedAltFetchItemCount, feeds[0]->last_fetch_item_count);
EXPECT_EQ(kExpectedAltFetchPlayNextCount,
feeds[0]->last_fetch_play_next_count);
EXPECT_EQ(kExpectedAltFetchContentTypes,
feeds[0]->last_fetch_content_types);
EXPECT_TRUE(feeds[0]->logos.empty());
EXPECT_EQ(kExpectedDisplayName, feeds[0]->display_name);
EXPECT_FALSE(feeds[0]->user_identifier);
EXPECT_FALSE(feeds[0]->last_display_time.has_value());
EXPECT_TRUE(feeds[0]->cookie_name_filter.empty());
EXPECT_EQ(GetAltExpectedItems(3), items);
last_fetch_time_not_cache_hit = feeds[0]->last_fetch_time_not_cache_hit;
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
}
{
auto result = SuccessfulResultWithItems(feed_id, GetAltExpectedItems());
result.display_name = kExpectedDisplayName;
service()->StoreMediaFeedFetchResult(std::move(result), base::DoNothing());
WaitForDB();
// The media items should be stored and the feed should be updated.
auto feeds = GetMediaFeedsSync(service());
auto items = GetItemsForMediaFeedSync(service(), feed_id);
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
EXPECT_TRUE(items.empty());
} else {
EXPECT_EQ(feed_id, feeds[0]->id);
EXPECT_TRUE(feeds[0]->last_fetch_time.has_value());
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
EXPECT_EQ(0, feeds[0]->fetch_failed_count);
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit.has_value());
EXPECT_EQ(kExpectedAltFetchItemCount, feeds[0]->last_fetch_item_count);
EXPECT_EQ(kExpectedAltFetchPlayNextCount,
feeds[0]->last_fetch_play_next_count);
EXPECT_EQ(kExpectedAltFetchContentTypes,
feeds[0]->last_fetch_content_types);
EXPECT_TRUE(feeds[0]->logos.empty());
EXPECT_EQ(kExpectedDisplayName, feeds[0]->display_name);
EXPECT_FALSE(feeds[0]->last_display_time.has_value());
EXPECT_EQ(GetAltExpectedItems(4), items);
EXPECT_EQ(last_fetch_time_not_cache_hit,
feeds[0]->last_fetch_time_not_cache_hit);
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
}
service()->UpdateMediaFeedDisplayTime(feed_id);
WaitForDB();
{
// The media feed should have a display time.
auto feeds = GetMediaFeedsSync(service());
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
EXPECT_EQ(feed_id, feeds[0]->id);
EXPECT_TRUE(feeds[0]->last_display_time.has_value());
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_StoreMediaFeedFetchResult_WithEmpty \
DISABLED_StoreMediaFeedFetchResult_WithEmpty
#else
#define MAYBE_StoreMediaFeedFetchResult_WithEmpty \
StoreMediaFeedFetchResult_WithEmpty
#endif
TEST_P(MediaHistoryStoreFeedsTest, MAYBE_StoreMediaFeedFetchResult_WithEmpty) {
DiscoverMediaFeed(GURL("https://www.google.com/feed"));
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
service()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(feed_id, GetExpectedItems()),
base::DoNothing());
WaitForDB();
{
// The media items should be stored.
auto items = GetItemsForMediaFeedSync(service(), feed_id);
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
EXPECT_EQ(GetExpectedItems(), items);
}
// The OTR service should have the same data.
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
}
service()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(
feed_id, std::vector<media_feeds::mojom::MediaFeedItemPtr>()),
base::DoNothing());
WaitForDB();
{
// There should be no items stored.
auto items = GetItemsForMediaFeedSync(service(), feed_id);
EXPECT_TRUE(items.empty());
// The OTR service should have the same data.
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_StoreMediaFeedFetchResult_MultipleFeeds \
DISABLED_StoreMediaFeedFetchResult_MultipleFeeds
#else
#define MAYBE_StoreMediaFeedFetchResult_MultipleFeeds \
StoreMediaFeedFetchResult_MultipleFeeds
#endif
TEST_P(MediaHistoryStoreFeedsTest,
MAYBE_StoreMediaFeedFetchResult_MultipleFeeds) {
const GURL feed_a_url("https://www.google.com/feed");
const GURL feed_b_url("https://www.google.co.uk/feed");
DiscoverMediaFeed(feed_a_url);
DiscoverMediaFeed(feed_b_url);
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id_a = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
const int feed_id_b = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[1]->id;
service()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(feed_id_a, GetExpectedItems()),
base::DoNothing());
WaitForDB();
service()->StoreMediaFeedFetchResult(
ResultWithItems(feed_id_b, GetAltExpectedItems(),
media_feeds::mojom::FetchResult::kFailedNetworkError),
base::DoNothing());
WaitForDB();
{
// Check the feeds were updated.
auto feeds = GetMediaFeedsSync(service());
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
EXPECT_EQ(2u, feeds.size());
EXPECT_EQ(feed_id_a, feeds[0]->id);
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
EXPECT_EQ(0, feeds[0]->fetch_failed_count);
EXPECT_EQ(feed_id_b, feeds[1]->id);
EXPECT_EQ(media_feeds::mojom::FetchResult::kFailedNetworkError,
feeds[1]->last_fetch_result);
EXPECT_EQ(1, feeds[1]->fetch_failed_count);
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
}
{
// The media items should be stored.
auto items = GetItemsForMediaFeedSync(service(), feed_id_a);
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
EXPECT_EQ(GetExpectedItems(), items);
}
// The OTR service should have the same data.
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id_a));
}
{
// The media items should be stored.
auto items = GetItemsForMediaFeedSync(service(), feed_id_b);
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
EXPECT_EQ(GetAltExpectedItems(3), items);
}
// The OTR service should have the same data.
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id_b));
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_RediscoverMediaFeed DISABLED_RediscoverMediaFeed
#else
#define MAYBE_RediscoverMediaFeed RediscoverMediaFeed
#endif
TEST_P(MediaHistoryStoreFeedsTest, MAYBE_RediscoverMediaFeed) {
GURL feed_url("https://www.google.com/feed");
DiscoverMediaFeed(feed_url);
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
int feed_id = -1;
base::Time feed_last_time;
if (!IsReadOnly()) {
auto feeds = GetMediaFeedsSync(service());
feed_id = feeds[0]->id;
feed_last_time = feeds[0]->last_discovery_time;
EXPECT_LT(base::Time(), feed_last_time);
EXPECT_GT(base::Time::Now(), feed_last_time);
EXPECT_EQ(feed_url, feeds[0]->url);
}
service()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(feed_id, GetExpectedItems()),
base::DoNothing());
WaitForDB();
{
// The media items should be stored.
auto items = GetItemsForMediaFeedSync(service(), feed_id);
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
EXPECT_EQ(GetExpectedItems(), items);
}
// The OTR service should have the same data.
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
}
// Rediscovering the same feed should not replace the feed.
DiscoverMediaFeed(feed_url);
WaitForDB();
if (!IsReadOnly()) {
auto feeds = GetMediaFeedsSync(service());
EXPECT_LE(feed_last_time, feeds[0]->last_discovery_time);
EXPECT_EQ(feed_id, feeds[0]->id);
EXPECT_EQ(feed_url, feeds[0]->url);
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
}
{
// The media items should be stored.
auto items = GetItemsForMediaFeedSync(service(), feed_id);
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
EXPECT_EQ(GetExpectedItems(), items);
}
// The OTR service should have the same data.
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
}
// Finding a new URL should replace the feed.
GURL new_url("https://www.google.com/feed2");
DiscoverMediaFeed(new_url);
WaitForDB();
if (!IsReadOnly()) {
auto feeds = GetMediaFeedsSync(service());
EXPECT_LE(feed_last_time, feeds[0]->last_discovery_time);
EXPECT_LT(feed_id, feeds[0]->id);
EXPECT_EQ(new_url, feeds[0]->url);
EXPECT_EQ(media_feeds::mojom::FetchResult::kNone,
feeds[0]->last_fetch_result);
}
{
// The media items should be deleted.
auto items = GetItemsForMediaFeedSync(service(), feed_id);
EXPECT_TRUE(items.empty());
// The OTR service should have the same data.
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_StoreMediaFeedFetchResult_IncreaseFailed \
DISABLED_StoreMediaFeedFetchResult_IncreaseFailed
#else
#define MAYBE_StoreMediaFeedFetchResult_IncreaseFailed \
StoreMediaFeedFetchResult_IncreaseFailed
#endif
TEST_P(MediaHistoryStoreFeedsTest,
MAYBE_StoreMediaFeedFetchResult_IncreaseFailed) {
DiscoverMediaFeed(GURL("https://www.google.com/feed"));
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
{
auto result =
ResultWithItems(feed_id, GetExpectedItems(),
media_feeds::mojom::FetchResult::kFailedNetworkError);
result.logos = GetExpectedLogos();
result.display_name = kExpectedDisplayName;
service()->StoreMediaFeedFetchResult(std::move(result), base::DoNothing());
WaitForDB();
// The fetch failed count should have been increased.
auto feeds = GetMediaFeedsSync(service());
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
EXPECT_EQ(feed_id, feeds[0]->id);
EXPECT_EQ(media_feeds::mojom::FetchResult::kFailedNetworkError,
feeds[0]->last_fetch_result);
EXPECT_EQ(1, feeds[0]->fetch_failed_count);
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
}
{
auto result =
ResultWithItems(feed_id, GetExpectedItems(),
media_feeds::mojom::FetchResult::kFailedBackendError);
result.logos = GetExpectedLogos();
result.display_name = kExpectedDisplayName;
service()->StoreMediaFeedFetchResult(std::move(result), base::DoNothing());
WaitForDB();
// The fetch failed count should have been increased.
auto feeds = GetMediaFeedsSync(service());
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
EXPECT_EQ(feed_id, feeds[0]->id);
EXPECT_EQ(media_feeds::mojom::FetchResult::kFailedBackendError,
feeds[0]->last_fetch_result);
EXPECT_EQ(2, feeds[0]->fetch_failed_count);
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
}
{
auto result = SuccessfulResultWithItems(feed_id, GetExpectedItems());
result.logos = GetExpectedLogos();
result.display_name = kExpectedDisplayName;
service()->StoreMediaFeedFetchResult(std::move(result), base::DoNothing());
WaitForDB();
// The fetch failed count should have been reset.
auto feeds = GetMediaFeedsSync(service());
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
EXPECT_EQ(feed_id, feeds[0]->id);
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
EXPECT_EQ(0, feeds[0]->fetch_failed_count);
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_StoreMediaFeedFetchResult_CheckLogoMax \
DISABLED_StoreMediaFeedFetchResult_CheckLogoMax
#else
#define MAYBE_StoreMediaFeedFetchResult_CheckLogoMax \
StoreMediaFeedFetchResult_CheckLogoMax
#endif
TEST_P(MediaHistoryStoreFeedsTest,
MAYBE_StoreMediaFeedFetchResult_CheckLogoMax) {
DiscoverMediaFeed(GURL("https://www.google.com/feed"));
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
std::vector<media_feeds::mojom::MediaImagePtr> logos;
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image1.png");
logos.push_back(std::move(image));
}
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image2.png");
logos.push_back(std::move(image));
}
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image3.png");
logos.push_back(std::move(image));
}
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image4.png");
logos.push_back(std::move(image));
}
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image5.png");
logos.push_back(std::move(image));
}
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image6.png");
logos.push_back(std::move(image));
}
auto result =
ResultWithItems(feed_id, GetExpectedItems(),
media_feeds::mojom::FetchResult::kFailedNetworkError);
result.logos = std::move(logos);
result.display_name = kExpectedDisplayName;
service()->StoreMediaFeedFetchResult(std::move(result), base::DoNothing());
WaitForDB();
{
// The feed should have at most 5 logos.
auto feeds = GetMediaFeedsSync(service());
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
EXPECT_EQ(feed_id, feeds[0]->id);
EXPECT_EQ(5u, feeds[0]->logos.size());
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_StoreMediaFeedFetchResult_CheckImageMax \
DISABLED_StoreMediaFeedFetchResult_CheckImageMax
#else
#define MAYBE_StoreMediaFeedFetchResult_CheckImageMax \
StoreMediaFeedFetchResult_CheckImageMax
#endif
TEST_P(MediaHistoryStoreFeedsTest,
MAYBE_StoreMediaFeedFetchResult_CheckImageMax) {
DiscoverMediaFeed(GURL("https://www.google.com/feed"));
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
auto item = media_feeds::mojom::MediaFeedItem::New();
item->name = base::ASCIIToUTF16("The Movie");
item->type = media_feeds::mojom::MediaFeedItemType::kMovie;
item->safe_search_result = media_feeds::mojom::SafeSearchResult::kUnknown;
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image1.png");
item->images.push_back(std::move(image));
}
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image2.png");
item->images.push_back(std::move(image));
}
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image3.png");
item->images.push_back(std::move(image));
}
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image4.png");
item->images.push_back(std::move(image));
}
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image5.png");
item->images.push_back(std::move(image));
}
{
media_feeds::mojom::MediaImagePtr image =
media_feeds::mojom::MediaImage::New();
image->src = GURL("https://www.example.org/image6.png");
item->images.push_back(std::move(image));
}
std::vector<media_feeds::mojom::MediaFeedItemPtr> items;
items.push_back(std::move(item));
auto result = SuccessfulResultWithItems(feed_id, std::move(items));
result.logos = GetExpectedLogos();
result.display_name = kExpectedDisplayName;
service()->StoreMediaFeedFetchResult(std::move(result), base::DoNothing());
WaitForDB();
{
// The item should have at most 5 images.
auto items = GetItemsForMediaFeedSync(service(), feed_id);
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
EXPECT_EQ(5u, items[0]->images.size());
}
// The OTR service should have the same data.
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_StoreMediaFeedFetchResult_DefaultSafeSearchResult \
DISABLED_StoreMediaFeedFetchResult_DefaultSafeSearchResult
#else
#define MAYBE_StoreMediaFeedFetchResult_DefaultSafeSearchResult \
StoreMediaFeedFetchResult_DefaultSafeSearchResult
#endif
TEST_P(MediaHistoryStoreFeedsTest,
MAYBE_StoreMediaFeedFetchResult_DefaultSafeSearchResult) {
DiscoverMediaFeed(GURL("https://www.google.com/feed"));
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
auto item = media_feeds::mojom::MediaFeedItem::New();
item->name = base::ASCIIToUTF16("The Movie");
item->type = media_feeds::mojom::MediaFeedItemType::kMovie;
std::vector<media_feeds::mojom::MediaFeedItemPtr> items;
items.push_back(std::move(item));
auto result = SuccessfulResultWithItems(feed_id, GetExpectedItems());
result.logos = GetExpectedLogos();
result.display_name = kExpectedDisplayName;
service()->StoreMediaFeedFetchResult(std::move(result), base::DoNothing());
WaitForDB();
{
// The item should set a default safe search result.
auto items = GetItemsForMediaFeedSync(service(), feed_id);
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnknown,
items[0]->safe_search_result);
}
// The OTR service should have the same data.
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_SafeSearchCheck DISABLED_SafeSearchCheck
#else
#define MAYBE_SafeSearchCheck SafeSearchCheck
#endif
TEST_P(MediaHistoryStoreFeedsTest, MAYBE_SafeSearchCheck) {
const GURL feed_url_a("https://www.google.com/feed");
const GURL feed_url_b("https://www.google.co.uk/feed");
DiscoverMediaFeed(feed_url_a);
DiscoverMediaFeed(feed_url_b);
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id_a = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
const int feed_id_b = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[1]->id;
media_history::MediaHistoryKeyedService::MediaFeedFetchResult result_a;
result_a.feed_id = feed_id_a;
result_a.status = media_feeds::mojom::FetchResult::kSuccess;
result_a.items = GetExpectedItems();
service()->StoreMediaFeedFetchResult(std::move(result_a), base::DoNothing());
WaitForDB();
media_history::MediaHistoryKeyedService::MediaFeedFetchResult result_b;
result_b.feed_id = feed_id_b;
result_b.status = media_feeds::mojom::FetchResult::kSuccess;
result_b.items = GetAltExpectedItems();
service()->StoreMediaFeedFetchResult(std::move(result_b), base::DoNothing());
WaitForDB();
std::map<media_history::MediaHistoryKeyedService::SafeSearchID,
media_feeds::mojom::SafeSearchResult>
found_ids;
std::map<media_history::MediaHistoryKeyedService::SafeSearchID,
media_feeds::mojom::SafeSearchResult>
found_ids_unsafe;
{
// Media items from all feeds should be in the pending items list.
auto pending_items = GetPendingSafeSearchCheckMediaFeedItemsSync(service());
auto feeds = GetMediaFeedsSync(
service(), MediaHistoryKeyedService::GetMediaFeedsRequest());
if (IsReadOnly()) {
EXPECT_TRUE(pending_items.empty());
EXPECT_TRUE(feeds.empty());
} else {
ASSERT_EQ(2u, feeds.size());
EXPECT_EQ(4u, pending_items.size());
std::set<GURL> found_urls;
for (auto& item : pending_items) {
EXPECT_NE(0, item->id.second);
found_ids.emplace(item->id,
media_feeds::mojom::SafeSearchResult::kSafe);
found_ids_unsafe.emplace(item->id,
media_feeds::mojom::SafeSearchResult::kUnsafe);
for (auto& url : item->urls) {
found_urls.insert(url);
}
}
std::set<GURL> expected_urls;
expected_urls.insert(GURL("https://www.example.com/action"));
expected_urls.insert(GURL("https://www.example.com/next"));
expected_urls.insert(GURL("https://www.example.com/action-alt"));
expected_urls.insert(feed_url_a);
expected_urls.insert(feed_url_b);
EXPECT_EQ(expected_urls, found_urls);
EXPECT_EQ(1, feeds[0]->last_fetch_safe_item_count);
EXPECT_EQ(0, feeds[1]->last_fetch_safe_item_count);
}
}
service()->StoreMediaFeedItemSafeSearchResults(found_ids);
WaitForDB();
{
// The pending item list should be empty and the safe counters should be
// set.
EXPECT_TRUE(GetPendingSafeSearchCheckMediaFeedItemsSync(service()).empty());
auto feeds = GetMediaFeedsSync(
service(), MediaHistoryKeyedService::GetMediaFeedsRequest());
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
ASSERT_EQ(2u, feeds.size());
EXPECT_EQ(feed_id_a, feeds[0]->id);
EXPECT_EQ(2, feeds[0]->last_fetch_safe_item_count);
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kSafe,
feeds[0]->safe_search_result);
EXPECT_EQ(feed_id_b, feeds[1]->id);
EXPECT_EQ(1, feeds[1]->last_fetch_safe_item_count);
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kSafe,
feeds[1]->safe_search_result);
}
}
service()->StoreMediaFeedItemSafeSearchResults(found_ids_unsafe);
WaitForDB();
{
auto feeds = GetMediaFeedsSync(
service(), MediaHistoryKeyedService::GetMediaFeedsRequest());
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
ASSERT_EQ(2u, feeds.size());
EXPECT_EQ(feed_id_a, feeds[0]->id);
EXPECT_EQ(1, feeds[0]->last_fetch_safe_item_count);
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnsafe,
feeds[0]->safe_search_result);
EXPECT_EQ(feed_id_b, feeds[1]->id);
EXPECT_EQ(0, feeds[1]->last_fetch_safe_item_count);
EXPECT_EQ(media_feeds::mojom::SafeSearchResult::kUnsafe,
feeds[1]->safe_search_result);
}
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_GetMediaFeedsSortByWatchtimePercentile \
DISABLED_GetMediaFeedsSortByWatchtimePercentile
#else
#define MAYBE_GetMediaFeedsSortByWatchtimePercentile \
GetMediaFeedsSortByWatchtimePercentile
#endif
TEST_P(MediaHistoryStoreFeedsTest,
MAYBE_GetMediaFeedsSortByWatchtimePercentile) {
// We add 111 origins with watchtime and feeds for all but one of these. Half
// of the feeds will have items.
const unsigned kNumberOfOrigins = 111;
const unsigned kNumberOfFeeds = 110;
// The starting percentile always has one percentage value taken off. This
// is because we have one extra origin with the highest watchtime that does
// not have a feed.
const double kPercentageValue = 100.0 / kNumberOfOrigins;
const double kStartingPercentile = 100 - kPercentageValue;
// Generate a bunch of media feeds.
std::set<GURL> feeds;
for (unsigned i = 0; i < kNumberOfOrigins; i++) {
GURL url(base::StringPrintf("https://www.google%i.com/feed", i));
feeds.insert(url);
// Each origin will have a ascending amount of watchtime from 0 to
// |kNumberOfOrigins|.
auto watchtime = base::TimeDelta::FromMinutes(i);
content::MediaPlayerWatchTime watch_time(url, url.GetOrigin(), watchtime,
base::TimeDelta(), true, true);
service()->SavePlayback(watch_time);
if (i < kNumberOfFeeds) {
DiscoverMediaFeed(url);
if (i == 0) {
service()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(i + 1, GetExpectedItems()),
base::DoNothing());
} else if (i % 2 == 0) {
service()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(i + 1, GetAltExpectedItems()),
base::DoNothing());
}
}
}
WaitForDB();
{
// Check the feeds and origins were stored.
auto feeds = GetMediaFeedsSync(service());
auto origins = GetOriginRowsSync(service());
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
EXPECT_TRUE(origins.empty());
} else {
EXPECT_EQ(kNumberOfFeeds, feeds.size());
EXPECT_EQ(kNumberOfOrigins, origins.size());
int i = 0;
for (auto& origin : origins) {
auto watchtime = base::TimeDelta::FromMinutes(i);
EXPECT_EQ(watchtime, origin->cached_audio_video_watchtime);
EXPECT_EQ(watchtime, origin->actual_audio_video_watchtime);
i++;
}
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
EXPECT_EQ(origins, GetOriginRowsSync(otr_service()));
}
{
// Check the media feed sorting by works for top feeds for fetch.
auto feeds = GetMediaFeedsSync(
service(),
MediaHistoryKeyedService::GetMediaFeedsRequest::CreateTopFeedsForFetch(
kNumberOfFeeds, base::TimeDelta()));
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
EXPECT_EQ(kNumberOfFeeds, feeds.size());
unsigned count = kNumberOfFeeds;
double percentile = kStartingPercentile;
double last_percentile = 101.0;
for (auto& feed : feeds) {
GURL url(
base::StringPrintf("https://www.google%i.com/feed", count - 1));
EXPECT_EQ(count, feed->id);
EXPECT_EQ(url, feed->url);
EXPECT_NEAR(percentile, feed->origin_audio_video_watchtime_percentile,
1);
EXPECT_GT(last_percentile,
feed->origin_audio_video_watchtime_percentile);
last_percentile = feed->origin_audio_video_watchtime_percentile;
percentile = percentile - kPercentageValue;
count--;
}
}
}
{
// Check the media feed sorting works for top feeds with a limit.
auto feeds = GetMediaFeedsSync(
service(),
MediaHistoryKeyedService::GetMediaFeedsRequest::CreateTopFeedsForFetch(
10, base::TimeDelta()));
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
EXPECT_EQ(10u, feeds.size());
unsigned count = kNumberOfFeeds;
double percentile = kStartingPercentile;
double last_percentile = 101.0;
for (auto& feed : feeds) {
GURL url(
base::StringPrintf("https://www.google%i.com/feed", count - 1));
EXPECT_EQ(count, feed->id);
EXPECT_EQ(url, feed->url);
EXPECT_NEAR(percentile, feed->origin_audio_video_watchtime_percentile,
1);
EXPECT_GT(last_percentile,
feed->origin_audio_video_watchtime_percentile);
last_percentile = feed->origin_audio_video_watchtime_percentile;
percentile = percentile - kPercentageValue;
count--;
}
}
}
{
// Check the media feed sorting works for top feeds with a minimum watchtime
// requirement and ranking.
auto feeds = GetMediaFeedsSync(
service(),
MediaHistoryKeyedService::GetMediaFeedsRequest::CreateTopFeedsForFetch(
kNumberOfFeeds, base::TimeDelta::FromMinutes(30)));
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
EXPECT_EQ(80u, feeds.size());
unsigned count = kNumberOfFeeds;
double percentile = kStartingPercentile;
double last_percentile = 101.0;
for (auto& feed : feeds) {
GURL url(
base::StringPrintf("https://www.google%i.com/feed", count - 1));
EXPECT_EQ(count, feed->id);
EXPECT_EQ(url, feed->url);
EXPECT_NEAR(percentile, feed->origin_audio_video_watchtime_percentile,
1);
EXPECT_GT(last_percentile,
feed->origin_audio_video_watchtime_percentile);
last_percentile = feed->origin_audio_video_watchtime_percentile;
percentile = percentile - kPercentageValue;
count--;
}
}
}
{
// Check the media feed sorting works for top feeds with a minimum watchtime
// requirement, ranking and limit.
auto feeds = GetMediaFeedsSync(
service(),
MediaHistoryKeyedService::GetMediaFeedsRequest::CreateTopFeedsForFetch(
10, base::TimeDelta::FromMinutes(30)));
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
EXPECT_EQ(10u, feeds.size());
unsigned count = kNumberOfFeeds;
double percentile = kStartingPercentile;
double last_percentile = 101.0;
for (auto& feed : feeds) {
GURL url(
base::StringPrintf("https://www.google%i.com/feed", count - 1));
EXPECT_EQ(count, feed->id);
EXPECT_EQ(url, feed->url);
EXPECT_NEAR(percentile, feed->origin_audio_video_watchtime_percentile,
1);
EXPECT_GT(last_percentile,
feed->origin_audio_video_watchtime_percentile);
last_percentile = feed->origin_audio_video_watchtime_percentile;
percentile = percentile - kPercentageValue;
count--;
}
}
}
{
// Check the media feed fetched items for display works.
auto feeds = GetMediaFeedsSync(
service(), MediaHistoryKeyedService::GetMediaFeedsRequest::
CreateTopFeedsForDisplay(kNumberOfFeeds, 1, false));
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
EXPECT_EQ(55u, feeds.size());
unsigned count = kNumberOfFeeds;
double percentile = kStartingPercentile;
double last_percentile = 101.0;
for (auto& feed : feeds) {
const int i = count - 1;
if (i % 2 != 0)
continue;
GURL url(base::StringPrintf("https://www.google%i.com/feed", i));
EXPECT_EQ(count, feed->id);
EXPECT_EQ(url, feed->url);
EXPECT_NEAR(percentile, feed->origin_audio_video_watchtime_percentile,
1);
EXPECT_GT(last_percentile,
feed->origin_audio_video_watchtime_percentile);
last_percentile = feed->origin_audio_video_watchtime_percentile;
percentile = percentile - kPercentageValue;
count--;
}
}
}
{
// Check the media feed fetched items for display works for safe search.
auto feeds = GetMediaFeedsSync(
service(), MediaHistoryKeyedService::GetMediaFeedsRequest::
CreateTopFeedsForDisplay(kNumberOfFeeds, 1, true));
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
} else {
ASSERT_EQ(1u, feeds.size());
EXPECT_EQ(1, feeds[0]->id);
}
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_FeedItemsClickAndShown DISABLED_FeedItemsClickAndShown
#else
#define MAYBE_FeedItemsClickAndShown FeedItemsClickAndShown
#endif
TEST_P(MediaHistoryStoreFeedsTest, MAYBE_FeedItemsClickAndShown) {
DiscoverMediaFeed(GURL("https://www.google.com/feed"));
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
auto result = SuccessfulResultWithItems(feed_id, GetExpectedItems());
result.logos = GetExpectedLogos();
result.display_name = kExpectedDisplayName;
service()->StoreMediaFeedFetchResult(std::move(result), base::DoNothing());
WaitForDB();
{
// The media items should be stored.
auto items = GetItemsForMediaFeedSync(service(), feed_id);
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
EXPECT_EQ(3u, items[0]->shown_count);
EXPECT_TRUE(items[0]->clicked);
EXPECT_EQ(0u, items[1]->shown_count);
EXPECT_FALSE(items[1]->clicked);
EXPECT_EQ(0u, items[2]->shown_count);
EXPECT_FALSE(items[2]->clicked);
}
// The OTR service should have the same data.
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
}
std::set<int64_t> ids;
ids.insert(1);
ids.insert(2);
// Increment the shown count.
service()->IncrementMediaFeedItemsShownCount(ids);
WaitForDB();
{
// The media items should have been incremented.
auto items = GetItemsForMediaFeedSync(service(), feed_id);
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
EXPECT_EQ(4u, items[0]->shown_count);
EXPECT_TRUE(items[0]->clicked);
EXPECT_EQ(1u, items[1]->shown_count);
EXPECT_FALSE(items[1]->clicked);
EXPECT_EQ(0u, items[2]->shown_count);
EXPECT_FALSE(items[2]->clicked);
}
// The OTR service should have the same data.
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
}
// Increment the shown count.
service()->IncrementMediaFeedItemsShownCount(ids);
WaitForDB();
{
// The media items should have been incremented.
auto items = GetItemsForMediaFeedSync(service(), feed_id);
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
EXPECT_EQ(5u, items[0]->shown_count);
EXPECT_TRUE(items[0]->clicked);
EXPECT_EQ(2u, items[1]->shown_count);
EXPECT_FALSE(items[1]->clicked);
EXPECT_EQ(0u, items[2]->shown_count);
EXPECT_FALSE(items[2]->clicked);
}
// The OTR service should have the same data.
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
}
// Mark the item as clicked.
service()->MarkMediaFeedItemAsClicked(2);
WaitForDB();
{
// The media item should have been clicked.
auto items = GetItemsForMediaFeedSync(service(), feed_id);
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
EXPECT_EQ(5u, items[0]->shown_count);
EXPECT_TRUE(items[0]->clicked);
EXPECT_EQ(2u, items[1]->shown_count);
EXPECT_TRUE(items[1]->clicked);
EXPECT_EQ(0u, items[2]->shown_count);
EXPECT_FALSE(items[2]->clicked);
}
// The OTR service should have the same data.
EXPECT_EQ(items, GetItemsForMediaFeedSync(otr_service(), feed_id));
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_ResetMediaFeed DISABLED_ResetMediaFeed
#else
#define MAYBE_ResetMediaFeed ResetMediaFeed
#endif
TEST_P(MediaHistoryStoreFeedsTest, MAYBE_ResetMediaFeed) {
const GURL feed_url_a("https://www.google.com/feed");
const GURL feed_url_b("https://www.google.co.uk/feed");
DiscoverMediaFeed(feed_url_a);
DiscoverMediaFeed(feed_url_b);
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id_a = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
const int feed_id_b = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[1]->id;
auto result = SuccessfulResultWithItems(feed_id_a, GetExpectedItems());
result.logos = GetExpectedLogos();
result.display_name = kExpectedDisplayName;
result.user_identifier = GetExpectedUserIdentifier();
service()->StoreMediaFeedFetchResult(std::move(result), base::DoNothing());
service()->UpdateMediaFeedDisplayTime(feed_id_a);
auto alt_result = SuccessfulResultWithItems(feed_id_b, GetAltExpectedItems());
alt_result.logos = GetExpectedLogos();
alt_result.display_name = kExpectedDisplayName;
service()->StoreMediaFeedFetchResult(std::move(alt_result),
base::DoNothing());
service()->UpdateMediaFeedDisplayTime(feed_id_b);
WaitForDB();
{
// The media items should be stored and the feed should be updated.
auto feeds = GetMediaFeedsSync(service());
auto items_a = GetItemsForMediaFeedSync(service(), feed_id_a);
auto items_b = GetItemsForMediaFeedSync(service(), feed_id_b);
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
EXPECT_TRUE(items_a.empty());
EXPECT_TRUE(items_b.empty());
} else {
EXPECT_EQ(feed_id_a, feeds[0]->id);
EXPECT_FALSE(feeds[0]->last_discovery_time.is_null());
EXPECT_TRUE(feeds[0]->last_fetch_time.has_value());
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
EXPECT_EQ(0, feeds[0]->fetch_failed_count);
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit.has_value());
EXPECT_EQ(kExpectedFetchItemCount, feeds[0]->last_fetch_item_count);
EXPECT_EQ(kExpectedFetchPlayNextCount,
feeds[0]->last_fetch_play_next_count);
EXPECT_EQ(kExpectedFetchContentTypes, feeds[0]->last_fetch_content_types);
EXPECT_EQ(GetExpectedLogos(), feeds[0]->logos);
EXPECT_EQ(kExpectedDisplayName, feeds[0]->display_name);
EXPECT_TRUE(feeds[0]->last_display_time.has_value());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
EXPECT_EQ(GetExpectedUserIdentifier(), feeds[0]->user_identifier);
EXPECT_EQ(feed_id_b, feeds[1]->id);
EXPECT_EQ(GetExpectedItems(), items_a);
EXPECT_EQ(GetAltExpectedItems(3), items_b);
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
EXPECT_EQ(items_a, GetItemsForMediaFeedSync(otr_service(), feed_id_a));
EXPECT_EQ(items_b, GetItemsForMediaFeedSync(otr_service(), feed_id_b));
}
if (auto* service = GetMediaFeedsService()) {
service->ResetMediaFeed(url::Origin::Create(feed_url_a),
media_feeds::mojom::ResetReason::kCookies);
WaitForDB();
}
{
// The feed should have been reset.
auto feeds = GetMediaFeedsSync(service());
auto items_a = GetItemsForMediaFeedSync(service(), feed_id_a);
auto items_b = GetItemsForMediaFeedSync(service(), feed_id_b);
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
EXPECT_TRUE(items_a.empty());
EXPECT_TRUE(items_b.empty());
} else {
EXPECT_EQ(feed_id_a, feeds[0]->id);
EXPECT_FALSE(feeds[0]->last_discovery_time.is_null());
EXPECT_FALSE(feeds[0]->last_fetch_time.has_value());
EXPECT_EQ(media_feeds::mojom::FetchResult::kNone,
feeds[0]->last_fetch_result);
EXPECT_EQ(0, feeds[0]->fetch_failed_count);
EXPECT_FALSE(feeds[0]->last_fetch_time_not_cache_hit.has_value());
EXPECT_EQ(0, feeds[0]->last_fetch_item_count);
EXPECT_EQ(0, feeds[0]->last_fetch_play_next_count);
EXPECT_EQ(0, feeds[0]->last_fetch_content_types);
EXPECT_TRUE(feeds[0]->logos.empty());
EXPECT_TRUE(feeds[0]->display_name.empty());
EXPECT_TRUE(feeds[0]->last_display_time.has_value());
EXPECT_EQ(media_feeds::mojom::ResetReason::kCookies,
feeds[0]->reset_reason);
EXPECT_FALSE(feeds[0]->user_identifier);
EXPECT_EQ(feed_id_b, feeds[1]->id);
EXPECT_TRUE(items_a.empty());
EXPECT_EQ(GetAltExpectedItems(3), items_b);
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
EXPECT_EQ(items_a, GetItemsForMediaFeedSync(otr_service(), feed_id_a));
EXPECT_EQ(items_b, GetItemsForMediaFeedSync(otr_service(), feed_id_b));
}
{
auto result = SuccessfulResultWithItems(feed_id_a, GetExpectedItems());
result.logos = GetExpectedLogos();
result.display_name = kExpectedDisplayName;
service()->StoreMediaFeedFetchResult(std::move(result), base::DoNothing());
service()->UpdateMediaFeedDisplayTime(feed_id_a);
WaitForDB();
// The media items and feed should be repopulated.
auto feeds = GetMediaFeedsSync(service());
auto items_a = GetItemsForMediaFeedSync(service(), feed_id_a);
auto items_b = GetItemsForMediaFeedSync(service(), feed_id_b);
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
EXPECT_TRUE(items_a.empty());
EXPECT_TRUE(items_b.empty());
} else {
EXPECT_EQ(feed_id_a, feeds[0]->id);
EXPECT_FALSE(feeds[0]->last_discovery_time.is_null());
EXPECT_TRUE(feeds[0]->last_fetch_time.has_value());
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
EXPECT_EQ(0, feeds[0]->fetch_failed_count);
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit.has_value());
EXPECT_EQ(kExpectedFetchItemCount, feeds[0]->last_fetch_item_count);
EXPECT_EQ(kExpectedFetchPlayNextCount,
feeds[0]->last_fetch_play_next_count);
EXPECT_EQ(kExpectedFetchContentTypes, feeds[0]->last_fetch_content_types);
EXPECT_EQ(GetExpectedLogos(), feeds[0]->logos);
EXPECT_EQ(kExpectedDisplayName, feeds[0]->display_name);
EXPECT_TRUE(feeds[0]->last_display_time.has_value());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
EXPECT_EQ(feed_id_b, feeds[1]->id);
EXPECT_EQ(GetExpectedItems(4), items_a);
EXPECT_EQ(GetAltExpectedItems(3), items_b);
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
EXPECT_EQ(items_a, GetItemsForMediaFeedSync(otr_service(), feed_id_a));
EXPECT_EQ(items_b, GetItemsForMediaFeedSync(otr_service(), feed_id_b));
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_ResetMediaFeedDueToCacheClearing \
DISABLED_ResetMediaFeedDueToCacheClearing
#else
#define MAYBE_ResetMediaFeedDueToCacheClearing ResetMediaFeedDueToCacheClearing
#endif
TEST_P(MediaHistoryStoreFeedsTest, MAYBE_ResetMediaFeedDueToCacheClearing) {
const GURL feed_url_a("https://www.google.com/feed");
const GURL feed_url_b("https://www.google.co.uk/feed");
DiscoverMediaFeed(feed_url_a);
DiscoverMediaFeed(feed_url_b);
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id_a = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
const int feed_id_b = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[1]->id;
auto result = SuccessfulResultWithItems(feed_id_a, GetExpectedItems());
result.logos = GetExpectedLogos();
result.display_name = kExpectedDisplayName;
service()->StoreMediaFeedFetchResult(std::move(result), base::DoNothing());
WaitForDB();
auto alt_result = SuccessfulResultWithItems(feed_id_b, GetAltExpectedItems());
alt_result.logos = GetExpectedLogos();
alt_result.display_name = kExpectedDisplayName;
service()->StoreMediaFeedFetchResult(std::move(alt_result),
base::DoNothing());
WaitForDB();
service()->UpdateMediaFeedDisplayTime(feed_id_a);
service()->UpdateMediaFeedDisplayTime(feed_id_b);
WaitForDB();
{
// The media items should be stored and the feed should be stored.
auto feeds = GetMediaFeedsSync(service());
auto items_a = GetItemsForMediaFeedSync(service(), feed_id_a);
auto items_b = GetItemsForMediaFeedSync(service(), feed_id_b);
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
EXPECT_TRUE(items_a.empty());
EXPECT_TRUE(items_b.empty());
} else {
ASSERT_EQ(2u, feeds.size());
for (auto& feed : feeds) {
EXPECT_FALSE(feed->last_discovery_time.is_null());
EXPECT_TRUE(feed->last_fetch_time.has_value());
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feed->last_fetch_result);
EXPECT_EQ(0, feed->fetch_failed_count);
EXPECT_TRUE(feed->last_fetch_time_not_cache_hit.has_value());
EXPECT_NE(0, feed->last_fetch_item_count);
EXPECT_NE(0, feed->last_fetch_play_next_count);
EXPECT_NE(0, feed->last_fetch_content_types);
EXPECT_EQ(GetExpectedLogos(), feed->logos);
EXPECT_EQ(kExpectedDisplayName, feed->display_name);
EXPECT_TRUE(feed->last_display_time.has_value());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feed->reset_reason);
}
EXPECT_EQ(GetExpectedItems(), items_a);
EXPECT_EQ(GetAltExpectedItems(3), items_b);
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
EXPECT_EQ(items_a, GetItemsForMediaFeedSync(otr_service(), feed_id_a));
EXPECT_EQ(items_b, GetItemsForMediaFeedSync(otr_service(), feed_id_b));
}
service()->ResetMediaFeedDueToCacheClearing(
base::Time(), base::Time::Now() - base::TimeDelta::FromDays(1),
base::BindRepeating([](const GURL& url) { return true; }),
base::DoNothing());
WaitForDB();
{
// The media items should not have been affected since we cleared yesterday.
auto feeds = GetMediaFeedsSync(service());
auto items_a = GetItemsForMediaFeedSync(service(), feed_id_a);
auto items_b = GetItemsForMediaFeedSync(service(), feed_id_b);
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
EXPECT_TRUE(items_a.empty());
EXPECT_TRUE(items_b.empty());
} else {
ASSERT_EQ(2u, feeds.size());
for (auto& feed : feeds) {
EXPECT_FALSE(feed->last_discovery_time.is_null());
EXPECT_TRUE(feed->last_fetch_time.has_value());
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feed->last_fetch_result);
EXPECT_EQ(0, feed->fetch_failed_count);
EXPECT_TRUE(feed->last_fetch_time_not_cache_hit.has_value());
EXPECT_NE(0, feed->last_fetch_item_count);
EXPECT_NE(0, feed->last_fetch_play_next_count);
EXPECT_NE(0, feed->last_fetch_content_types);
EXPECT_EQ(GetExpectedLogos(), feed->logos);
EXPECT_EQ(kExpectedDisplayName, feed->display_name);
EXPECT_TRUE(feed->last_display_time.has_value());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feed->reset_reason);
}
EXPECT_EQ(GetExpectedItems(), items_a);
EXPECT_EQ(GetAltExpectedItems(3), items_b);
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
EXPECT_EQ(items_a, GetItemsForMediaFeedSync(otr_service(), feed_id_a));
EXPECT_EQ(items_b, GetItemsForMediaFeedSync(otr_service(), feed_id_b));
}
service()->ResetMediaFeedDueToCacheClearing(
base::Time(), base::Time::Max(),
base::BindRepeating([](const GURL& url) { return true; }),
base::DoNothing());
WaitForDB();
{
// The feeds should have been reset.
auto feeds = GetMediaFeedsSync(service());
auto items_a = GetItemsForMediaFeedSync(service(), feed_id_a);
auto items_b = GetItemsForMediaFeedSync(service(), feed_id_b);
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
EXPECT_TRUE(items_a.empty());
EXPECT_TRUE(items_b.empty());
} else {
ASSERT_EQ(2u, feeds.size());
for (auto& feed : feeds) {
EXPECT_FALSE(feed->last_discovery_time.is_null());
EXPECT_FALSE(feed->last_fetch_time.has_value());
EXPECT_EQ(media_feeds::mojom::FetchResult::kNone,
feed->last_fetch_result);
EXPECT_EQ(0, feed->fetch_failed_count);
EXPECT_FALSE(feed->last_fetch_time_not_cache_hit.has_value());
EXPECT_EQ(0, feed->last_fetch_item_count);
EXPECT_EQ(0, feed->last_fetch_play_next_count);
EXPECT_EQ(0, feed->last_fetch_content_types);
EXPECT_TRUE(feed->logos.empty());
EXPECT_TRUE(feed->display_name.empty());
EXPECT_TRUE(feed->last_display_time.has_value());
EXPECT_EQ(media_feeds::mojom::ResetReason::kCache, feed->reset_reason);
}
EXPECT_TRUE(items_a.empty());
EXPECT_TRUE(items_b.empty());
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
EXPECT_EQ(items_a, GetItemsForMediaFeedSync(otr_service(), feed_id_a));
EXPECT_EQ(items_b, GetItemsForMediaFeedSync(otr_service(), feed_id_b));
}
{
auto result = SuccessfulResultWithItems(feed_id_a, GetExpectedItems());
result.logos = GetExpectedLogos();
result.display_name = kExpectedDisplayName;
service()->StoreMediaFeedFetchResult(std::move(result), base::DoNothing());
service()->UpdateMediaFeedDisplayTime(feed_id_a);
WaitForDB();
// The media items and feed should be repopulated.
auto feeds = GetMediaFeedsSync(service());
auto items_a = GetItemsForMediaFeedSync(service(), feed_id_a);
auto items_b = GetItemsForMediaFeedSync(service(), feed_id_b);
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
EXPECT_TRUE(items_a.empty());
EXPECT_TRUE(items_b.empty());
} else {
EXPECT_EQ(feed_id_a, feeds[0]->id);
EXPECT_FALSE(feeds[0]->last_discovery_time.is_null());
EXPECT_TRUE(feeds[0]->last_fetch_time.has_value());
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
EXPECT_EQ(0, feeds[0]->fetch_failed_count);
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit.has_value());
EXPECT_EQ(kExpectedFetchItemCount, feeds[0]->last_fetch_item_count);
EXPECT_EQ(kExpectedFetchPlayNextCount,
feeds[0]->last_fetch_play_next_count);
EXPECT_EQ(kExpectedFetchContentTypes, feeds[0]->last_fetch_content_types);
EXPECT_EQ(GetExpectedLogos(), feeds[0]->logos);
EXPECT_EQ(kExpectedDisplayName, feeds[0]->display_name);
EXPECT_TRUE(feeds[0]->last_display_time.has_value());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
EXPECT_EQ(feed_id_b, feeds[1]->id);
EXPECT_EQ(GetExpectedItems(4), items_a);
EXPECT_TRUE(items_b.empty());
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
EXPECT_EQ(items_a, GetItemsForMediaFeedSync(otr_service(), feed_id_a));
EXPECT_EQ(items_b, GetItemsForMediaFeedSync(otr_service(), feed_id_b));
}
service()->ResetMediaFeedDueToCacheClearing(
base::Time(), base::Time::Max(),
base::BindRepeating([](const GURL& url) { return false; }),
base::DoNothing());
WaitForDB();
{
// The media items and feed should still be populated because the filter
// returned false.
auto feeds = GetMediaFeedsSync(service());
auto items_a = GetItemsForMediaFeedSync(service(), feed_id_a);
auto items_b = GetItemsForMediaFeedSync(service(), feed_id_b);
if (IsReadOnly()) {
EXPECT_TRUE(feeds.empty());
EXPECT_TRUE(items_a.empty());
EXPECT_TRUE(items_b.empty());
} else {
EXPECT_EQ(feed_id_a, feeds[0]->id);
EXPECT_FALSE(feeds[0]->last_discovery_time.is_null());
EXPECT_TRUE(feeds[0]->last_fetch_time.has_value());
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
feeds[0]->last_fetch_result);
EXPECT_EQ(0, feeds[0]->fetch_failed_count);
EXPECT_TRUE(feeds[0]->last_fetch_time_not_cache_hit.has_value());
EXPECT_EQ(kExpectedFetchItemCount, feeds[0]->last_fetch_item_count);
EXPECT_EQ(kExpectedFetchPlayNextCount,
feeds[0]->last_fetch_play_next_count);
EXPECT_EQ(kExpectedFetchContentTypes, feeds[0]->last_fetch_content_types);
EXPECT_EQ(GetExpectedLogos(), feeds[0]->logos);
EXPECT_EQ(kExpectedDisplayName, feeds[0]->display_name);
EXPECT_TRUE(feeds[0]->last_display_time.has_value());
EXPECT_EQ(media_feeds::mojom::ResetReason::kNone, feeds[0]->reset_reason);
EXPECT_EQ(feed_id_b, feeds[1]->id);
EXPECT_EQ(GetExpectedItems(4), items_a);
EXPECT_TRUE(items_b.empty());
}
// The OTR service should have the same data.
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
EXPECT_EQ(items_a, GetItemsForMediaFeedSync(otr_service(), feed_id_a));
EXPECT_EQ(items_b, GetItemsForMediaFeedSync(otr_service(), feed_id_b));
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_DeleteMediaFeed DISABLED_DeleteMediaFeed
#else
#define MAYBE_DeleteMediaFeed DeleteMediaFeed
#endif
TEST_P(MediaHistoryStoreFeedsTest, MAYBE_DeleteMediaFeed) {
DiscoverMediaFeed(GURL("https://www.google.com/feed"));
DiscoverMediaFeed(GURL("https://www.google.co.uk/feed"));
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id_a = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
const int feed_id_b = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[1]->id;
service()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(feed_id_a, GetExpectedItems()),
base::DoNothing());
WaitForDB();
service()->StoreMediaFeedFetchResult(
ResultWithItems(feed_id_b, GetAltExpectedItems(),
media_feeds::mojom::FetchResult::kFailedNetworkError),
base::DoNothing());
WaitForDB();
{
// Check the feed and items are stored.
auto stats = GetStatsSync(service());
auto feeds = GetMediaFeedsSync(service());
if (IsReadOnly()) {
EXPECT_EQ(0, stats->table_row_counts[MediaHistoryFeedsTable::kTableName]);
EXPECT_EQ(
0, stats->table_row_counts[MediaHistoryFeedItemsTable::kTableName]);
} else {
ASSERT_EQ(2, stats->table_row_counts[MediaHistoryFeedsTable::kTableName]);
EXPECT_EQ(
4, stats->table_row_counts[MediaHistoryFeedItemsTable::kTableName]);
EXPECT_EQ(feed_id_a, feeds[0]->id);
EXPECT_EQ(feed_id_b, feeds[1]->id);
}
// The OTR service should have the same data.
EXPECT_EQ(stats, GetStatsSync(otr_service()));
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
}
service()->DeleteMediaFeed(feed_id_a, base::DoNothing());
WaitForDB();
{
// Check the first feed was deleted.
auto stats = GetStatsSync(service());
auto feeds = GetMediaFeedsSync(service());
if (IsReadOnly()) {
EXPECT_EQ(0, stats->table_row_counts[MediaHistoryFeedsTable::kTableName]);
EXPECT_EQ(
0, stats->table_row_counts[MediaHistoryFeedItemsTable::kTableName]);
} else {
ASSERT_EQ(1, stats->table_row_counts[MediaHistoryFeedsTable::kTableName]);
EXPECT_EQ(
1, stats->table_row_counts[MediaHistoryFeedItemsTable::kTableName]);
EXPECT_EQ(feed_id_b, feeds[0]->id);
}
// The OTR service should have the same data.
EXPECT_EQ(stats, GetStatsSync(otr_service()));
EXPECT_EQ(feeds, GetMediaFeedsSync(otr_service()));
}
}
// TODO(crbug.com/1087974).
#if defined(THREAD_SANITIZER)
#define MAYBE_GetMediaFeedFetchDetails DISABLED_GetMediaFeedFetchDetails
#else
#define MAYBE_GetMediaFeedFetchDetails GetMediaFeedFetchDetails
#endif
TEST_P(MediaHistoryStoreFeedsTest, MAYBE_GetMediaFeedFetchDetails) {
const GURL feed_url("https://www.google.com/feed");
DiscoverMediaFeed(feed_url);
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
{
auto details = GetFetchDetailsSync(feed_id);
if (IsReadOnly()) {
EXPECT_FALSE(details.has_value());
} else {
EXPECT_FALSE(details->reset_token.has_value());
EXPECT_EQ(feed_url, details->url);
EXPECT_EQ(media_feeds::mojom::FetchResult::kNone,
details->last_fetch_result);
}
}
service()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(feed_id, GetExpectedItems()),
base::DoNothing());
WaitForDB();
{
auto details = GetFetchDetailsSync(feed_id);
if (IsReadOnly()) {
EXPECT_FALSE(details.has_value());
} else {
EXPECT_FALSE(details->reset_token.has_value());
EXPECT_EQ(feed_url, details->url);
EXPECT_EQ(media_feeds::mojom::FetchResult::kSuccess,
details->last_fetch_result);
}
}
if (auto* service = GetMediaFeedsService()) {
service->ResetMediaFeed(url::Origin::Create(feed_url),
media_feeds::mojom::ResetReason::kCookies);
WaitForDB();
}
base::Optional<base::UnguessableToken> token;
{
// The feed should have been reset and the token should have been generated.
auto details = GetFetchDetailsSync(feed_id);
if (IsReadOnly()) {
EXPECT_FALSE(details.has_value());
} else {
EXPECT_TRUE(details->reset_token.has_value());
EXPECT_EQ(feed_url, details->url);
EXPECT_EQ(media_feeds::mojom::FetchResult::kNone,
details->last_fetch_result);
token = details->reset_token;
}
}
if (auto* service = GetMediaFeedsService()) {
service->ResetMediaFeed(url::Origin::Create(feed_url),
media_feeds::mojom::ResetReason::kVisit);
WaitForDB();
}
{
// A new token should have been generated.
auto details = GetFetchDetailsSync(feed_id);
if (IsReadOnly()) {
EXPECT_FALSE(details.has_value());
} else {
EXPECT_TRUE(details->reset_token.has_value());
EXPECT_NE(token, details->reset_token);
EXPECT_EQ(feed_url, details->url);
EXPECT_EQ(media_feeds::mojom::FetchResult::kNone,
details->last_fetch_result);
}
}
}
TEST_P(MediaHistoryStoreFeedsTest, GetContinueWatching) {
const GURL feed_url("https://www.google.com/feed");
DiscoverMediaFeed(feed_url);
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
service()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(feed_id, GetExpectedItems()),
base::DoNothing());
WaitForDB();
{
auto items = GetItemsForMediaFeedSync(
service(), MediaHistoryKeyedService::GetMediaFeedItemsRequest::
CreateItemsForContinueWatching(5, false));
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
// We should have the first item because it has play next details and the
// second item because it has an active action status.
ASSERT_EQ(2u, items.size());
EXPECT_EQ(2, items[0]->id);
EXPECT_EQ(1, items[1]->id);
}
}
{
auto items = GetItemsForMediaFeedSync(
service(), MediaHistoryKeyedService::GetMediaFeedItemsRequest::
CreateItemsForContinueWatching(5, true));
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
// We should only return the second item because it is the only one that
// is safe.
ASSERT_EQ(1u, items.size());
EXPECT_EQ(2, items[0]->id);
}
}
{
auto items = GetItemsForMediaFeedSync(
service(), MediaHistoryKeyedService::GetMediaFeedItemsRequest::
CreateItemsForContinueWatching(1, false));
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
// We should only return the second item because we are limiting to one
// item.
ASSERT_EQ(1u, items.size());
EXPECT_EQ(2, items[0]->id);
}
}
}
TEST_P(MediaHistoryStoreFeedsTest, GetItemsForFeed) {
const GURL feed_url("https://www.google.com/feed");
DiscoverMediaFeed(feed_url);
WaitForDB();
// If we are read only we should use -1 as a placeholder feed id because the
// feed will not have been stored. This is so we can run the rest of the test
// to ensure a no-op.
const int feed_id = IsReadOnly() ? -1 : GetMediaFeedsSync(service())[0]->id;
service()->StoreMediaFeedFetchResult(
SuccessfulResultWithItems(feed_id, GetExpectedItems()),
base::DoNothing());
WaitForDB();
{
auto items = GetItemsForMediaFeedSync(
service(),
MediaHistoryKeyedService::GetMediaFeedItemsRequest::CreateItemsForFeed(
1, 5, false));
if (IsReadOnly()) {
EXPECT_TRUE(items.empty());
} else {
// We should have the third item because the others have continue
// watching details are have been removed.
ASSERT_EQ(1u, items.size());
EXPECT_EQ(3, items[0]->id);
}
}
{
auto items = GetItemsForMediaFeedSync(
service(),
MediaHistoryKeyedService::GetMediaFeedItemsRequest::CreateItemsForFeed(
1, 5, true));
// Do not return anything since all the feed items are "unsafe".
EXPECT_TRUE(items.empty());
}
{
auto items = GetItemsForMediaFeedSync(
service(),
MediaHistoryKeyedService::GetMediaFeedItemsRequest::CreateItemsForFeed(
1, 0, false));
// Do not return anything since the limit is 0.
EXPECT_TRUE(items.empty());
}
}
#endif // !defined(OS_ANDROID)
} // namespace media_history