diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc | 1185 |
1 files changed, 1185 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc new file mode 100644 index 00000000000..52f09b80ab1 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc @@ -0,0 +1,1185 @@ +// 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/common/idle_helper.h" + +#include <utility> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/task/sequence_manager/sequence_manager.h" +#include "base/task/sequence_manager/task_queue.h" +#include "base/task/sequence_manager/test/sequence_manager_for_test.h" +#include "base/task/sequence_manager/time_domain.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/common/scheduler_helper.h" +#include "third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_helper.h" + +using testing::_; +using testing::AnyNumber; +using testing::AtLeast; +using testing::Exactly; +using testing::Invoke; +using testing::Return; + +namespace blink { +namespace scheduler { +// To avoid symbol collisions in jumbo builds. +namespace idle_helper_unittest { + +using base::sequence_manager::TaskQueue; +using base::sequence_manager::SequenceManager; + +void AppendToVectorTestTask(std::vector<std::string>* vector, + std::string value) { + vector->push_back(value); +} + +void AppendToVectorIdleTestTask(std::vector<std::string>* vector, + std::string value, + base::TimeTicks deadline) { + AppendToVectorTestTask(vector, value); +} + +void NullTask() {} + +void NullIdleTask(base::TimeTicks deadline) {} + +void AppendToVectorReentrantTask(base::SingleThreadTaskRunner* task_runner, + std::vector<int>* vector, + int* reentrant_count, + int max_reentrant_count) { + vector->push_back((*reentrant_count)++); + if (*reentrant_count < max_reentrant_count) { + task_runner->PostTask(FROM_HERE, + base::BindOnce(AppendToVectorReentrantTask, + base::Unretained(task_runner), vector, + reentrant_count, max_reentrant_count)); + } +} + +void IdleTestTask(int* run_count, + base::TimeTicks* deadline_out, + base::TimeTicks deadline) { + (*run_count)++; + *deadline_out = deadline; +} + +int g_max_idle_task_reposts = 2; + +void RepostingIdleTestTask(SingleThreadIdleTaskRunner* idle_task_runner, + int* run_count, + base::TimeTicks* deadline_out, + base::TimeTicks deadline) { + if ((*run_count + 1) < g_max_idle_task_reposts) { + idle_task_runner->PostIdleTask( + FROM_HERE, base::BindOnce(&RepostingIdleTestTask, + base::Unretained(idle_task_runner), run_count, + deadline_out)); + } + *deadline_out = deadline; + (*run_count)++; +} + +void RepostingUpdateClockIdleTestTask( + SingleThreadIdleTaskRunner* idle_task_runner, + int* run_count, + scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner, + base::TimeDelta advance_time, + std::vector<base::TimeTicks>* deadlines, + base::TimeTicks deadline) { + if ((*run_count + 1) < g_max_idle_task_reposts) { + idle_task_runner->PostIdleTask( + FROM_HERE, base::BindOnce(&RepostingUpdateClockIdleTestTask, + base::Unretained(idle_task_runner), run_count, + test_task_runner, advance_time, deadlines)); + } + deadlines->push_back(deadline); + (*run_count)++; + test_task_runner->AdvanceMockTickClock(advance_time); +} + +void RepeatingTask(base::SingleThreadTaskRunner* task_runner, + int num_repeats, + base::TimeDelta delay) { + if (num_repeats > 1) { + task_runner->PostDelayedTask( + FROM_HERE, + base::BindOnce(&RepeatingTask, base::Unretained(task_runner), + num_repeats - 1, delay), + delay); + } +} + +void UpdateClockIdleTestTask( + scoped_refptr<base::TestMockTimeTaskRunner> task_runner, + int* run_count, + base::TimeTicks set_time, + base::TimeTicks deadline) { + task_runner->AdvanceMockTickClock(set_time - task_runner->NowTicks()); + (*run_count)++; +} + +void UpdateClockToDeadlineIdleTestTask( + scoped_refptr<base::TestMockTimeTaskRunner> task_runner, + int* run_count, + base::TimeTicks deadline) { + UpdateClockIdleTestTask(task_runner, run_count, deadline, deadline); +} + +void EndIdlePeriodIdleTask(IdleHelper* idle_helper, base::TimeTicks deadline) { + idle_helper->EndIdlePeriod(); +} + +void ShutdownIdleTask(IdleHelper* helper, + bool* shutdown_task_run, + base::TimeTicks deadline) { + *shutdown_task_run = true; + helper->Shutdown(); +} + +class IdleHelperForTest : public IdleHelper, public IdleHelper::Delegate { + public: + explicit IdleHelperForTest( + SchedulerHelper* scheduler_helper, + base::TimeDelta required_quiescence_duration_before_long_idle_period, + scoped_refptr<TaskQueue> idle_task_runner) + : IdleHelper(scheduler_helper, + this, + "TestSchedulerIdlePeriod", + required_quiescence_duration_before_long_idle_period, + idle_task_runner) {} + + ~IdleHelperForTest() override = default; + + // IdleHelper::Delegate implementation: + MOCK_METHOD2(CanEnterLongIdlePeriod, + bool(base::TimeTicks now, + base::TimeDelta* next_long_idle_period_delay_out)); + + MOCK_METHOD0(IsNotQuiescent, void()); + MOCK_METHOD0(OnIdlePeriodStarted, void()); + MOCK_METHOD0(OnIdlePeriodEnded, void()); + MOCK_METHOD1(OnPendingTasksChanged, void(bool has_tasks)); +}; + +class BaseIdleHelperTest : public testing::Test { + public: + BaseIdleHelperTest( + std::unique_ptr<base::MessageLoop> message_loop, + base::TimeDelta required_quiescence_duration_before_long_idle_period) + : message_loop_(std::move(message_loop)), + test_task_runner_(base::MakeRefCounted<base::TestMockTimeTaskRunner>( + base::TestMockTimeTaskRunner::Type::kStandalone)) { + std::unique_ptr<SequenceManager> sequence_manager; + if (!message_loop_) { + sequence_manager = base::sequence_manager::SequenceManagerForTest::Create( + nullptr, test_task_runner_, test_task_runner_->GetMockTickClock()); + } else { + // It's okay to use |test_task_runner_| just as a mock clock because + // it isn't bound to thread and all tasks will go through a MessageLoop. + sequence_manager = base::sequence_manager::SequenceManagerForTest::Create( + message_loop_.get(), message_loop_->task_runner(), + test_task_runner_->GetMockTickClock()); + } + sequence_manager_ = sequence_manager.get(); + scheduler_helper_ = std::make_unique<NonMainThreadSchedulerHelper>( + std::move(sequence_manager), nullptr, TaskType::kInternalTest); + idle_helper_ = std::make_unique<IdleHelperForTest>( + scheduler_helper_.get(), + required_quiescence_duration_before_long_idle_period, + scheduler_helper_->NewTaskQueue(TaskQueue::Spec("idle_test"))); + default_task_runner_ = scheduler_helper_->DefaultNonMainThreadTaskQueue(); + idle_task_runner_ = idle_helper_->IdleTaskRunner(); + test_task_runner_->AdvanceMockTickClock( + base::TimeDelta::FromMicroseconds(5000)); + } + + ~BaseIdleHelperTest() override = default; + + void SetUp() override { + EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(AnyNumber()); + EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(AnyNumber()); + EXPECT_CALL(*idle_helper_, CanEnterLongIdlePeriod(_, _)) + .Times(AnyNumber()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(_)).Times(AnyNumber()); + } + + void TearDown() override { + EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(AnyNumber()); + idle_helper_->Shutdown(); + test_task_runner_->FastForwardUntilNoTasksRemain(); + } + + SequenceManager* sequence_manager() const { return sequence_manager_; } + + template <typename E> + static void CallForEachEnumValue(E first, + E last, + const char* (*function)(E)) { + for (E val = first; val < last; + val = static_cast<E>(static_cast<int>(val) + 1)) { + (*function)(val); + } + } + + static void CheckAllTaskQueueIdToString() { + CallForEachEnumValue<IdleHelper::IdlePeriodState>( + IdleHelper::IdlePeriodState::kFirstIdlePeriodState, + IdleHelper::IdlePeriodState::kIdlePeriodStateCount, + &IdleHelper::IdlePeriodStateToString); + } + + bool IsInIdlePeriod() const { + return idle_helper_->IsInIdlePeriod( + idle_helper_->SchedulerIdlePeriodState()); + } + + protected: + static base::TimeDelta maximum_idle_period_duration() { + return base::TimeDelta::FromMilliseconds( + IdleHelper::kMaximumIdlePeriodMillis); + } + + static base::TimeDelta retry_enable_long_idle_period_delay() { + return base::TimeDelta::FromMilliseconds( + IdleHelper::kRetryEnableLongIdlePeriodDelayMillis); + } + + static base::TimeDelta minimum_idle_period_duration() { + return base::TimeDelta::FromMilliseconds( + IdleHelper::kMinimumIdlePeriodDurationMillis); + } + + base::TimeTicks CurrentIdleTaskDeadline() { + return idle_helper_->CurrentIdleTaskDeadline(); + } + + void CheckIdlePeriodStateIs(const char* expected) { + EXPECT_STREQ(expected, IdleHelper::IdlePeriodStateToString( + idle_helper_->SchedulerIdlePeriodState())); + } + + const scoped_refptr<TaskQueue>& idle_queue() const { + return idle_helper_->idle_queue_; + } + + std::unique_ptr<base::MessageLoop> message_loop_; + scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_; + std::unique_ptr<NonMainThreadSchedulerHelper> scheduler_helper_; + SequenceManager* sequence_manager_; // Owned by scheduler_helper_. + std::unique_ptr<IdleHelperForTest> idle_helper_; + scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_; + scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner_; + + DISALLOW_COPY_AND_ASSIGN(BaseIdleHelperTest); +}; + +class IdleHelperTest : public BaseIdleHelperTest { + public: + IdleHelperTest() : BaseIdleHelperTest(nullptr, base::TimeDelta()) {} + + ~IdleHelperTest() override = default; + + private: + DISALLOW_COPY_AND_ASSIGN(IdleHelperTest); +}; + +TEST_F(IdleHelperTest, TestPostIdleTask) { + int run_count = 0; + base::TimeTicks expected_deadline = + test_task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(2300); + base::TimeTicks deadline_in_task; + + test_task_runner_->AdvanceMockTickClock( + base::TimeDelta::FromMilliseconds(100)); + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(0, run_count); + + idle_helper_->StartIdlePeriod(IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + expected_deadline); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, run_count); + EXPECT_EQ(expected_deadline, deadline_in_task); +} + +TEST_F(IdleHelperTest, TestPostIdleTask_EndIdlePeriod) { + int run_count = 0; + base::TimeTicks deadline_in_task; + + test_task_runner_->AdvanceMockTickClock( + base::TimeDelta::FromMilliseconds(100)); + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(0, run_count); + + idle_helper_->StartIdlePeriod( + IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + test_task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(10)); + idle_helper_->EndIdlePeriod(); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(0, run_count); +} + +TEST_F(IdleHelperTest, TestRepostingIdleTask) { + base::TimeTicks actual_deadline; + int run_count = 0; + + g_max_idle_task_reposts = 2; + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&RepostingIdleTestTask, + base::RetainedRef(idle_task_runner_), + &run_count, &actual_deadline)); + idle_helper_->StartIdlePeriod( + IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + test_task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(10)); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, run_count); + + // Reposted tasks shouldn't run until next idle period. + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, run_count); + + idle_helper_->StartIdlePeriod( + IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + test_task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(10)); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(2, run_count); +} + +TEST_F(IdleHelperTest, TestIdleTaskExceedsDeadline) { + int run_count = 0; + + // Post two UpdateClockToDeadlineIdleTestTask tasks. + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&UpdateClockToDeadlineIdleTestTask, + test_task_runner_, &run_count)); + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&UpdateClockToDeadlineIdleTestTask, + test_task_runner_, &run_count)); + + idle_helper_->StartIdlePeriod( + IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + test_task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(10)); + test_task_runner_->RunUntilIdle(); + // Only the first idle task should execute since it's used up the deadline. + EXPECT_EQ(1, run_count); + + idle_helper_->EndIdlePeriod(); + idle_helper_->StartIdlePeriod( + IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + test_task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(10)); + test_task_runner_->RunUntilIdle(); + // Second task should be run on the next idle period. + EXPECT_EQ(2, run_count); +} + +class IdleHelperTestWithIdlePeriodObserver : public BaseIdleHelperTest { + public: + IdleHelperTestWithIdlePeriodObserver() + : BaseIdleHelperTest(nullptr, base::TimeDelta()) {} + + ~IdleHelperTestWithIdlePeriodObserver() override = default; + + void SetUp() override { + EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(_)).Times(AnyNumber()); + } + + void ExpectIdlePeriodStartsButNeverEnds() { + EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(1); + EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(0); + } + + void ExpectIdlePeriodStartsAndEnds(const testing::Cardinality& cardinality) { + EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(cardinality); + EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(cardinality); + } + + private: + DISALLOW_COPY_AND_ASSIGN(IdleHelperTestWithIdlePeriodObserver); +}; + +TEST_F(IdleHelperTestWithIdlePeriodObserver, TestEnterButNotExitIdlePeriod) { + ExpectIdlePeriodStartsButNeverEnds(); + + idle_helper_->StartIdlePeriod( + IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + test_task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(10)); +} + +TEST_F(IdleHelperTestWithIdlePeriodObserver, TestEnterAndExitIdlePeriod) { + BaseIdleHelperTest* fixture = this; + ON_CALL(*idle_helper_, OnIdlePeriodStarted()) + .WillByDefault( + Invoke([fixture]() { EXPECT_TRUE(fixture->IsInIdlePeriod()); })); + ON_CALL(*idle_helper_, OnIdlePeriodEnded()).WillByDefault(Invoke([fixture]() { + EXPECT_FALSE(fixture->IsInIdlePeriod()); + })); + + ExpectIdlePeriodStartsAndEnds(Exactly(1)); + + idle_helper_->StartIdlePeriod( + IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + test_task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(10)); + idle_helper_->EndIdlePeriod(); +} + +class IdleHelperWithMessageLoopTest : public BaseIdleHelperTest { + public: + IdleHelperWithMessageLoopTest() + : BaseIdleHelperTest(std::make_unique<base::MessageLoop>(), + base::TimeDelta()) {} + ~IdleHelperWithMessageLoopTest() override = default; + + void PostFromNestedRunloop( + std::vector<std::pair<SingleThreadIdleTaskRunner::IdleTask, bool>>* + tasks) { + for (std::pair<SingleThreadIdleTaskRunner::IdleTask, bool>& pair : *tasks) { + if (pair.second) { + idle_task_runner_->PostIdleTask(FROM_HERE, std::move(pair.first)); + } else { + idle_task_runner_->PostNonNestableIdleTask(FROM_HERE, + std::move(pair.first)); + } + } + idle_helper_->StartIdlePeriod( + IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + test_task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(10)); + base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle(); + } + + void SetUp() override { + EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(AnyNumber()); + EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(AnyNumber()); + EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(_)).Times(AnyNumber()); + } + + private: + DISALLOW_COPY_AND_ASSIGN(IdleHelperWithMessageLoopTest); +}; + +TEST_F(IdleHelperWithMessageLoopTest, + NonNestableIdleTaskDoesntExecuteInNestedLoop) { + std::vector<std::string> order; + idle_task_runner_->PostIdleTask( + FROM_HERE, + base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("1"))); + idle_task_runner_->PostIdleTask( + FROM_HERE, + base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("2"))); + + std::vector<std::pair<SingleThreadIdleTaskRunner::IdleTask, bool>> + tasks_to_post_from_nested_loop; + tasks_to_post_from_nested_loop.push_back(std::make_pair( + base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("3")), + false)); + tasks_to_post_from_nested_loop.push_back(std::make_pair( + base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("4")), + true)); + tasks_to_post_from_nested_loop.push_back(std::make_pair( + base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("5")), + true)); + + default_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&IdleHelperWithMessageLoopTest::PostFromNestedRunloop, + base::Unretained(this), + base::Unretained(&tasks_to_post_from_nested_loop))); + + idle_helper_->StartIdlePeriod( + IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + test_task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(10)); + base::RunLoop().RunUntilIdle(); + // Note we expect task 3 to run last because it's non-nestable. + EXPECT_THAT(order, testing::ElementsAre(std::string("1"), std::string("2"), + std::string("4"), std::string("5"), + std::string("3"))); +} + +TEST_F(IdleHelperTestWithIdlePeriodObserver, TestLongIdlePeriod) { + base::TimeTicks expected_deadline = + test_task_runner_->NowTicks() + maximum_idle_period_duration(); + base::TimeTicks deadline_in_task; + int run_count = 0; + + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + + EXPECT_CALL(*idle_helper_, CanEnterLongIdlePeriod(_, _)) + .Times(1) + .WillRepeatedly(Return(true)); + ExpectIdlePeriodStartsButNeverEnds(); + + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(0, run_count); // Shouldn't run yet as no idle period. + + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, run_count); // Should have run in a long idle time. + EXPECT_EQ(expected_deadline, deadline_in_task); +} + +TEST_F(IdleHelperTest, TestLongIdlePeriodWithPendingDelayedTask) { + base::TimeDelta pending_task_delay = base::TimeDelta::FromMilliseconds(30); + base::TimeTicks expected_deadline = + test_task_runner_->NowTicks() + pending_task_delay; + base::TimeTicks deadline_in_task; + int run_count = 0; + + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask), + pending_task_delay); + + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, run_count); // Should have run in a long idle time. + EXPECT_EQ(expected_deadline, deadline_in_task); +} + +TEST_F(IdleHelperTest, TestLongIdlePeriodWithLatePendingDelayedTask) { + base::TimeDelta pending_task_delay = base::TimeDelta::FromMilliseconds(10); + base::TimeTicks deadline_in_task; + int run_count = 0; + + default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask), + pending_task_delay); + + // Advance clock until after delayed task was meant to be run. + test_task_runner_->AdvanceMockTickClock( + base::TimeDelta::FromMilliseconds(20)); + + // Post an idle task and then EnableLongIdlePeriod. Since there is a late + // pending delayed task this shouldn't actually start an idle period. + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(0, run_count); + + // After the delayed task has been run we should trigger an idle period. + test_task_runner_->AdvanceMockTickClock(maximum_idle_period_duration()); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, run_count); +} + +TEST_F(IdleHelperTestWithIdlePeriodObserver, TestLongIdlePeriodRepeating) { + std::vector<base::TimeTicks> actual_deadlines; + int run_count = 0; + + EXPECT_CALL(*idle_helper_, CanEnterLongIdlePeriod(_, _)) + .Times(4) + .WillRepeatedly(Return(true)); + ExpectIdlePeriodStartsAndEnds(AtLeast(2)); + + g_max_idle_task_reposts = 3; + base::TimeTicks clock_before(test_task_runner_->NowTicks()); + base::TimeDelta idle_task_runtime(base::TimeDelta::FromMilliseconds(10)); + idle_task_runner_->PostIdleTask( + FROM_HERE, + base::BindOnce(&RepostingUpdateClockIdleTestTask, + base::RetainedRef(idle_task_runner_), &run_count, + test_task_runner_, idle_task_runtime, &actual_deadlines)); + + // Check each idle task runs in their own idle period. + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->FastForwardUntilNoTasksRemain(); + EXPECT_EQ(3, run_count); + EXPECT_THAT( + actual_deadlines, + testing::ElementsAre(clock_before + maximum_idle_period_duration(), + clock_before + 2 * maximum_idle_period_duration(), + clock_before + 3 * maximum_idle_period_duration())); + + g_max_idle_task_reposts = 5; + idle_task_runner_->PostIdleTask( + FROM_HERE, + base::BindOnce(&RepostingUpdateClockIdleTestTask, + base::RetainedRef(idle_task_runner_), &run_count, + test_task_runner_, idle_task_runtime, &actual_deadlines)); + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&EndIdlePeriodIdleTask, + base::Unretained(idle_helper_.get()))); + + // Ensure that reposting tasks stop after EndIdlePeriod is called. + test_task_runner_->FastForwardUntilNoTasksRemain(); + EXPECT_EQ(4, run_count); +} + +TEST_F(IdleHelperTestWithIdlePeriodObserver, + TestLongIdlePeriodWhenNotCanEnterLongIdlePeriod) { + base::TimeDelta delay = base::TimeDelta::FromMilliseconds(1000); + base::TimeDelta half_delay = base::TimeDelta::FromMilliseconds(500); + base::TimeTicks delay_over = test_task_runner_->NowTicks() + delay; + base::TimeTicks deadline_in_task; + int run_count = 0; + + ON_CALL(*idle_helper_, CanEnterLongIdlePeriod(_, _)) + .WillByDefault( + Invoke([delay, delay_over]( + base::TimeTicks now, + base::TimeDelta* next_long_idle_period_delay_out) { + if (now >= delay_over) + return true; + *next_long_idle_period_delay_out = delay; + return false; + })); + + EXPECT_CALL(*idle_helper_, CanEnterLongIdlePeriod(_, _)).Times(2); + EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(AnyNumber()); + + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + + // Make sure Idle tasks don't run until the delay has occurred. + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(0, run_count); + + test_task_runner_->AdvanceMockTickClock(half_delay); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(0, run_count); + + // Delay is finished, idle task should run. + test_task_runner_->AdvanceMockTickClock(half_delay); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, run_count); +} + +TEST_F(IdleHelperTest, + TestLongIdlePeriodDoesNotImmediatelyRestartIfMaxDeadline) { + std::vector<base::TimeTicks> actual_deadlines; + int run_count = 0; + + base::TimeTicks clock_before(test_task_runner_->NowTicks()); + base::TimeDelta idle_task_runtime(base::TimeDelta::FromMilliseconds(10)); + + // The second idle period should happen immediately after the first the + // they have max deadlines. + g_max_idle_task_reposts = 2; + idle_task_runner_->PostIdleTask( + FROM_HERE, + base::BindOnce(&RepostingUpdateClockIdleTestTask, + base::RetainedRef(idle_task_runner_), &run_count, + test_task_runner_, idle_task_runtime, &actual_deadlines)); + + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->FastForwardUntilNoTasksRemain(); + EXPECT_EQ(2, run_count); + EXPECT_THAT( + actual_deadlines, + testing::ElementsAre(clock_before + maximum_idle_period_duration(), + clock_before + 2 * maximum_idle_period_duration())); +} + +TEST_F(IdleHelperTest, TestLongIdlePeriodRestartWaitsIfNotMaxDeadline) { + base::TimeTicks actual_deadline; + int run_count = 0; + + base::TimeDelta pending_task_delay(base::TimeDelta::FromMilliseconds(20)); + base::TimeDelta idle_task_duration(base::TimeDelta::FromMilliseconds(10)); + base::TimeTicks expected_deadline( + test_task_runner_->NowTicks() + pending_task_delay + + maximum_idle_period_duration() + retry_enable_long_idle_period_delay()); + + // Post delayed task to ensure idle period doesn't have a max deadline. + default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask), + pending_task_delay); + + g_max_idle_task_reposts = 2; + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&RepostingIdleTestTask, + base::RetainedRef(idle_task_runner_), + &run_count, &actual_deadline)); + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, run_count); + test_task_runner_->AdvanceMockTickClock(idle_task_duration); + + // Next idle period shouldn't happen until the pending task has been run. + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, run_count); + + // Once the pending task is run the new idle period should start. + test_task_runner_->AdvanceMockTickClock(pending_task_delay - + idle_task_duration); + + // Since the idle period tried to start before the pending task ran we have to + // wait for the idle helper to retry starting the long idle period. + test_task_runner_->AdvanceMockTickClock( + retry_enable_long_idle_period_delay()); + test_task_runner_->RunUntilIdle(); + + EXPECT_EQ(2, run_count); + EXPECT_EQ(expected_deadline, actual_deadline); +} + +TEST_F(IdleHelperTest, TestLongIdlePeriodPaused) { + std::vector<base::TimeTicks> actual_deadlines; + int run_count = 0; + + // If there are no idle tasks posted we should start in the paused state. + idle_helper_->EnableLongIdlePeriod(); + CheckIdlePeriodStateIs("in_long_idle_period_paused"); + // There shouldn't be any delayed tasks posted by the idle helper when paused. + base::sequence_manager::LazyNow lazy_now_1( + test_task_runner_->GetMockTickClock()); + EXPECT_FALSE( + scheduler_helper_->real_time_domain()->DelayTillNextTask(&lazy_now_1)); + + // Posting a task should transition us to the an active state. + g_max_idle_task_reposts = 2; + base::TimeTicks clock_before(test_task_runner_->NowTicks()); + base::TimeDelta idle_task_runtime(base::TimeDelta::FromMilliseconds(10)); + idle_task_runner_->PostIdleTask( + FROM_HERE, + base::BindOnce(&RepostingUpdateClockIdleTestTask, + base::RetainedRef(idle_task_runner_), &run_count, + test_task_runner_, idle_task_runtime, &actual_deadlines)); + test_task_runner_->FastForwardUntilNoTasksRemain(); + EXPECT_EQ(2, run_count); + EXPECT_THAT( + actual_deadlines, + testing::ElementsAre(clock_before + maximum_idle_period_duration(), + clock_before + 2 * maximum_idle_period_duration())); + + // Once all task have been run we should go back to the paused state. + CheckIdlePeriodStateIs("in_long_idle_period_paused"); + base::sequence_manager::LazyNow lazy_now_2( + test_task_runner_->GetMockTickClock()); + EXPECT_FALSE( + scheduler_helper_->real_time_domain()->DelayTillNextTask(&lazy_now_2)); + + idle_helper_->EndIdlePeriod(); + CheckIdlePeriodStateIs("not_in_idle_period"); +} + +TEST_F(IdleHelperTest, TestLongIdlePeriodWhenShutdown) { + base::TimeTicks deadline_in_task; + int run_count = 0; + + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + idle_helper_->Shutdown(); + + // We shouldn't be able to enter a long idle period when shutdown + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->RunUntilIdle(); + CheckIdlePeriodStateIs("not_in_idle_period"); + EXPECT_EQ(0, run_count); +} + +void TestCanExceedIdleDeadlineIfRequiredTask(IdleHelperForTest* idle_helper, + bool* can_exceed_idle_deadline_out, + int* run_count, + base::TimeTicks deadline) { + *can_exceed_idle_deadline_out = + idle_helper->CanExceedIdleDeadlineIfRequired(); + (*run_count)++; +} + +TEST_F(IdleHelperTest, CanExceedIdleDeadlineIfRequired) { + int run_count = 0; + bool can_exceed_idle_deadline = false; + + // Should return false if not in an idle period. + EXPECT_FALSE(idle_helper_->CanExceedIdleDeadlineIfRequired()); + + // Should return false for short idle periods. + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&TestCanExceedIdleDeadlineIfRequiredTask, + idle_helper_.get(), &can_exceed_idle_deadline, + &run_count)); + idle_helper_->StartIdlePeriod( + IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + test_task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(10)); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, run_count); + EXPECT_FALSE(can_exceed_idle_deadline); + + // Should return false for a long idle period which is shortened due to a + // pending delayed task. + default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask), + base::TimeDelta::FromMilliseconds(10)); + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&TestCanExceedIdleDeadlineIfRequiredTask, + idle_helper_.get(), &can_exceed_idle_deadline, + &run_count)); + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(2, run_count); + EXPECT_FALSE(can_exceed_idle_deadline); + + // Next long idle period will be for the maximum time, so + // CanExceedIdleDeadlineIfRequired should return true. + test_task_runner_->AdvanceMockTickClock(maximum_idle_period_duration()); + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&TestCanExceedIdleDeadlineIfRequiredTask, + idle_helper_.get(), &can_exceed_idle_deadline, + &run_count)); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(3, run_count); + EXPECT_TRUE(can_exceed_idle_deadline); +} + +class IdleHelperWithQuiescencePeriodTest : public BaseIdleHelperTest { + public: + enum { + kQuiescenceDelayMs = 100, + kLongIdlePeriodMs = 50, + }; + + IdleHelperWithQuiescencePeriodTest() + : BaseIdleHelperTest( + nullptr, + base::TimeDelta::FromMilliseconds(kQuiescenceDelayMs)) {} + + ~IdleHelperWithQuiescencePeriodTest() override = default; + + void SetUp() override { + EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(AnyNumber()); + EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(AnyNumber()); + EXPECT_CALL(*idle_helper_, CanEnterLongIdlePeriod(_, _)) + .Times(AnyNumber()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*idle_helper_, IsNotQuiescent()).Times(AnyNumber()); + EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(_)).Times(AnyNumber()); + } + + void MakeNonQuiescent() { + // Run an arbitrary task so we're deemed to be not quiescent. + default_task_runner_->PostTask(FROM_HERE, base::BindOnce(NullTask)); + test_task_runner_->RunUntilIdle(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(IdleHelperWithQuiescencePeriodTest); +}; + +class IdleHelperWithQuiescencePeriodTestWithIdlePeriodObserver + : public IdleHelperWithQuiescencePeriodTest { + public: + IdleHelperWithQuiescencePeriodTestWithIdlePeriodObserver() + : IdleHelperWithQuiescencePeriodTest() {} + + ~IdleHelperWithQuiescencePeriodTestWithIdlePeriodObserver() override = + default; + + void SetUp() override { + EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(_)).Times(AnyNumber()); + } + + private: + DISALLOW_COPY_AND_ASSIGN( + IdleHelperWithQuiescencePeriodTestWithIdlePeriodObserver); +}; + +TEST_F(IdleHelperWithQuiescencePeriodTest, + LongIdlePeriodStartsImmediatelyIfQuiescent) { + base::TimeTicks actual_deadline; + int run_count = 0; + g_max_idle_task_reposts = 1; + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&RepostingIdleTestTask, + base::RetainedRef(idle_task_runner_), + &run_count, &actual_deadline)); + + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->RunUntilIdle(); + + EXPECT_EQ(1, run_count); +} + +TEST_F(IdleHelperWithQuiescencePeriodTestWithIdlePeriodObserver, + LongIdlePeriodDoesNotStartsImmediatelyIfBusy) { + MakeNonQuiescent(); + EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(0); + EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(0); + EXPECT_CALL(*idle_helper_, CanEnterLongIdlePeriod(_, _)).Times(0); + EXPECT_CALL(*idle_helper_, IsNotQuiescent()).Times(AtLeast(1)); + + base::TimeTicks actual_deadline; + int run_count = 0; + g_max_idle_task_reposts = 1; + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&RepostingIdleTestTask, + base::RetainedRef(idle_task_runner_), + &run_count, &actual_deadline)); + + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->RunUntilIdle(); + + EXPECT_EQ(0, run_count); + + scheduler_helper_->Shutdown(); +} + +TEST_F(IdleHelperWithQuiescencePeriodTest, + LongIdlePeriodStartsAfterQuiescence) { + MakeNonQuiescent(); + + // Run a repeating task so we're deemed to be busy for the next 400ms. + default_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&RepeatingTask, + base::Unretained(default_task_runner_.get()), + 10, base::TimeDelta::FromMilliseconds(40))); + + int run_count = 0; + // In this scenario EnableLongIdlePeriod deems us not to be quiescent 5x in + // a row. + base::TimeTicks expected_deadline = + test_task_runner_->NowTicks() + + base::TimeDelta::FromMilliseconds(5 * kQuiescenceDelayMs + + kLongIdlePeriodMs); + base::TimeTicks deadline_in_task; + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->FastForwardUntilNoTasksRemain(); + EXPECT_EQ(1, run_count); + EXPECT_EQ(expected_deadline, deadline_in_task); +} + +TEST_F(IdleHelperWithQuiescencePeriodTest, + QuescienceCheckedForAfterLongIdlePeriodEnds) { + idle_task_runner_->PostIdleTask(FROM_HERE, base::BindOnce(&NullIdleTask)); + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->RunUntilIdle(); + + // Post a normal task to make the scheduler non-quiescent. + default_task_runner_->PostTask(FROM_HERE, base::BindOnce(&NullTask)); + test_task_runner_->RunUntilIdle(); + + // Post an idle task. The idle task won't run initially because the system is + // not judged to be quiescent, but should be run after the quiescence delay. + int run_count = 0; + base::TimeTicks deadline_in_task; + base::TimeTicks expected_deadline = + test_task_runner_->NowTicks() + + base::TimeDelta::FromMilliseconds(kQuiescenceDelayMs + kLongIdlePeriodMs); + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->FastForwardUntilNoTasksRemain(); + + EXPECT_EQ(1, run_count); + EXPECT_EQ(expected_deadline, deadline_in_task); +} + +TEST_F(IdleHelperTest, NoShortIdlePeriodWhenDeadlineTooClose) { + int run_count = 0; + base::TimeTicks deadline_in_task; + + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + + base::TimeDelta half_a_ms(base::TimeDelta::FromMicroseconds(50)); + base::TimeTicks less_than_min_deadline(test_task_runner_->NowTicks() + + minimum_idle_period_duration() - + half_a_ms); + base::TimeTicks more_than_min_deadline(test_task_runner_->NowTicks() + + minimum_idle_period_duration() + + half_a_ms); + + idle_helper_->StartIdlePeriod(IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + less_than_min_deadline); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(0, run_count); + + idle_helper_->StartIdlePeriod(IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + more_than_min_deadline); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, run_count); +} + +TEST_F(IdleHelperTest, NoLongIdlePeriodWhenDeadlineTooClose) { + int run_count = 0; + base::TimeTicks deadline_in_task; + + base::TimeDelta half_a_ms(base::TimeDelta::FromMicroseconds(50)); + base::TimeDelta less_than_min_deadline_duration( + minimum_idle_period_duration() - half_a_ms); + base::TimeDelta more_than_min_deadline_duration( + minimum_idle_period_duration() + half_a_ms); + + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask), + less_than_min_deadline_duration); + + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(0, run_count); + + idle_helper_->EndIdlePeriod(); + test_task_runner_->AdvanceMockTickClock(maximum_idle_period_duration()); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(0, run_count); + + default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask), + more_than_min_deadline_duration); + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, run_count); +} + +TEST_F(IdleHelperWithQuiescencePeriodTest, + PendingEnableLongIdlePeriodNotRunAfterShutdown) { + MakeNonQuiescent(); + + bool shutdown_task_run = false; + int run_count = 0; + base::TimeTicks deadline_in_task; + idle_task_runner_->PostIdleTask( + FROM_HERE, + base::BindOnce(&ShutdownIdleTask, base::Unretained(idle_helper_.get()), + &shutdown_task_run)); + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + + // Delayed call to IdleHelper::EnableLongIdlePeriod enables idle tasks. + idle_helper_->EnableLongIdlePeriod(); + test_task_runner_->AdvanceMockTickClock(maximum_idle_period_duration() * 2.0); + test_task_runner_->RunUntilIdle(); + EXPECT_TRUE(shutdown_task_run); + EXPECT_EQ(0, run_count); + + // Shutdown immediately after idle period started should prevent the idle + // task from running. + idle_helper_->Shutdown(); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(0, run_count); +} + +TEST_F(IdleHelperTest, TestPostDelayedIdleTask) { + int run_count = 0; + base::TimeTicks expected_deadline = + test_task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(2300); + base::TimeTicks deadline_in_task; + + // Posting a delayed idle task should not post anything on the underlying + // task queue until the delay is up. + idle_task_runner_->PostDelayedIdleTask( + FROM_HERE, base::TimeDelta::FromMilliseconds(200), + base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + EXPECT_EQ(0u, idle_queue()->GetNumberOfPendingTasks()); + + test_task_runner_->AdvanceMockTickClock( + base::TimeDelta::FromMilliseconds(100)); + + // It shouldn't run until the delay is over even though we went idle. + idle_helper_->StartIdlePeriod(IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + expected_deadline); + EXPECT_EQ(0u, idle_queue()->GetNumberOfPendingTasks()); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(0, run_count); + + test_task_runner_->AdvanceMockTickClock( + base::TimeDelta::FromMilliseconds(100)); + idle_helper_->StartIdlePeriod(IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + expected_deadline); + EXPECT_EQ(1u, idle_queue()->GetNumberOfPendingTasks()); + test_task_runner_->RunUntilIdle(); + + EXPECT_EQ(1, run_count); + EXPECT_EQ(expected_deadline, deadline_in_task); +} + +// Tests that the OnPendingTasksChanged callback is called once when the idle +// queue becomes non-empty and again when it becomes empty. +TEST_F(IdleHelperTest, OnPendingTasksChanged) { + int run_count = 0; + base::TimeTicks expected_deadline = + test_task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(2300); + base::TimeTicks deadline_in_task; + + { + testing::InSequence dummy; + // This will be called once. I.e when the one and only task is posted. + EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(true)).Times(1); + // This will be called once. I.e when the one and only task completes. + EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(false)).Times(1); + } + + test_task_runner_->AdvanceMockTickClock( + base::TimeDelta::FromMilliseconds(100)); + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(0, run_count); + + idle_helper_->StartIdlePeriod(IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + expected_deadline); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, run_count); + EXPECT_EQ(expected_deadline, deadline_in_task); +} + +// Tests that the OnPendingTasksChanged callback is still only called once +// with false despite there being two idle tasks posted. +TEST_F(IdleHelperTest, OnPendingTasksChanged_TwoTasksAtTheSameTime) { + int run_count = 0; + base::TimeTicks expected_deadline = + test_task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(2300); + base::TimeTicks deadline_in_task; + + { + testing::InSequence dummy; + // This will be called 3 times. I.e when T1 and T2 are posted and when T1 + // completes. + EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(true)).Times(3); + // This will be called once. I.e when T2 completes. + EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(false)).Times(1); + } + + test_task_runner_->AdvanceMockTickClock( + base::TimeDelta::FromMilliseconds(100)); + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + idle_task_runner_->PostIdleTask( + FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task)); + + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(0, run_count); + + idle_helper_->StartIdlePeriod(IdleHelper::IdlePeriodState::kInShortIdlePeriod, + test_task_runner_->NowTicks(), + expected_deadline); + test_task_runner_->RunUntilIdle(); + EXPECT_EQ(2, run_count); + EXPECT_EQ(expected_deadline, deadline_in_task); +} + +} // namespace idle_helper_unittest +} // namespace scheduler +} // namespace blink |