diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:20:33 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:28:57 +0000 |
commit | d17ea114e5ef69ad5d5d7413280a13e6428098aa (patch) | |
tree | 2c01a75df69f30d27b1432467cfe7c1467a498da /chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc | |
parent | 8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (diff) | |
download | qtwebengine-chromium-d17ea114e5ef69ad5d5d7413280a13e6428098aa.tar.gz |
BASELINE: Update Chromium to 67.0.3396.47
Change-Id: Idcb1341782e417561a2473eeecc82642dafda5b7
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc | 1174 |
1 files changed, 1174 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc new file mode 100644 index 00000000000..0b5260dbb29 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc @@ -0,0 +1,1174 @@ +// 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/strings/stringprintf.h" +#include "base/test/simple_test_tick_clock.h" +#include "components/viz/test/ordered_simple_task_runner.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/scheduler/child/task_runner_impl.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/scheduler/test/task_queue_manager_for_test.h" +#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" + +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; + + void SetUp() override { + clock_.Advance(base::TimeDelta::FromMicroseconds(5000)); + mock_task_runner_ = + base::MakeRefCounted<cc::OrderedSimpleTaskRunner>(&clock_, true); + scheduler_.reset(new MainThreadSchedulerImpl( + TaskQueueManagerForTest::Create(nullptr, mock_task_runner_, &clock_), + base::nullopt)); + page_scheduler_.reset(new PageSchedulerImpl( + nullptr, scheduler_.get(), DisableBackgroundTimerThrottling())); + frame_scheduler_ = page_scheduler_->CreateFrameSchedulerImpl( + nullptr, FrameScheduler::FrameType::kSubframe); + } + + void TearDown() override { + frame_scheduler_.reset(); + page_scheduler_.reset(); + scheduler_->Shutdown(); + scheduler_.reset(); + } + + virtual bool DisableBackgroundTimerThrottling() const { return false; } + + protected: + static scoped_refptr<TaskQueue> ThrottleableTaskQueueForScheduler( + FrameSchedulerImpl* scheduler) { + return scheduler->ThrottleableTaskQueue(); + } + + scoped_refptr<base::SingleThreadTaskRunner> ThrottleableTaskRunner() { + return TaskRunnerImpl::Create(ThrottleableTaskQueue(), + TaskType::kInternalTest); + } + + scoped_refptr<base::SingleThreadTaskRunner> LoadingTaskRunner() { + return TaskRunnerImpl::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(); + } + + base::SimpleTestTickClock clock_; + scoped_refptr<cc::OrderedSimpleTaskRunner> mock_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::OnceClosure MakeRepeatingTask(scoped_refptr<TaskQueue> task_queue, + int* run_count) { + return base::BindOnce(&RunRepeatingTask, std::move(task_queue), + base::Unretained(run_count)); +} + +void RunRepeatingTask(scoped_refptr<TaskQueue> task_queue, int* run_count) { + ++*run_count; + TaskQueue* task_queue_ptr = task_queue.get(); + task_queue_ptr->PostDelayedTask( + FROM_HERE, MakeRepeatingTask(std::move(task_queue_ptr), run_count), + base::TimeDelta::FromMilliseconds(1)); +} + +} // 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)); + + mock_task_runner_->RunForPeriod(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(1)); + + mock_task_runner_->RunForPeriod(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; + mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1)); + EXPECT_EQ(1001, run_count); // Note we end up running 1001 here because the + // task was posted while throttled with a delay of 1ms so the first task was + // due to run before the 1s period started. +} + +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)); + + mock_task_runner_->RunForPeriod(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(1)); + ThrottleableTaskQueueForScheduler(frame_scheduler2.get()) + ->PostDelayedTask(FROM_HERE, + MakeRepeatingTask(ThrottleableTaskQueueForScheduler( + frame_scheduler2.get()), + &run_count2), + base::TimeDelta::FromMilliseconds(1)); + + mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1)); + EXPECT_EQ(1000, run_count1); + EXPECT_EQ(1, run_count2); +} + +namespace { + +void RunVirtualTimeRecorderTask( + base::SimpleTestTickClock* 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( + base::SimpleTestTickClock* clock, + MainThreadSchedulerImpl* scheduler, + std::vector<base::TimeTicks>* out_real_times, + std::vector<base::TimeTicks>* out_virtual_times) { + return WTF::Bind(&RunVirtualTimeRecorderTask, WTF::Unretained(clock), + WTF::Unretained(scheduler), WTF::Unretained(out_real_times), + WTF::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(&clock_, scheduler_.get(), &real_times, + &virtual_times), + base::TimeDelta::FromMilliseconds(2)); + + ThrottleableTaskRunner()->PostDelayedTask( + FROM_HERE, + MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times, + &virtual_times), + base::TimeDelta::FromMilliseconds(20)); + + ThrottleableTaskRunner()->PostDelayedTask( + FROM_HERE, + MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times, + &virtual_times), + base::TimeDelta::FromMilliseconds(200)); + + mock_task_runner_->RunUntilIdle(); + + 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(&clock_, scheduler_.get(), &real_times, + &virtual_times), + base::TimeDelta::FromMilliseconds(2)); + + LoadingTaskRunner()->PostDelayedTask( + FROM_HERE, + MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times, + &virtual_times), + base::TimeDelta::FromMilliseconds(20)); + + LoadingTaskRunner()->PostDelayedTask( + FROM_HERE, + MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times, + &virtual_times), + base::TimeDelta::FromMilliseconds(200)); + + mock_task_runner_->RunUntilIdle(); + + 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)); + + mock_task_runner_->RunTasksWhile(mock_task_runner_->TaskRunCountBelow(2000)); + // Virtual time means page visibility is ignored. + EXPECT_EQ(1999, 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)); + + mock_task_runner_->RunUntilIdle(); + + // 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)); + + mock_task_runner_->RunUntilIdle(); + + EXPECT_THAT(run_order, ElementsAre(0, 1, 2, 3, 4)); +} + +class PageSchedulerImplTestWithDisabledBackgroundTimerThrottling + : public PageSchedulerImplTest { + public: + PageSchedulerImplTestWithDisabledBackgroundTimerThrottling() = default; + ~PageSchedulerImplTestWithDisabledBackgroundTimerThrottling() override = + default; + + bool DisableBackgroundTimerThrottling() const override { return true; } +}; + +TEST_F(PageSchedulerImplTestWithDisabledBackgroundTimerThrottling, + RepeatingTimer_PageInBackground) { + page_scheduler_->SetPageVisible(false); + + int run_count = 0; + ThrottleableTaskQueue()->PostDelayedTask( + FROM_HERE, MakeRepeatingTask(ThrottleableTaskQueue(), &run_count), + base::TimeDelta::FromMilliseconds(1)); + + mock_task_runner_->RunForPeriod(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)); + + mock_task_runner_->RunUntilIdle(); + EXPECT_TRUE(run_order.empty()); + + page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kAdvance); + mock_task_runner_->RunUntilIdle(); + + 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)); + } + mock_task_runner_->RunUntilIdle(); +} + +TEST_F(PageSchedulerImplTest, DeletePageScheduler_InTask) { + ThrottleableTaskQueue()->PostTask( + FROM_HERE, MakeDeletionTask(page_scheduler_.release())); + mock_task_runner_->RunUntilIdle(); +} + +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(1)); + + // 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)); + + mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(100)); + EXPECT_EQ(90015, 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, + WTF::Bind(&PauseAndUnpauseVirtualTime, WTF::Unretained(scheduler_.get()), + WTF::Unretained(frame_scheduler.get()), + WTF::Unretained(&time_paused), WTF::Unretained(&time_unpaused)), + base::TimeDelta::FromMilliseconds(3)); + + // Will run after the first task has advanced virtual time past 5ms. + ThrottleableTaskRunner()->PostDelayedTask( + FROM_HERE, + WTF::Bind(&RecordVirtualTime, WTF::Unretained(scheduler_.get()), + WTF::Unretained(&time_second_task)), + base::TimeDelta::FromMilliseconds(5)); + + mock_task_runner_->RunUntilIdle(); + + 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))); + + mock_task_runner_->RunUntilIdle(); + EXPECT_TRUE(run_order.empty()); + + page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kAdvance); + mock_task_runner_->RunUntilIdle(); + + 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(&clock_, scheduler_.get(), &real_times, + &virtual_times), + base::TimeDelta::FromMilliseconds(1)); + + ThrottleableTaskRunner()->PostDelayedTask( + FROM_HERE, + MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times, + &virtual_times), + base::TimeDelta::FromMilliseconds(2)); + + ThrottleableTaskRunner()->PostDelayedTask( + FROM_HERE, + MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times, + &virtual_times), + base::TimeDelta::FromMilliseconds(5)); + + ThrottleableTaskRunner()->PostDelayedTask( + FROM_HERE, + MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times, + &virtual_times), + base::TimeDelta::FromMilliseconds(7)); + + page_scheduler_->GrantVirtualTimeBudget( + base::TimeDelta::FromMilliseconds(5), + WTF::Bind( + [](PageScheduler* scheduler) { + scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause); + }, + WTF::Unretained(page_scheduler_.get()))); + + mock_task_runner_->RunUntilIdle(); + + // 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), + WTF::Bind( + [](PageScheduler* scheduler) { + scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause); + }, + WTF::Unretained(page_scheduler_.get()))); + + mock_task_runner_->RunUntilIdle(); + + 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), + WTF::Bind( + [](PageScheduler* scheduler) { + scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause); + }, + WTF::Unretained(page_scheduler_.get()))); + + mock_task_runner_->RunUntilIdle(); + + // 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, WTF::Unretained(&count), + WTF::Unretained(&delayed_task_run_at_count)), + base::TimeDelta::FromMilliseconds(10)); + + page_scheduler_->GrantVirtualTimeBudget( + base::TimeDelta::FromMilliseconds(1000), + WTF::Bind( + [](PageScheduler* scheduler) { + scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause); + }, + WTF::Unretained(page_scheduler_.get()))); + + mock_task_runner_->RunUntilIdle(); + + 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, WTF::Unretained(&count), + WTF::Unretained(&delayed_task_run_at_count)), + base::TimeDelta::FromMilliseconds(10)); + + page_scheduler_->GrantVirtualTimeBudget( + base::TimeDelta::FromMilliseconds(1000), + WTF::Bind( + [](PageScheduler* scheduler) { + scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause); + }, + WTF::Unretained(page_scheduler_.get()))); + + mock_task_runner_->RunUntilIdle(); + + 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(base::SimpleTestTickClock* clock, + std::vector<base::TimeTicks>* run_times) { + run_times->push_back(clock->NowTicks()); + clock->Advance(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)); + + std::vector<base::TimeTicks> run_times; + frame_scheduler_ = page_scheduler_->CreateFrameSchedulerImpl( + nullptr, FrameScheduler::FrameType::kSubframe); + page_scheduler_->SetPageVisible(true); + + mock_task_runner_->RunUntilTime(base::TimeTicks() + + base::TimeDelta::FromMilliseconds(2500)); + + ThrottleableTaskQueue()->PostDelayedTask( + FROM_HERE, base::BindOnce(&ExpensiveTestTask, &clock_, &run_times), + base::TimeDelta::FromMilliseconds(1)); + ThrottleableTaskQueue()->PostDelayedTask( + FROM_HERE, base::BindOnce(&ExpensiveTestTask, &clock_, &run_times), + base::TimeDelta::FromMilliseconds(1)); + + mock_task_runner_->RunUntilTime(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); + + ThrottleableTaskQueue()->PostDelayedTask( + FROM_HERE, base::BindOnce(&ExpensiveTestTask, &clock_, &run_times), + base::TimeDelta::FromMicroseconds(1)); + ThrottleableTaskQueue()->PostDelayedTask( + FROM_HERE, base::BindOnce(&ExpensiveTestTask, &clock_, &run_times), + base::TimeDelta::FromMicroseconds(1)); + + mock_task_runner_->RunUntilIdle(); + + // Check that tasks are aligned and throttled. + EXPECT_THAT( + run_times, + ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(4), + 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. + mock_task_runner_->RunUntilTime(base::TimeTicks() + + base::TimeDelta::FromMilliseconds(20500)); + + for (size_t i = 0; i < 3; ++i) { + ThrottleableTaskQueueForScheduler(frame_scheduler1.get()) + ->PostDelayedTask( + FROM_HERE, base::BindOnce(&ExpensiveTestTask, &clock_, &run_times), + base::TimeDelta::FromMilliseconds(1)); + } + + mock_task_runner_->RunUntilTime(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, &clock_, &run_times), + base::TimeDelta::FromMilliseconds(1)); + } + + mock_task_runner_->RunUntilTime(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, &clock_, &run_times), + base::TimeDelta::FromMilliseconds(1)); + } + + mock_task_runner_->RunUntilTime(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. + mock_task_runner_->RunUntilTime(base::TimeTicks() + + base::TimeDelta::FromMilliseconds(70500)); + + for (size_t i = 0; i < 3; ++i) { + ThrottleableTaskQueueForScheduler(frame_scheduler1.get()) + ->PostDelayedTask( + FROM_HERE, base::BindOnce(&ExpensiveTestTask, &clock_, &run_times), + base::TimeDelta::FromMilliseconds(1)); + } + + mock_task_runner_->RunUntilIdle(); + + // 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. + mock_task_runner_->RunUntilIdle(); + 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. + mock_task_runner_->RunUntilIdle(); + 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. + mock_task_runner_->RunUntilIdle(); + EXPECT_EQ(5, counter); +} + +} // namespace page_scheduler_impl_unittest +} // namespace scheduler +} // namespace blink |