blob: b3eb0501ea4f58cd57a9a6d14cd3c2f0c7da8df3 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/resource_coordinator/session_restore_policy.h"
#include <algorithm>
#include <vector>
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/simple_test_tick_clock.h"
#include "build/build_config.h"
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/performance_manager/test_support/site_data_utils.h"
#endif
#include "chrome/browser/resource_coordinator/tab_helper.h"
#include "chrome/browser/resource_coordinator/tab_manager_features.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/performance_manager/persistence/site_data/site_data_impl.h"
#include "components/performance_manager/persistence/site_data/site_data_writer.h"
#include "components/performance_manager/public/decorators/site_data_recorder.h"
#include "components/performance_manager/public/performance_manager.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/mock_permission_controller.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace resource_coordinator {
namespace {
// Delegate that exposes testing seams for testing SessionRestorePolicy.
class TestDelegate : public SessionRestorePolicy::Delegate {
public:
explicit TestDelegate(base::TickClock* clock) : clock_(clock) {}
TestDelegate(const TestDelegate&) = delete;
TestDelegate& operator=(const TestDelegate&) = delete;
~TestDelegate() override {}
size_t GetNumberOfCores() const override { return number_of_cores_; }
size_t GetFreeMemoryMiB() const override { return free_memory_mb_; }
base::TimeTicks NowTicks() const override { return clock_->NowTicks(); }
size_t GetSiteEngagementScore(
content::WebContents* unused_contents) const override {
return site_engagement_score_;
}
void SetNumberOfCores(size_t number_of_cores) {
number_of_cores_ = number_of_cores;
}
void SetFreeMemoryMiB(size_t free_memory_mb) {
free_memory_mb_ = free_memory_mb;
}
void SetSiteEngagementScore(size_t site_engagement_score) {
site_engagement_score_ = site_engagement_score;
}
private:
size_t number_of_cores_ = 1;
size_t free_memory_mb_ = 0;
raw_ptr<base::TickClock> clock_ = nullptr;
size_t site_engagement_score_ = 0;
};
class LenientTabScoreChangeMock {
public:
LenientTabScoreChangeMock() = default;
~LenientTabScoreChangeMock() = default;
MOCK_METHOD2(NotifyTabScoreChanged, void(content::WebContents*, float));
};
using TabScoreChangeMock = ::testing::StrictMock<LenientTabScoreChangeMock>;
// Exposes testing functions on SessionRestorePolicy.
class TestSessionRestorePolicy : public SessionRestorePolicy {
public:
using SessionRestorePolicy::CalculateAgeScore;
using SessionRestorePolicy::CalculateSimultaneousTabLoads;
using SessionRestorePolicy::ScoreTab;
using SessionRestorePolicy::SetTabLoadsStartedForTesting;
using SessionRestorePolicy::TabData;
using SessionRestorePolicy::UpdateSiteEngagementScoreForTesting;
// Expose some member variables.
using SessionRestorePolicy::tab_data_;
// Expose parameters.
using SessionRestorePolicy::cores_per_simultaneous_tab_load_;
using SessionRestorePolicy::max_simultaneous_tab_loads_;
using SessionRestorePolicy::max_tabs_to_restore_;
using SessionRestorePolicy::max_time_since_last_use_to_restore_;
using SessionRestorePolicy::mb_free_memory_per_tab_to_restore_;
using SessionRestorePolicy::min_simultaneous_tab_loads_;
using SessionRestorePolicy::min_site_engagement_to_restore_;
using SessionRestorePolicy::min_tabs_to_restore_;
using SessionRestorePolicy::simultaneous_tab_loads_;
TestSessionRestorePolicy(bool policy_enabled, const Delegate* delegate)
: SessionRestorePolicy(policy_enabled, delegate) {}
TestSessionRestorePolicy(const TestSessionRestorePolicy&) = delete;
TestSessionRestorePolicy& operator=(const TestSessionRestorePolicy&) = delete;
~TestSessionRestorePolicy() override {}
using RescoreTabCallback =
base::RepeatingCallback<bool(content::WebContents*, TabData*)>;
void SetRescoreTabCallback(RescoreTabCallback rescore_tab_callback) {
rescore_tab_callback_ = rescore_tab_callback;
}
bool RescoreTabAfterDataLoaded(content::WebContents* contents,
TabData* tab_data) override {
// Invoke the callback if one is provided.
if (!rescore_tab_callback_.is_null())
return rescore_tab_callback_.Run(contents, tab_data);
// Otherwise defer to the default implementation.
return SessionRestorePolicy::RescoreTabAfterDataLoaded(contents, tab_data);
}
float GetTabScore(content::WebContents* contents) const {
auto it = tab_data_.find(contents);
return it->second->score;
}
private:
RescoreTabCallback rescore_tab_callback_;
};
} // namespace
class SessionRestorePolicyTest : public ChromeRenderViewHostTestHarness {
public:
SessionRestorePolicyTest() : delegate_(&clock_) {}
SessionRestorePolicyTest(const SessionRestorePolicyTest&) = delete;
SessionRestorePolicyTest& operator=(const SessionRestorePolicyTest&) = delete;
~SessionRestorePolicyTest() override {}
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
#if !BUILDFLAG(IS_ANDROID)
// Some tests requires the SiteData database to be initialized.
site_data_harness_.SetUp();
#endif
// Set some reasonable delegate constants.
delegate_.SetNumberOfCores(4);
delegate_.SetFreeMemoryMiB(1024);
delegate_.SetSiteEngagementScore(30);
// Put the clock in the future so that we can LastActiveTimes in the past.
clock_.Advance(base::Days(1));
CreateTestContents();
}
void TearDown() override {
#if !BUILDFLAG(IS_ANDROID)
performance_manager::MarkWebContentsAsUnloadedInBackgroundInSiteDataDb(
contents1_.get());
performance_manager::MarkWebContentsAsUnloadedInBackgroundInSiteDataDb(
contents2_.get());
performance_manager::MarkWebContentsAsUnloadedInBackgroundInSiteDataDb(
contents3_.get());
#endif
if (policy_)
policy_.reset();
// The WebContents must be deleted before the test harness deletes the
// RenderProcessHost.
contents1_.reset();
contents2_.reset();
contents3_.reset();
tab_for_scoring_.clear();
#if !BUILDFLAG(IS_ANDROID)
site_data_harness_.TearDown(profile());
#endif
ChromeRenderViewHostTestHarness::TearDown();
}
void CreateTestContents() {
contents1_ = CreateAndInitTestWebContents(
GURL("https://a.com"), clock_.NowTicks() - base::Hours(1));
contents2_ = CreateAndInitTestWebContents(
GURL("https://b.com"), clock_.NowTicks() - base::Hours(2));
contents3_ = CreateAndInitTestWebContents(
GURL("https://c.com"), clock_.NowTicks() - base::Hours(3));
tab_for_scoring_ = {contents1_.get(), contents2_.get(), contents3_.get()};
}
std::unique_ptr<content::WebContents> CreateAndInitTestWebContents(
const GURL& url,
const base::TimeTicks& last_active) {
auto contents = CreateTestWebContents();
auto* tester = content::WebContentsTester::For(contents.get());
tester->SetLastActiveTime(last_active);
#if !BUILDFLAG(IS_ANDROID)
tester->NavigateAndCommit(url);
performance_manager::MarkWebContentsAsLoadedInBackgroundInSiteDataDb(
contents.get());
performance_manager::ExpireSiteDataObservationWindowsForWebContents(
contents.get());
#endif
return contents;
}
void CreatePolicy(bool policy_enabled) {
policy_ =
std::make_unique<TestSessionRestorePolicy>(policy_enabled, &delegate_);
// Set some reasonable initial parameters.
policy_->min_simultaneous_tab_loads_ = 1;
policy_->max_simultaneous_tab_loads_ = 4;
policy_->cores_per_simultaneous_tab_load_ = 2;
policy_->min_tabs_to_restore_ = 2;
policy_->max_tabs_to_restore_ = 30;
policy_->mb_free_memory_per_tab_to_restore_ = 150;
policy_->max_time_since_last_use_to_restore_ = base::Hours(6);
policy_->min_site_engagement_to_restore_ = 15;
// Ensure the simultaneous tab loads is properly calculated wrt the above
// parameters.
policy_->CalculateSimultaneousTabLoadsForTesting();
policy_->SetTabScoreChangedCallback(base::BindRepeating(
&TabScoreChangeMock::NotifyTabScoreChanged, base::Unretained(&mock_)));
for (content::WebContents* tab : tab_for_scoring_) {
policy_->AddTabForScoring(tab);
}
}
void WaitForFinalTabScores() {
base::RunLoop run_loop;
EXPECT_CALL(mock_, NotifyTabScoreChanged(nullptr, 0.0))
.WillOnce(::testing::Invoke(
[&run_loop](content::WebContents*, float) { run_loop.Quit(); }));
run_loop.Run();
}
void AddExtraTabForScoring(content::WebContents* contents) {
tab_for_scoring_.push_back(contents);
}
protected:
base::SimpleTestTickClock clock_;
TestDelegate delegate_;
#if !BUILDFLAG(IS_ANDROID)
performance_manager::SiteDataTestHarness site_data_harness_;
#endif
TabScoreChangeMock mock_;
std::unique_ptr<TestSessionRestorePolicy> policy_;
std::unique_ptr<content::WebContents> contents1_;
std::unique_ptr<content::WebContents> contents2_;
std::unique_ptr<content::WebContents> contents3_;
std::vector<raw_ptr<content::WebContents, VectorExperimental>>
tab_for_scoring_;
};
TEST_F(SessionRestorePolicyTest, CalculateSimultaneousTabLoads) {
using TSRP = TestSessionRestorePolicy;
// Test the minimum is enforced.
EXPECT_EQ(10u, TSRP::CalculateSimultaneousTabLoads(10 /* min */, 20 /* max */,
1 /* cores_per_load */,
1 /* cores */));
// Test the maximum is enforced.
EXPECT_EQ(20u, TSRP::CalculateSimultaneousTabLoads(10 /* min */, 20 /* max */,
1 /* cores_per_load */,
30 /* cores */));
// Test the per-core calculation is correct.
EXPECT_EQ(15u, TSRP::CalculateSimultaneousTabLoads(10 /* min */, 20 /* max */,
1 /* cores_per_load */,
15 /* cores */));
EXPECT_EQ(15u, TSRP::CalculateSimultaneousTabLoads(10 /* min */, 20 /* max */,
2 /* cores_per_load */,
30 /* cores */));
// If no per-core is specified then max is returned.
EXPECT_EQ(5u, TSRP::CalculateSimultaneousTabLoads(1 /* min */, 5 /* max */,
0 /* cores_per_load */,
10 /* cores */));
// If no per-core and no max is applied, then "max" is returned.
EXPECT_EQ(
std::numeric_limits<size_t>::max(),
TSRP::CalculateSimultaneousTabLoads(
3 /* min */, 0 /* max */, 0 /* cores_per_load */, 4 /* cores */));
}
TEST_F(SessionRestorePolicyTest, ShouldLoadFeatureEnabled) {
CreatePolicy(true);
EXPECT_TRUE(policy_->policy_enabled());
EXPECT_EQ(2u, policy_->simultaneous_tab_loads());
WaitForFinalTabScores();
// By default all the tabs should be loadable.
EXPECT_TRUE(policy_->ShouldLoad(contents1_.get()));
policy_->NotifyTabLoadStarted();
EXPECT_TRUE(policy_->ShouldLoad(contents2_.get()));
policy_->NotifyTabLoadStarted();
EXPECT_TRUE(policy_->ShouldLoad(contents3_.get()));
policy_->NotifyTabLoadStarted();
// Reset and set a maximum number of tabs to load policy.
policy_->SetTabLoadsStartedForTesting(0);
policy_->max_tabs_to_restore_ = 2;
EXPECT_TRUE(policy_->ShouldLoad(contents1_.get()));
policy_->NotifyTabLoadStarted();
EXPECT_TRUE(policy_->ShouldLoad(contents2_.get()));
policy_->NotifyTabLoadStarted();
EXPECT_FALSE(policy_->ShouldLoad(contents3_.get()));
// Disable the number of tab load limits entirely.
policy_->min_tabs_to_restore_ = 0;
policy_->max_tabs_to_restore_ = 0;
// Reset and impose a memory policy.
policy_->SetTabLoadsStartedForTesting(0);
constexpr size_t kFreeMemoryLimit = 150;
policy_->mb_free_memory_per_tab_to_restore_ = kFreeMemoryLimit;
delegate_.SetFreeMemoryMiB(kFreeMemoryLimit);
EXPECT_TRUE(policy_->ShouldLoad(contents1_.get()));
policy_->NotifyTabLoadStarted();
delegate_.SetFreeMemoryMiB(kFreeMemoryLimit - 1);
EXPECT_FALSE(policy_->ShouldLoad(contents2_.get()));
delegate_.SetFreeMemoryMiB(kFreeMemoryLimit + 1);
EXPECT_TRUE(policy_->ShouldLoad(contents3_.get()));
policy_->NotifyTabLoadStarted();
// Disable memory limits to not interfere with later tests.
policy_->mb_free_memory_per_tab_to_restore_ = 0;
// Reset and impose a max time since use policy. The contents have ages of 1,
// 2 and 3 hours respectively.
policy_->SetTabLoadsStartedForTesting(0);
policy_->max_time_since_last_use_to_restore_ = base::Minutes(90);
EXPECT_TRUE(policy_->ShouldLoad(contents1_.get()));
policy_->NotifyTabLoadStarted();
EXPECT_FALSE(policy_->ShouldLoad(contents2_.get()));
EXPECT_FALSE(policy_->ShouldLoad(contents3_.get()));
// Disable the age limits entirely.
policy_->max_time_since_last_use_to_restore_ = base::TimeDelta();
// Reset and impose a site engagement policy.
policy_->SetTabLoadsStartedForTesting(0);
constexpr size_t kEngagementLimit = 15;
policy_->min_site_engagement_to_restore_ = kEngagementLimit;
policy_->UpdateSiteEngagementScoreForTesting(contents1_.get(),
kEngagementLimit + 1);
EXPECT_TRUE(policy_->ShouldLoad(contents1_.get()));
policy_->UpdateSiteEngagementScoreForTesting(contents1_.get(),
kEngagementLimit);
EXPECT_TRUE(policy_->ShouldLoad(contents1_.get()));
policy_->UpdateSiteEngagementScoreForTesting(contents1_.get(),
kEngagementLimit - 1);
EXPECT_FALSE(policy_->ShouldLoad(contents1_.get()));
}
TEST_F(SessionRestorePolicyTest, ShouldLoadFeatureDisabled) {
CreatePolicy(false);
EXPECT_FALSE(policy_->policy_enabled());
EXPECT_EQ(std::numeric_limits<size_t>::max(),
policy_->simultaneous_tab_loads());
WaitForFinalTabScores();
// Set everything aggressive so it would return false if the feature was
// enabled.
policy_->min_tabs_to_restore_ = 0;
policy_->max_tabs_to_restore_ = 1;
policy_->mb_free_memory_per_tab_to_restore_ = 1024;
policy_->max_time_since_last_use_to_restore_ = base::Minutes(1);
policy_->min_site_engagement_to_restore_ = 100;
// Make the system look like its effectively out of memory as well.
delegate_.SetFreeMemoryMiB(1);
// Everything should still be allowed to load, as the policy engine is
// disabled.
EXPECT_TRUE(policy_->ShouldLoad(contents1_.get()));
policy_->NotifyTabLoadStarted();
EXPECT_TRUE(policy_->ShouldLoad(contents2_.get()));
policy_->NotifyTabLoadStarted();
EXPECT_TRUE(policy_->ShouldLoad(contents3_.get()));
policy_->NotifyTabLoadStarted();
}
TEST_F(SessionRestorePolicyTest, ShouldLoadBackgroundData) {
using TabData = TestSessionRestorePolicy::TabData;
CreatePolicy(true);
EXPECT_TRUE(policy_->policy_enabled());
EXPECT_EQ(2u, policy_->simultaneous_tab_loads());
WaitForFinalTabScores();
// Disable other limit mechanisms.
policy_->min_tabs_to_restore_ = 0;
policy_->max_tabs_to_restore_ = 0;
policy_->mb_free_memory_per_tab_to_restore_ = 0;
policy_->max_time_since_last_use_to_restore_ = base::TimeDelta();
constexpr size_t kEngagementLimit = 15;
policy_->min_site_engagement_to_restore_ = kEngagementLimit;
policy_->UpdateSiteEngagementScoreForTesting(contents1_.get(),
kEngagementLimit + 1);
EXPECT_TRUE(policy_->ShouldLoad(contents1_.get()));
policy_->UpdateSiteEngagementScoreForTesting(contents1_.get(),
kEngagementLimit);
EXPECT_TRUE(policy_->ShouldLoad(contents1_.get()));
policy_->UpdateSiteEngagementScoreForTesting(contents1_.get(),
kEngagementLimit - 1);
EXPECT_FALSE(policy_->ShouldLoad(contents1_.get()));
// Mark the tab as using background communication mechanisms, and expect the
// site engagement policy to no longer be applied.
auto iter = policy_->tab_data_.insert(
std::make_pair(contents1_.get(), std::make_unique<TabData>()));
TabData* tab_data = iter.first->second.get();
tab_data->used_in_bg = true;
EXPECT_TRUE(policy_->ShouldLoad(contents1_.get()));
}
TEST_F(SessionRestorePolicyTest, NotificationPermissionSetUsedInBgBit) {
CreatePolicy(true);
WaitForFinalTabScores();
auto iter = policy_->tab_data_.find(contents1_.get());
EXPECT_TRUE(iter != policy_->tab_data_.end());
EXPECT_FALSE(iter->second->UsedInBg());
// Allow |contents1_| to display notifications, this should cause the
// |used_in_bg| bit to change to true.
GetBrowserContext()->SetPermissionControllerForTesting(
std::make_unique<content::MockPermissionController>());
// Adding/Removing the tab for scoring will cause the callback to be called a
// few times, ignore this.
EXPECT_CALL(mock_, NotifyTabScoreChanged(::testing::_, ::testing::_))
.Times(::testing::AnyNumber());
policy_->RemoveTabForScoring(contents1_.get());
policy_->AddTabForScoring(contents1_.get());
WaitForFinalTabScores();
iter = policy_->tab_data_.find(contents1_.get());
EXPECT_TRUE(iter != policy_->tab_data_.end());
EXPECT_TRUE(iter->second->UsedInBg());
}
TEST_F(SessionRestorePolicyTest, MultipleAllTabsDoneCallbacks) {
CreatePolicy(true);
WaitForFinalTabScores();
// Another "all tabs scored" notification should be sent after more tabs
// are added to the policy engine.
std::unique_ptr<content::WebContents> contents4 =
CreateAndInitTestWebContents(GURL("https://d.com"),
base::TimeTicks::Now());
std::unique_ptr<content::WebContents> contents5 =
CreateAndInitTestWebContents(GURL("https://e.com"),
base::TimeTicks::Now());
policy_->AddTabForScoring(contents4.get());
policy_->AddTabForScoring(contents5.get());
WaitForFinalTabScores();
performance_manager::MarkWebContentsAsUnloadedInBackgroundInSiteDataDb(
contents4.get());
performance_manager::MarkWebContentsAsUnloadedInBackgroundInSiteDataDb(
contents5.get());
}
TEST_F(SessionRestorePolicyTest, CalculateAgeScore) {
using TabData = TestSessionRestorePolicy::TabData;
constexpr int kMonthInSeconds = 60 * 60 * 24 * 31;
// Generate a bunch of random tab ages.
std::vector<std::unique_ptr<TabData>> tab_data;
tab_data.reserve(1000);
for (size_t i = 0; i < 1000; ++i)
tab_data.push_back(std::make_unique<TabData>());
// Generate some known edge cases.
size_t i = 0;
tab_data[i++]->last_active = base::Milliseconds(-1001);
tab_data[i++]->last_active = base::Milliseconds(-1000);
tab_data[i++]->last_active = base::Milliseconds(-999);
tab_data[i++]->last_active = base::Milliseconds(-500);
tab_data[i++]->last_active = base::Milliseconds(0);
tab_data[i++]->last_active = base::Milliseconds(500);
tab_data[i++]->last_active = base::Milliseconds(999);
tab_data[i++]->last_active = base::Milliseconds(1000);
tab_data[i++]->last_active = base::Milliseconds(1001);
// Generate a logarithmic selection of ages to test the whole range.
tab_data[i++]->last_active = base::Seconds(-1000000);
tab_data[i++]->last_active = base::Seconds(-100000);
tab_data[i++]->last_active = base::Seconds(-10000);
tab_data[i++]->last_active = base::Seconds(-1000);
tab_data[i++]->last_active = base::Seconds(-100);
tab_data[i++]->last_active = base::Seconds(-10);
tab_data[i++]->last_active = base::Seconds(10);
tab_data[i++]->last_active = base::Seconds(100);
tab_data[i++]->last_active = base::Seconds(1000);
tab_data[i++]->last_active = base::Seconds(10000);
tab_data[i++]->last_active = base::Seconds(100000);
tab_data[i++]->last_active = base::Seconds(1000000);
tab_data[i++]->last_active = base::Seconds(10000000);
// Generate a bunch more random ages.
for (; i < tab_data.size(); ++i) {
tab_data[i]->last_active =
base::Seconds(base::RandInt(-kMonthInSeconds, kMonthInSeconds));
}
// Calculate the tab scores.
for (i = 0; i < tab_data.size(); ++i) {
tab_data[i]->score =
TestSessionRestorePolicy::CalculateAgeScore(tab_data[i].get());
}
// Sort tabs by increasing last active time.
std::sort(tab_data.begin(), tab_data.end(),
[](const std::unique_ptr<TabData>& td1,
const std::unique_ptr<TabData>& td2) {
return td1->last_active < td2->last_active;
});
// The scores should be in decreasing order (>= is necessary because some
// last active times collapse to the same score).
for (i = 1; i < tab_data.size(); ++i)
ASSERT_GE(tab_data[i - 1]->score, tab_data[i]->score);
}
TEST_F(SessionRestorePolicyTest, ScoreTab) {
using TabData = TestSessionRestorePolicy::TabData;
TabData td_bg;
td_bg.used_in_bg = true;
td_bg.last_active = base::Days(30);
EXPECT_TRUE(TestSessionRestorePolicy::ScoreTab(&td_bg));
TabData td_normal_young;
TabData td_normal_old;
td_normal_young.last_active = base::Seconds(1);
td_normal_old.last_active = base::Days(7);
EXPECT_TRUE(TestSessionRestorePolicy::ScoreTab(&td_normal_young));
EXPECT_TRUE(TestSessionRestorePolicy::ScoreTab(&td_normal_old));
TabData td_internal;
td_internal.is_internal = true;
EXPECT_TRUE(TestSessionRestorePolicy::ScoreTab(&td_internal));
// Check the score produces the expected ordering of tabs.
EXPECT_LT(td_internal.score, td_normal_old.score);
EXPECT_LT(td_normal_old.score, td_normal_young.score);
EXPECT_LT(td_normal_young.score, td_bg.score);
}
TEST_F(SessionRestorePolicyTest, RescoringSendsNotification) {
using TabData = TestSessionRestorePolicy::TabData;
// Inject code that causes some tabs to receive updated scores.
CreatePolicy(true);
policy_->SetRescoreTabCallback(base::BindLambdaForTesting(
[&](content::WebContents* contents, TabData* tab_data) {
float delta = 0;
if (contents == contents1_.get())
delta = 1.0;
else if (contents == contents2_.get())
delta = 2.0;
tab_data->score += delta;
return delta != 0;
}));
// Get the current scores.
float score1 = policy_->GetTabScore(contents1_.get());
float score2 = policy_->GetTabScore(contents2_.get());
// Expect tab score change notifications for the first 2 tabs, but not the
// third.
EXPECT_CALL(mock_, NotifyTabScoreChanged(contents1_.get(), score1 + 1.0));
EXPECT_CALL(mock_, NotifyTabScoreChanged(contents2_.get(), score2 + 2.0));
WaitForFinalTabScores();
}
#if !BUILDFLAG(IS_ANDROID)
TEST_F(SessionRestorePolicyTest, FeatureUsageSetUsedInBgBit) {
CreatePolicy(true);
WaitForFinalTabScores();
auto iter = policy_->tab_data_.find(contents1_.get());
EXPECT_TRUE(iter != policy_->tab_data_.end());
EXPECT_FALSE(iter->second->UsedInBg());
// Indicates that |contents1_| might update its title while in background,
// this should set the |used_in_bg_| bit.
base::RunLoop run_loop;
performance_manager::PerformanceManager::CallOnGraph(
FROM_HERE, base::BindOnce(
[](base::WeakPtr<performance_manager::PageNode> page_node,
base::OnceClosure closure) {
EXPECT_TRUE(page_node);
auto* impl =
performance_manager::GetSiteDataImplForPageNode(
page_node.get());
EXPECT_TRUE(impl);
impl->NotifyUpdatesTitleInBackground();
std::move(closure).Run();
},
performance_manager::PerformanceManager::
GetPrimaryPageNodeForWebContents(contents1_.get()),
run_loop.QuitClosure()));
run_loop.Run();
// Adding/Removing the tab for scoring will cause the callback to be called a
// few times, ignore this.
EXPECT_CALL(mock_, NotifyTabScoreChanged(::testing::_, ::testing::_))
.Times(::testing::AnyNumber());
policy_->RemoveTabForScoring(contents1_.get());
policy_->AddTabForScoring(contents1_.get());
WaitForFinalTabScores();
iter = policy_->tab_data_.find(contents1_.get());
EXPECT_TRUE(iter != policy_->tab_data_.end());
EXPECT_TRUE(iter->second->UsedInBg());
}
TEST_F(SessionRestorePolicyTest, UnknownUsageSetUsedInBgBit) {
auto contents = CreateTestWebContents();
auto* tester = content::WebContentsTester::For(contents.get());
ResourceCoordinatorTabHelper::CreateForWebContents(contents.get());
tester->NavigateAndCommit(GURL("https://d.com"));
// Adding/Removing the tab for scoring will cause the callback to be called a
// few times, ignore this.
EXPECT_CALL(mock_, NotifyTabScoreChanged(::testing::_, ::testing::_))
.Times(::testing::AnyNumber());
AddExtraTabForScoring(contents.get());
CreatePolicy(true);
WaitForFinalTabScores();
base::RunLoop run_loop;
performance_manager::PerformanceManager::CallOnGraph(
FROM_HERE,
base::BindOnce(
[](base::WeakPtr<performance_manager::PageNode> page_node,
base::OnceClosure closure) {
EXPECT_TRUE(page_node);
auto* impl = performance_manager::GetSiteDataImplForPageNode(
page_node.get());
EXPECT_TRUE(impl);
performance_manager::SiteFeatureUsage title_feature_usage =
impl->UpdatesTitleInBackground();
EXPECT_EQ(
performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
title_feature_usage);
std::move(closure).Run();
},
performance_manager::PerformanceManager::
GetPrimaryPageNodeForWebContents(contents.get()),
run_loop.QuitClosure()));
run_loop.Run();
auto iter = policy_->tab_data_.find(contents.get());
EXPECT_TRUE(iter != policy_->tab_data_.end());
EXPECT_TRUE(iter->second->UsedInBg());
}
#endif
} // namespace resource_coordinator