blob: a987e9acf6a89e5e5e5f695ffe63c387f01e01b2 [file] [log] [blame]
// Copyright 2015 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 "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
#include <memory>
#include "base/callback.h"
#include "base/location.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_param_associator.h"
#include "base/metrics/field_trial_params.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/test/test_mock_time_task_runner.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/scheduler/base/test/task_queue_manager_for_test.h"
#include "third_party/blink/renderer/platform/scheduler/child/task_queue_with_task_type.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
using base::sequence_manager::TaskQueue;
using testing::ElementsAre;
using VirtualTimePolicy = blink::PageScheduler::VirtualTimePolicy;
namespace blink {
namespace scheduler {
// To avoid symbol collisions in jumbo builds.
namespace page_scheduler_impl_unittest {
class PageSchedulerImplTest : public testing::Test {
public:
PageSchedulerImplTest() = default;
~PageSchedulerImplTest() override = default;
protected:
void SetUp() override {
test_task_runner_ = base::WrapRefCounted(new base::TestMockTimeTaskRunner(
base::TestMockTimeTaskRunner::Type::kBoundToThread));
// A null clock triggers some assertions.
test_task_runner_->AdvanceMockTickClock(
base::TimeDelta::FromMilliseconds(5));
scheduler_.reset(new MainThreadSchedulerImpl(
base::sequence_manager::TaskQueueManagerForTest::Create(
nullptr, test_task_runner_, test_task_runner_->GetMockTickClock()),
base::nullopt));
CreatePageScheduler(false);
}
void TearDown() override {
frame_scheduler_.reset();
page_scheduler_.reset();
scheduler_->Shutdown();
scheduler_.reset();
}
void CreatePageScheduler(bool disable_background_timer_throttling) {
if (frame_scheduler_)
frame_scheduler_.reset();
page_scheduler_.reset(new PageSchedulerImpl(
nullptr, scheduler_.get(), disable_background_timer_throttling));
frame_scheduler_ = page_scheduler_->CreateFrameSchedulerImpl(
nullptr, FrameScheduler::FrameType::kSubframe);
}
void FastForwardTo(base::TimeTicks time) {
base::TimeTicks now = test_task_runner_->GetMockTickClock()->NowTicks();
CHECK_LE(now, time);
test_task_runner_->FastForwardBy(time - now);
}
static scoped_refptr<TaskQueue> ThrottleableTaskQueueForScheduler(
FrameSchedulerImpl* scheduler) {
return scheduler->ThrottleableTaskQueue();
}
scoped_refptr<base::SingleThreadTaskRunner> ThrottleableTaskRunner() {
return TaskQueueWithTaskType::Create(ThrottleableTaskQueue(),
TaskType::kInternalTest);
}
scoped_refptr<base::SingleThreadTaskRunner> LoadingTaskRunner() {
return TaskQueueWithTaskType::Create(LoadingTaskQueue(),
TaskType::kInternalTest);
}
scoped_refptr<TaskQueue> ThrottleableTaskQueue() {
return frame_scheduler_->ThrottleableTaskQueue();
}
scoped_refptr<TaskQueue> LoadingTaskQueue() {
return frame_scheduler_->LoadingTaskQueue();
}
scoped_refptr<TaskQueue> DeferrableTaskQueue() {
return frame_scheduler_->DeferrableTaskQueue();
}
scoped_refptr<TaskQueue> PausableTaskQueue() {
return frame_scheduler_->PausableTaskQueue();
}
scoped_refptr<TaskQueue> UnpausableTaskQueue() {
return frame_scheduler_->UnpausableTaskQueue();
}
scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_;
std::unique_ptr<MainThreadSchedulerImpl> scheduler_;
std::unique_ptr<PageSchedulerImpl> page_scheduler_;
std::unique_ptr<FrameSchedulerImpl> frame_scheduler_;
};
TEST_F(PageSchedulerImplTest, TestDestructionOfFrameSchedulersBefore) {
std::unique_ptr<blink::FrameScheduler> frame1(
page_scheduler_->CreateFrameScheduler(
nullptr, FrameScheduler::FrameType::kSubframe));
std::unique_ptr<blink::FrameScheduler> frame2(
page_scheduler_->CreateFrameScheduler(
nullptr, FrameScheduler::FrameType::kSubframe));
}
TEST_F(PageSchedulerImplTest, TestDestructionOfFrameSchedulersAfter) {
std::unique_ptr<blink::FrameScheduler> frame1(
page_scheduler_->CreateFrameScheduler(
nullptr, FrameScheduler::FrameType::kSubframe));
std::unique_ptr<blink::FrameScheduler> frame2(
page_scheduler_->CreateFrameScheduler(
nullptr, FrameScheduler::FrameType::kSubframe));
page_scheduler_.reset();
}
namespace {
void RunRepeatingTask(scoped_refptr<TaskQueue>,
int* run_count,
base::TimeDelta delay);
base::OnceClosure MakeRepeatingTask(scoped_refptr<TaskQueue> task_queue,
int* run_count,
base::TimeDelta delay) {
return base::BindOnce(&RunRepeatingTask, std::move(task_queue),
base::Unretained(run_count), delay);
}
void RunRepeatingTask(scoped_refptr<TaskQueue> task_queue,
int* run_count,
base::TimeDelta delay) {
// Limit the number of repetitions.
// Test cases can make expectations against this number.
if (++*run_count == 2000)
return;
TaskQueue* task_queue_ptr = task_queue.get();
task_queue_ptr->PostDelayedTask(
FROM_HERE, MakeRepeatingTask(std::move(task_queue_ptr), run_count, delay),
delay);
}
} // namespace
TEST_F(PageSchedulerImplTest, RepeatingTimer_PageInForeground) {
page_scheduler_->SetPageVisible(true);
int run_count = 0;
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
MakeRepeatingTask(ThrottleableTaskQueue(), &run_count,
base::TimeDelta::FromMilliseconds(1)),
base::TimeDelta::FromMilliseconds(1));
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
EXPECT_EQ(1000, run_count);
}
TEST_F(PageSchedulerImplTest, RepeatingTimer_PageInBackgroundThenForeground) {
page_scheduler_->SetPageVisible(false);
int run_count = 0;
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
MakeRepeatingTask(ThrottleableTaskQueue(), &run_count,
base::TimeDelta::FromMilliseconds(20)),
base::TimeDelta::FromMilliseconds(20));
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
EXPECT_EQ(1, run_count);
// Make sure there's no delay in throttling being removed for pages that have
// become visible.
page_scheduler_->SetPageVisible(true);
run_count = 0;
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
EXPECT_EQ(50, run_count);
}
TEST_F(PageSchedulerImplTest, RepeatingLoadingTask_PageInBackground) {
page_scheduler_->SetPageVisible(false);
int run_count = 0;
LoadingTaskQueue()->PostDelayedTask(
FROM_HERE,
MakeRepeatingTask(LoadingTaskQueue(), &run_count,
base::TimeDelta::FromMilliseconds(1)),
base::TimeDelta::FromMilliseconds(1));
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
EXPECT_EQ(1000, run_count); // Loading tasks should not be throttled
}
TEST_F(PageSchedulerImplTest, RepeatingTimers_OneBackgroundOneForeground) {
std::unique_ptr<PageSchedulerImpl> page_scheduler2(
new PageSchedulerImpl(nullptr, scheduler_.get(), false));
std::unique_ptr<FrameSchedulerImpl> frame_scheduler2 =
page_scheduler2->CreateFrameSchedulerImpl(
nullptr, FrameScheduler::FrameType::kSubframe);
page_scheduler_->SetPageVisible(true);
page_scheduler2->SetPageVisible(false);
int run_count1 = 0;
int run_count2 = 0;
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
MakeRepeatingTask(ThrottleableTaskQueue(), &run_count1,
base::TimeDelta::FromMilliseconds(20)),
base::TimeDelta::FromMilliseconds(20));
ThrottleableTaskQueueForScheduler(frame_scheduler2.get())
->PostDelayedTask(
FROM_HERE,
MakeRepeatingTask(
ThrottleableTaskQueueForScheduler(frame_scheduler2.get()),
&run_count2, base::TimeDelta::FromMilliseconds(20)),
base::TimeDelta::FromMilliseconds(20));
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
EXPECT_EQ(50, run_count1);
EXPECT_EQ(1, run_count2);
}
namespace {
void RunVirtualTimeRecorderTask(
const base::TickClock* clock,
MainThreadSchedulerImpl* scheduler,
std::vector<base::TimeTicks>* out_real_times,
std::vector<base::TimeTicks>* out_virtual_times) {
out_real_times->push_back(clock->NowTicks());
out_virtual_times->push_back(scheduler->GetVirtualTimeDomain()->Now());
}
base::OnceClosure MakeVirtualTimeRecorderTask(
const base::TickClock* clock,
MainThreadSchedulerImpl* scheduler,
std::vector<base::TimeTicks>* out_real_times,
std::vector<base::TimeTicks>* out_virtual_times) {
return base::BindOnce(&RunVirtualTimeRecorderTask, base::Unretained(clock),
base::Unretained(scheduler),
base::Unretained(out_real_times),
base::Unretained(out_virtual_times));
}
} // namespace
TEST_F(PageSchedulerImplTest, VirtualTime_TimerFastForwarding) {
std::vector<base::TimeTicks> real_times;
std::vector<base::TimeTicks> virtual_times;
page_scheduler_->EnableVirtualTime();
base::TimeTicks initial_real_time = scheduler_->tick_clock()->NowTicks();
base::TimeTicks initial_virtual_time =
scheduler_->GetVirtualTimeDomain()->Now();
ThrottleableTaskRunner()->PostDelayedTask(
FROM_HERE,
MakeVirtualTimeRecorderTask(test_task_runner_->GetMockTickClock(),
scheduler_.get(), &real_times,
&virtual_times),
base::TimeDelta::FromMilliseconds(2));
ThrottleableTaskRunner()->PostDelayedTask(
FROM_HERE,
MakeVirtualTimeRecorderTask(test_task_runner_->GetMockTickClock(),
scheduler_.get(), &real_times,
&virtual_times),
base::TimeDelta::FromMilliseconds(20));
ThrottleableTaskRunner()->PostDelayedTask(
FROM_HERE,
MakeVirtualTimeRecorderTask(test_task_runner_->GetMockTickClock(),
scheduler_.get(), &real_times,
&virtual_times),
base::TimeDelta::FromMilliseconds(200));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(real_times, ElementsAre(initial_real_time, initial_real_time,
initial_real_time));
EXPECT_THAT(
virtual_times,
ElementsAre(
initial_virtual_time + base::TimeDelta::FromMilliseconds(2),
initial_virtual_time + base::TimeDelta::FromMilliseconds(20),
initial_virtual_time + base::TimeDelta::FromMilliseconds(200)));
}
TEST_F(PageSchedulerImplTest, VirtualTime_LoadingTaskFastForwarding) {
std::vector<base::TimeTicks> real_times;
std::vector<base::TimeTicks> virtual_times;
page_scheduler_->EnableVirtualTime();
base::TimeTicks initial_real_time = scheduler_->tick_clock()->NowTicks();
base::TimeTicks initial_virtual_time =
scheduler_->GetVirtualTimeDomain()->Now();
LoadingTaskRunner()->PostDelayedTask(
FROM_HERE,
MakeVirtualTimeRecorderTask(test_task_runner_->GetMockTickClock(),
scheduler_.get(), &real_times,
&virtual_times),
base::TimeDelta::FromMilliseconds(2));
LoadingTaskRunner()->PostDelayedTask(
FROM_HERE,
MakeVirtualTimeRecorderTask(test_task_runner_->GetMockTickClock(),
scheduler_.get(), &real_times,
&virtual_times),
base::TimeDelta::FromMilliseconds(20));
LoadingTaskRunner()->PostDelayedTask(
FROM_HERE,
MakeVirtualTimeRecorderTask(test_task_runner_->GetMockTickClock(),
scheduler_.get(), &real_times,
&virtual_times),
base::TimeDelta::FromMilliseconds(200));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(real_times, ElementsAre(initial_real_time, initial_real_time,
initial_real_time));
EXPECT_THAT(
virtual_times,
ElementsAre(
initial_virtual_time + base::TimeDelta::FromMilliseconds(2),
initial_virtual_time + base::TimeDelta::FromMilliseconds(20),
initial_virtual_time + base::TimeDelta::FromMilliseconds(200)));
}
TEST_F(PageSchedulerImplTest,
RepeatingTimer_PageInBackground_MeansNothingForVirtualTime) {
page_scheduler_->EnableVirtualTime();
page_scheduler_->SetPageVisible(false);
scheduler_->GetSchedulerHelperForTesting()->SetWorkBatchSizeForTesting(1);
base::TimeTicks initial_real_time = scheduler_->tick_clock()->NowTicks();
int run_count = 0;
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
MakeRepeatingTask(ThrottleableTaskQueue(), &run_count,
base::TimeDelta::FromMilliseconds(1)),
base::TimeDelta::FromMilliseconds(1));
test_task_runner_->RunUntilIdle();
// Virtual time means page visibility is ignored.
// 2000 is the |run_count| limit, we expect to reach it.
EXPECT_EQ(2000, run_count);
// The global tick clock has not moved, yet we ran a large number of "delayed"
// tasks despite calling setPageVisible(false).
EXPECT_EQ(initial_real_time, scheduler_->tick_clock()->NowTicks());
}
namespace {
void RunOrderTask(int index, std::vector<int>* out_run_order) {
out_run_order->push_back(index);
}
void DelayedRunOrderTask(int index,
scoped_refptr<TaskQueue> task_queue,
std::vector<int>* out_run_order) {
out_run_order->push_back(index);
task_queue->PostTask(FROM_HERE,
base::BindOnce(&RunOrderTask, index + 1,
base::Unretained(out_run_order)));
}
} // namespace
TEST_F(PageSchedulerImplTest, VirtualTime_NotAllowedToAdvance) {
std::vector<int> run_order;
page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
page_scheduler_->EnableVirtualTime();
ThrottleableTaskQueue()->PostTask(
FROM_HERE,
base::BindOnce(&RunOrderTask, 0, base::Unretained(&run_order)));
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&DelayedRunOrderTask, 1, ThrottleableTaskQueue(),
base::Unretained(&run_order)),
base::TimeDelta::FromMilliseconds(2));
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&DelayedRunOrderTask, 3, ThrottleableTaskQueue(),
base::Unretained(&run_order)),
base::TimeDelta::FromMilliseconds(4));
test_task_runner_->FastForwardUntilNoTasksRemain();
// No timer tasks are allowed to run.
EXPECT_THAT(run_order, ElementsAre());
}
TEST_F(PageSchedulerImplTest, VirtualTime_AllowedToAdvance) {
std::vector<int> run_order;
page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kAdvance);
page_scheduler_->EnableVirtualTime();
ThrottleableTaskQueue()->PostTask(
FROM_HERE,
base::BindOnce(&RunOrderTask, 0, base::Unretained(&run_order)));
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&DelayedRunOrderTask, 1, ThrottleableTaskQueue(),
base::Unretained(&run_order)),
base::TimeDelta::FromMilliseconds(2));
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&DelayedRunOrderTask, 3, ThrottleableTaskQueue(),
base::Unretained(&run_order)),
base::TimeDelta::FromMilliseconds(4));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_order, ElementsAre(0, 1, 2, 3, 4));
}
TEST_F(PageSchedulerImplTest, RepeatingTimer_PageInBackground) {
// Disable background timer throttling.
CreatePageScheduler(true);
page_scheduler_->SetPageVisible(false);
int run_count = 0;
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
MakeRepeatingTask(ThrottleableTaskQueue(), &run_count,
base::TimeDelta::FromMilliseconds(1)),
base::TimeDelta::FromMilliseconds(1));
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
EXPECT_EQ(1000, run_count);
}
TEST_F(PageSchedulerImplTest, VirtualTimeSettings_NewFrameScheduler) {
std::vector<int> run_order;
page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
page_scheduler_->EnableVirtualTime();
std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
page_scheduler_->CreateFrameSchedulerImpl(
nullptr, FrameScheduler::FrameType::kSubframe);
ThrottleableTaskQueueForScheduler(frame_scheduler.get())
->PostDelayedTask(
FROM_HERE,
base::BindOnce(&RunOrderTask, 1, base::Unretained(&run_order)),
base::TimeDelta::FromMilliseconds(1));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_TRUE(run_order.empty());
page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kAdvance);
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_order, ElementsAre(1));
}
namespace {
template <typename T>
base::OnceClosure MakeDeletionTask(T* obj) {
return base::BindOnce([](T* obj) { delete obj; }, base::Unretained(obj));
}
} // namespace
TEST_F(PageSchedulerImplTest, DeleteFrameSchedulers_InTask) {
for (int i = 0; i < 10; i++) {
FrameSchedulerImpl* frame_scheduler =
page_scheduler_
->CreateFrameSchedulerImpl(nullptr,
FrameScheduler::FrameType::kSubframe)
.release();
ThrottleableTaskQueueForScheduler(frame_scheduler)
->PostDelayedTask(FROM_HERE, MakeDeletionTask(frame_scheduler),
base::TimeDelta::FromMilliseconds(1));
}
test_task_runner_->FastForwardUntilNoTasksRemain();
}
TEST_F(PageSchedulerImplTest, DeletePageScheduler_InTask) {
ThrottleableTaskQueue()->PostTask(
FROM_HERE, MakeDeletionTask(page_scheduler_.release()));
test_task_runner_->FastForwardUntilNoTasksRemain();
}
TEST_F(PageSchedulerImplTest, DeleteThrottledQueue_InTask) {
page_scheduler_->SetPageVisible(false);
FrameSchedulerImpl* frame_scheduler =
page_scheduler_
->CreateFrameSchedulerImpl(nullptr,
FrameScheduler::FrameType::kSubframe)
.release();
scoped_refptr<TaskQueue> timer_task_queue =
ThrottleableTaskQueueForScheduler(frame_scheduler);
int run_count = 0;
timer_task_queue->PostDelayedTask(
FROM_HERE,
MakeRepeatingTask(timer_task_queue, &run_count,
base::TimeDelta::FromMilliseconds(100)),
base::TimeDelta::FromMilliseconds(100));
// Note this will run at time t = 10s since we start at time t = 5000us.
// However, we still should run all tasks after frame scheduler deletion.
timer_task_queue->PostDelayedTask(FROM_HERE,
MakeDeletionTask(frame_scheduler),
base::TimeDelta::FromMilliseconds(9990));
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(20));
EXPECT_EQ(110, run_count);
}
TEST_F(PageSchedulerImplTest, VirtualTimePauseCount_DETERMINISTIC_LOADING) {
page_scheduler_->SetVirtualTimePolicy(
VirtualTimePolicy::kDeterministicLoading);
EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
scheduler_->IncrementVirtualTimePauseCount();
EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
scheduler_->IncrementVirtualTimePauseCount();
EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
scheduler_->DecrementVirtualTimePauseCount();
EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
scheduler_->DecrementVirtualTimePauseCount();
EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
scheduler_->IncrementVirtualTimePauseCount();
EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
scheduler_->DecrementVirtualTimePauseCount();
EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
}
TEST_F(PageSchedulerImplTest,
WebScopedVirtualTimePauser_DETERMINISTIC_LOADING) {
page_scheduler_->SetVirtualTimePolicy(
VirtualTimePolicy::kDeterministicLoading);
std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
page_scheduler_->CreateFrameSchedulerImpl(
nullptr, FrameScheduler::FrameType::kSubframe);
{
WebScopedVirtualTimePauser virtual_time_pauser =
frame_scheduler->CreateWebScopedVirtualTimePauser(
"test",
WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
virtual_time_pauser.PauseVirtualTime();
EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
virtual_time_pauser.UnpauseVirtualTime();
EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
virtual_time_pauser.PauseVirtualTime();
EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
}
EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
}
namespace {
void RecordVirtualTime(MainThreadSchedulerImpl* scheduler,
base::TimeTicks* out) {
*out = scheduler->GetVirtualTimeDomain()->Now();
}
void PauseAndUnpauseVirtualTime(MainThreadSchedulerImpl* scheduler,
FrameSchedulerImpl* frame_scheduler,
base::TimeTicks* paused,
base::TimeTicks* unpaused) {
*paused = scheduler->GetVirtualTimeDomain()->Now();
{
WebScopedVirtualTimePauser virtual_time_pauser =
frame_scheduler->CreateWebScopedVirtualTimePauser(
"test",
WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
virtual_time_pauser.PauseVirtualTime();
}
*unpaused = scheduler->GetVirtualTimeDomain()->Now();
}
} // namespace
TEST_F(PageSchedulerImplTest,
WebScopedVirtualTimePauserWithInterleavedTasks_DETERMINISTIC_LOADING) {
// Make task queue manager ask the virtual time domain for the next task delay
// after each task.
scheduler_->GetSchedulerHelperForTesting()->SetWorkBatchSizeForTesting(1);
page_scheduler_->EnableVirtualTime();
page_scheduler_->SetVirtualTimePolicy(
VirtualTimePolicy::kDeterministicLoading);
base::TimeTicks initial_virtual_time =
scheduler_->GetVirtualTimeDomain()->Now();
base::TimeTicks time_paused;
base::TimeTicks time_unpaused;
base::TimeTicks time_second_task;
std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
page_scheduler_->CreateFrameSchedulerImpl(
nullptr, FrameScheduler::FrameType::kSubframe);
// Pauses and unpauses virtual time, thereby advancing virtual time by an
// additional 10ms due to WebScopedVirtualTimePauser's delay.
ThrottleableTaskRunner()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
&PauseAndUnpauseVirtualTime, base::Unretained(scheduler_.get()),
base::Unretained(frame_scheduler.get()),
base::Unretained(&time_paused), base::Unretained(&time_unpaused)),
base::TimeDelta::FromMilliseconds(3));
// Will run after the first task has advanced virtual time past 5ms.
ThrottleableTaskRunner()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&RecordVirtualTime, base::Unretained(scheduler_.get()),
base::Unretained(&time_second_task)),
base::TimeDelta::FromMilliseconds(5));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_EQ(time_paused,
initial_virtual_time + base::TimeDelta::FromMilliseconds(3));
EXPECT_EQ(time_unpaused,
initial_virtual_time + base::TimeDelta::FromMilliseconds(13));
EXPECT_EQ(time_second_task,
initial_virtual_time + base::TimeDelta::FromMilliseconds(13));
}
TEST_F(PageSchedulerImplTest,
MultipleWebScopedVirtualTimePausers_DETERMINISTIC_LOADING) {
page_scheduler_->SetVirtualTimePolicy(
VirtualTimePolicy::kDeterministicLoading);
std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
page_scheduler_->CreateFrameSchedulerImpl(
nullptr, FrameScheduler::FrameType::kSubframe);
WebScopedVirtualTimePauser virtual_time_pauser1 =
frame_scheduler->CreateWebScopedVirtualTimePauser(
"test", WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
WebScopedVirtualTimePauser virtual_time_pauser2 =
frame_scheduler->CreateWebScopedVirtualTimePauser(
"test", WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
virtual_time_pauser1.PauseVirtualTime();
virtual_time_pauser2.PauseVirtualTime();
EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
virtual_time_pauser2.UnpauseVirtualTime();
EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
virtual_time_pauser1.UnpauseVirtualTime();
EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
}
TEST_F(PageSchedulerImplTest, NestedMessageLoop_DETERMINISTIC_LOADING) {
page_scheduler_->SetVirtualTimePolicy(
VirtualTimePolicy::kDeterministicLoading);
EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
scheduler_->OnBeginNestedRunLoop();
EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
scheduler_->OnExitNestedRunLoop();
EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
}
TEST_F(PageSchedulerImplTest, PauseTimersWhileVirtualTimeIsPaused) {
std::vector<int> run_order;
std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
page_scheduler_->CreateFrameSchedulerImpl(
nullptr, FrameScheduler::FrameType::kSubframe);
page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
page_scheduler_->EnableVirtualTime();
ThrottleableTaskQueueForScheduler(frame_scheduler.get())
->PostTask(FROM_HERE, base::BindOnce(&RunOrderTask, 1,
base::Unretained(&run_order)));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_TRUE(run_order.empty());
page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kAdvance);
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_order, ElementsAre(1));
}
TEST_F(PageSchedulerImplTest, VirtualTimeBudgetExhaustedCallback) {
std::vector<base::TimeTicks> real_times;
std::vector<base::TimeTicks> virtual_times;
page_scheduler_->EnableVirtualTime();
base::TimeTicks initial_real_time = scheduler_->tick_clock()->NowTicks();
base::TimeTicks initial_virtual_time =
scheduler_->GetVirtualTimeDomain()->Now();
ThrottleableTaskRunner()->PostDelayedTask(
FROM_HERE,
MakeVirtualTimeRecorderTask(test_task_runner_->GetMockTickClock(),
scheduler_.get(), &real_times,
&virtual_times),
base::TimeDelta::FromMilliseconds(1));
ThrottleableTaskRunner()->PostDelayedTask(
FROM_HERE,
MakeVirtualTimeRecorderTask(test_task_runner_->GetMockTickClock(),
scheduler_.get(), &real_times,
&virtual_times),
base::TimeDelta::FromMilliseconds(2));
ThrottleableTaskRunner()->PostDelayedTask(
FROM_HERE,
MakeVirtualTimeRecorderTask(test_task_runner_->GetMockTickClock(),
scheduler_.get(), &real_times,
&virtual_times),
base::TimeDelta::FromMilliseconds(5));
ThrottleableTaskRunner()->PostDelayedTask(
FROM_HERE,
MakeVirtualTimeRecorderTask(test_task_runner_->GetMockTickClock(),
scheduler_.get(), &real_times,
&virtual_times),
base::TimeDelta::FromMilliseconds(7));
page_scheduler_->GrantVirtualTimeBudget(
base::TimeDelta::FromMilliseconds(5),
base::BindOnce(
[](PageScheduler* scheduler) {
scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
},
base::Unretained(page_scheduler_.get())));
test_task_runner_->FastForwardUntilNoTasksRemain();
// The timer that is scheduled for the exact point in time when virtual time
// expires will not run.
EXPECT_THAT(real_times, ElementsAre(initial_real_time, initial_real_time,
initial_real_time));
EXPECT_THAT(
virtual_times,
ElementsAre(initial_virtual_time + base::TimeDelta::FromMilliseconds(1),
initial_virtual_time + base::TimeDelta::FromMilliseconds(2),
initial_virtual_time + base::TimeDelta::FromMilliseconds(5)));
}
namespace {
class MockObserver : public PageScheduler::VirtualTimeObserver {
public:
~MockObserver() override = default;
void OnVirtualTimeAdvanced(base::TimeDelta virtual_time_offset) override {
virtual_time_log_.push_back(base::StringPrintf(
"Advanced to %dms",
static_cast<int>(virtual_time_offset.InMilliseconds())));
}
void OnVirtualTimePaused(base::TimeDelta virtual_time_offset) override {
virtual_time_log_.push_back(base::StringPrintf(
"Paused at %dms",
static_cast<int>(virtual_time_offset.InMilliseconds())));
}
const std::vector<std::string>& virtual_time_log() const {
return virtual_time_log_;
}
private:
std::vector<std::string> virtual_time_log_;
};
void NopTask() {}
} // namespace
TEST_F(PageSchedulerImplTest, VirtualTimeObserver) {
MockObserver mock_observer;
page_scheduler_->AddVirtualTimeObserver(&mock_observer);
page_scheduler_->EnableVirtualTime();
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE, base::BindOnce(&NopTask),
base::TimeDelta::FromMilliseconds(200));
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE, base::BindOnce(&NopTask),
base::TimeDelta::FromMilliseconds(20));
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE, base::BindOnce(&NopTask),
base::TimeDelta::FromMilliseconds(2));
page_scheduler_->GrantVirtualTimeBudget(
base::TimeDelta::FromMilliseconds(1000),
base::BindOnce(
[](PageScheduler* scheduler) {
scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
},
base::Unretained(page_scheduler_.get())));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(
mock_observer.virtual_time_log(),
ElementsAre("Advanced to 2ms", "Advanced to 20ms", "Advanced to 200ms",
"Advanced to 1000ms", "Paused at 1000ms"));
page_scheduler_->RemoveVirtualTimeObserver(&mock_observer);
}
namespace {
void RepostingTask(scoped_refptr<TaskQueue> task_queue,
int max_count,
int* count) {
if (++(*count) >= max_count)
return;
task_queue->PostTask(FROM_HERE,
base::BindOnce(&RepostingTask, task_queue, max_count,
base::Unretained(count)));
}
void DelayedTask(int* count_in, int* count_out) {
*count_out = *count_in;
}
} // namespace
TEST_F(PageSchedulerImplTest, MaxVirtualTimeTaskStarvationCountOneHundred) {
page_scheduler_->EnableVirtualTime();
page_scheduler_->SetMaxVirtualTimeTaskStarvationCount(100);
page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kAdvance);
int count = 0;
int delayed_task_run_at_count = 0;
RepostingTask(ThrottleableTaskQueue(), 1000, &count);
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
base::BindOnce(DelayedTask, base::Unretained(&count),
base::Unretained(&delayed_task_run_at_count)),
base::TimeDelta::FromMilliseconds(10));
page_scheduler_->GrantVirtualTimeBudget(
base::TimeDelta::FromMilliseconds(1000),
base::BindOnce(
[](PageScheduler* scheduler) {
scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
},
base::Unretained(page_scheduler_.get())));
test_task_runner_->FastForwardUntilNoTasksRemain();
// Two delayed tasks with a run of 100 tasks, plus initial call.
EXPECT_EQ(201, count);
EXPECT_EQ(102, delayed_task_run_at_count);
}
TEST_F(PageSchedulerImplTest,
MaxVirtualTimeTaskStarvationCountOneHundredNestedMessageLoop) {
page_scheduler_->EnableVirtualTime();
page_scheduler_->SetMaxVirtualTimeTaskStarvationCount(100);
page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kAdvance);
scheduler_->OnBeginNestedRunLoop();
int count = 0;
int delayed_task_run_at_count = 0;
RepostingTask(ThrottleableTaskQueue(), 1000, &count);
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
base::BindOnce(DelayedTask, base::Unretained(&count),
base::Unretained(&delayed_task_run_at_count)),
base::TimeDelta::FromMilliseconds(10));
page_scheduler_->GrantVirtualTimeBudget(
base::TimeDelta::FromMilliseconds(1000),
base::BindOnce(
[](PageScheduler* scheduler) {
scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
},
base::Unretained(page_scheduler_.get())));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_EQ(1000, count);
EXPECT_EQ(1000, delayed_task_run_at_count);
}
TEST_F(PageSchedulerImplTest, MaxVirtualTimeTaskStarvationCountZero) {
page_scheduler_->EnableVirtualTime();
page_scheduler_->SetMaxVirtualTimeTaskStarvationCount(0);
page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kAdvance);
int count = 0;
int delayed_task_run_at_count = 0;
RepostingTask(ThrottleableTaskQueue(), 1000, &count);
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
base::BindOnce(DelayedTask, base::Unretained(&count),
base::Unretained(&delayed_task_run_at_count)),
base::TimeDelta::FromMilliseconds(10));
page_scheduler_->GrantVirtualTimeBudget(
base::TimeDelta::FromMilliseconds(1000),
base::BindOnce(
[](PageScheduler* scheduler) {
scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
},
base::Unretained(page_scheduler_.get())));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_EQ(1000, count);
// If the initial count had been higher, the delayed task could have been
// arbitrarily delayed.
EXPECT_EQ(1000, delayed_task_run_at_count);
}
namespace {
void ExpensiveTestTask(scoped_refptr<base::TestMockTimeTaskRunner> task_runner,
std::vector<base::TimeTicks>* run_times) {
run_times->push_back(task_runner->GetMockTickClock()->NowTicks());
task_runner->AdvanceMockTickClock(base::TimeDelta::FromMilliseconds(250));
}
void InitializeTrialParams() {
std::map<std::string, std::string> params = {{"cpu_budget", "0.01"},
{"max_budget", "0.0"},
{"initial_budget", "0.0"},
{"max_delay", "0.0"}};
const char kParamName[] = "ExpensiveBackgroundTimerThrottling";
const char kGroupName[] = "Enabled";
EXPECT_TRUE(base::AssociateFieldTrialParams(kParamName, kGroupName, params));
EXPECT_TRUE(base::FieldTrialList::CreateFieldTrial(kParamName, kGroupName));
std::map<std::string, std::string> actual_params;
base::GetFieldTrialParams(kParamName, &actual_params);
EXPECT_EQ(actual_params, params);
}
} // namespace
TEST_F(PageSchedulerImplTest, BackgroundTimerThrottling) {
ScopedExpensiveBackgroundTimerThrottlingForTest
budget_background_throttling_enabler(true);
std::unique_ptr<base::FieldTrialList> field_trial_list =
std::make_unique<base::FieldTrialList>(nullptr);
InitializeTrialParams();
page_scheduler_.reset(
new PageSchedulerImpl(nullptr, scheduler_.get(), false));
EXPECT_FALSE(page_scheduler_->IsThrottled());
std::vector<base::TimeTicks> run_times;
frame_scheduler_ = page_scheduler_->CreateFrameSchedulerImpl(
nullptr, FrameScheduler::FrameType::kSubframe);
page_scheduler_->SetPageVisible(true);
EXPECT_FALSE(page_scheduler_->IsThrottled());
FastForwardTo(base::TimeTicks() + base::TimeDelta::FromMilliseconds(2500));
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, test_task_runner_, &run_times),
base::TimeDelta::FromMilliseconds(1));
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, test_task_runner_, &run_times),
base::TimeDelta::FromMilliseconds(1));
FastForwardTo(base::TimeTicks() + base::TimeDelta::FromMilliseconds(3500));
// Check that these tasks are aligned, but are not subject to budget-based
// throttling.
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(2501),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(2751)));
run_times.clear();
page_scheduler_->SetPageVisible(false);
EXPECT_FALSE(page_scheduler_->IsThrottled());
// Ensure that the page is fully throttled.
FastForwardTo(base::TimeTicks() + base::TimeDelta::FromSeconds(15));
EXPECT_TRUE(page_scheduler_->IsThrottled());
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, test_task_runner_, &run_times),
base::TimeDelta::FromMicroseconds(1));
ThrottleableTaskQueue()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, test_task_runner_, &run_times),
base::TimeDelta::FromMicroseconds(1));
test_task_runner_->FastForwardUntilNoTasksRemain();
// Check that tasks are aligned and throttled.
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(16),
base::TimeTicks() + base::TimeDelta::FromSeconds(26)));
base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
}
TEST_F(PageSchedulerImplTest, OpenWebSocketExemptsFromBudgetThrottling) {
ScopedExpensiveBackgroundTimerThrottlingForTest
budget_background_throttling_enabler(true);
std::unique_ptr<base::FieldTrialList> field_trial_list =
std::make_unique<base::FieldTrialList>(nullptr);
InitializeTrialParams();
std::unique_ptr<PageSchedulerImpl> page_scheduler(
new PageSchedulerImpl(nullptr, scheduler_.get(), false));
std::vector<base::TimeTicks> run_times;
std::unique_ptr<FrameSchedulerImpl> frame_scheduler1 =
page_scheduler->CreateFrameSchedulerImpl(
nullptr, FrameScheduler::FrameType::kSubframe);
std::unique_ptr<FrameSchedulerImpl> frame_scheduler2 =
page_scheduler->CreateFrameSchedulerImpl(
nullptr, FrameScheduler::FrameType::kSubframe);
page_scheduler->SetPageVisible(false);
// Wait for 20s to avoid initial throttling delay.
FastForwardTo(base::TimeTicks() + base::TimeDelta::FromMilliseconds(20500));
for (size_t i = 0; i < 3; ++i) {
ThrottleableTaskQueueForScheduler(frame_scheduler1.get())
->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, test_task_runner_, &run_times),
base::TimeDelta::FromMilliseconds(1));
}
FastForwardTo(base::TimeTicks() + base::TimeDelta::FromMilliseconds(55500));
// Check that tasks are throttled.
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(21),
base::TimeTicks() + base::TimeDelta::FromSeconds(26),
base::TimeTicks() + base::TimeDelta::FromSeconds(51)));
run_times.clear();
std::unique_ptr<FrameScheduler::ActiveConnectionHandle> websocket_connection =
frame_scheduler1->OnActiveConnectionCreated();
for (size_t i = 0; i < 3; ++i) {
ThrottleableTaskQueueForScheduler(frame_scheduler1.get())
->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, test_task_runner_, &run_times),
base::TimeDelta::FromMilliseconds(1));
}
FastForwardTo(base::TimeTicks() + base::TimeDelta::FromMilliseconds(58500));
// Check that the timer task queue from the first frame is aligned,
// but not throttled.
EXPECT_THAT(
run_times,
ElementsAre(
base::TimeTicks() + base::TimeDelta::FromMilliseconds(56000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(56250),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(56500)));
run_times.clear();
for (size_t i = 0; i < 3; ++i) {
ThrottleableTaskQueueForScheduler(frame_scheduler2.get())
->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, test_task_runner_, &run_times),
base::TimeDelta::FromMilliseconds(1));
}
FastForwardTo(base::TimeTicks() + base::TimeDelta::FromMilliseconds(59500));
// Check that the second frame scheduler becomes unthrottled.
EXPECT_THAT(
run_times,
ElementsAre(
base::TimeTicks() + base::TimeDelta::FromMilliseconds(59000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(59250),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(59500)));
run_times.clear();
websocket_connection.reset();
// Wait for 10s to enable throttling back.
FastForwardTo(base::TimeTicks() + base::TimeDelta::FromMilliseconds(70500));
for (size_t i = 0; i < 3; ++i) {
ThrottleableTaskQueueForScheduler(frame_scheduler1.get())
->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, test_task_runner_, &run_times),
base::TimeDelta::FromMilliseconds(1));
}
test_task_runner_->FastForwardUntilNoTasksRemain();
// WebSocket is closed, budget-based throttling now applies.
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(84),
base::TimeTicks() + base::TimeDelta::FromSeconds(109),
base::TimeTicks() + base::TimeDelta::FromSeconds(134)));
base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
}
namespace {
void IncrementCounter(int* counter) {
++*counter;
}
} // namespace
TEST_F(PageSchedulerImplTest, PageFreeze) {
ScopedStopLoadingInBackgroundForTest stop_loading_enabler(true);
ScopedStopNonTimersInBackgroundForTest stop_non_timers_enabler(true);
int counter = 0;
LoadingTaskQueue()->PostTask(
FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
ThrottleableTaskQueue()->PostTask(
FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
DeferrableTaskQueue()->PostTask(
FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
PausableTaskQueue()->PostTask(
FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
UnpausableTaskQueue()->PostTask(
FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
page_scheduler_->SetPageVisible(false);
EXPECT_EQ(false, page_scheduler_->IsFrozen());
// In a backgrounded active page, all queues should run.
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_EQ(5, counter);
LoadingTaskQueue()->PostTask(
FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
ThrottleableTaskQueue()->PostTask(
FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
DeferrableTaskQueue()->PostTask(
FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
PausableTaskQueue()->PostTask(
FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
UnpausableTaskQueue()->PostTask(
FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
counter = 0;
page_scheduler_->SetPageFrozen(true);
EXPECT_EQ(true, page_scheduler_->IsFrozen());
// In a backgrounded frozen page, only Unpausable queue should run.
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_EQ(1, counter);
// A visible page should not be frozen.
page_scheduler_->SetPageVisible(true);
EXPECT_EQ(false, page_scheduler_->IsFrozen());
// Once the page is unfrozen, the rest of the queues should run.
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_EQ(5, counter);
}
TEST_F(PageSchedulerImplTest, AudioState) {
page_scheduler_->AudioStateChanged(true);
EXPECT_TRUE(page_scheduler_->IsAudioPlaying());
page_scheduler_->AudioStateChanged(false);
// We are audible for a certain period after raw signal disappearing.
EXPECT_TRUE(page_scheduler_->IsAudioPlaying());
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(3));
page_scheduler_->AudioStateChanged(false);
// We are still audible. A new call to AudioStateChanged shouldn't change
// anything.
EXPECT_TRUE(page_scheduler_->IsAudioPlaying());
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(3));
// Audio is finally silent.
EXPECT_FALSE(page_scheduler_->IsAudioPlaying());
}
TEST_F(PageSchedulerImplTest, PageSchedulerDestroyedWhileAudioChangePending) {
page_scheduler_->AudioStateChanged(true);
EXPECT_TRUE(page_scheduler_->IsAudioPlaying());
page_scheduler_->AudioStateChanged(false);
page_scheduler_.reset();
test_task_runner_->FastForwardUntilNoTasksRemain();
}
TEST_F(PageSchedulerImplTest, AudiblePagesAreNotThrottled) {
page_scheduler_->SetPageVisible(false);
EXPECT_TRUE(scheduler_->task_queue_throttler()->IsThrottled(
ThrottleableTaskQueue().get()));
// No throttling when the page is audible.
page_scheduler_->AudioStateChanged(true);
EXPECT_FALSE(scheduler_->task_queue_throttler()->IsThrottled(
ThrottleableTaskQueue().get()));
// No throttling for some time after audio signal disappears.
page_scheduler_->AudioStateChanged(false);
EXPECT_FALSE(scheduler_->task_queue_throttler()->IsThrottled(
ThrottleableTaskQueue().get()));
// Eventually throttling is reenabled again.
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_TRUE(scheduler_->task_queue_throttler()->IsThrottled(
ThrottleableTaskQueue().get()));
}
TEST_F(PageSchedulerImplTest, BudgetBasedThrottlingForPageScheduler) {
page_scheduler_->SetPageVisible(false);
}
TEST_F(PageSchedulerImplTest, KeepActiveSetForNewPages) {
scheduler_->SetSchedulerKeepActive(true);
std::unique_ptr<PageSchedulerImpl> page_scheduler2 =
std::make_unique<PageSchedulerImpl>(nullptr, scheduler_.get(), false);
EXPECT_TRUE(page_scheduler_->KeepActive());
EXPECT_TRUE(page_scheduler2->KeepActive());
}
} // namespace page_scheduler_impl_unittest
} // namespace scheduler
} // namespace blink