|  | // 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 "base/test/test_mock_time_task_runner.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/containers/circular_deque.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/memory/ref_counted.h" | 
|  | #include "base/threading/thread_task_runner_handle.h" | 
|  |  | 
|  | namespace base { | 
|  | namespace { | 
|  |  | 
|  | // LegacyMockTickClock and LegacyMockClock are used by deprecated APIs of | 
|  | // TestMockTimeTaskRunner. They will be removed after updating callers of | 
|  | // GetMockClock() and GetMockTickClock() to GetMockClockPtr() and | 
|  | // GetMockTickClockPtr(). | 
|  | class LegacyMockTickClock : public TickClock { | 
|  | public: | 
|  | explicit LegacyMockTickClock( | 
|  | scoped_refptr<const TestMockTimeTaskRunner> task_runner) | 
|  | : task_runner_(std::move(task_runner)) {} | 
|  |  | 
|  | // TickClock: | 
|  | TimeTicks NowTicks() const override { return task_runner_->NowTicks(); } | 
|  |  | 
|  | private: | 
|  | scoped_refptr<const TestMockTimeTaskRunner> task_runner_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(LegacyMockTickClock); | 
|  | }; | 
|  |  | 
|  | class LegacyMockClock : public Clock { | 
|  | public: | 
|  | explicit LegacyMockClock( | 
|  | scoped_refptr<const TestMockTimeTaskRunner> task_runner) | 
|  | : task_runner_(std::move(task_runner)) {} | 
|  |  | 
|  | // Clock: | 
|  | Time Now() const override { return task_runner_->Now(); } | 
|  |  | 
|  | private: | 
|  | scoped_refptr<const TestMockTimeTaskRunner> task_runner_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(LegacyMockClock); | 
|  | }; | 
|  |  | 
|  | // A SingleThreadTaskRunner which forwards everything to its |target_|. This is | 
|  | // useful to break ownership chains when it is known that |target_| will outlive | 
|  | // the NonOwningProxyTaskRunner it's injected into. In particular, | 
|  | // TestMockTimeTaskRunner is forced to be ref-counted by virtue of being a | 
|  | // SingleThreadTaskRunner. As such it is impossible for it to have a | 
|  | // ThreadTaskRunnerHandle member that points back to itself as the | 
|  | // ThreadTaskRunnerHandle which it owns would hold a ref back to it. To break | 
|  | // this dependency cycle, the ThreadTaskRunnerHandle is instead handed a | 
|  | // NonOwningProxyTaskRunner which allows the TestMockTimeTaskRunner to not hand | 
|  | // a ref to its ThreadTaskRunnerHandle while promising in return that it will | 
|  | // outlive that ThreadTaskRunnerHandle instance. | 
|  | class NonOwningProxyTaskRunner : public SingleThreadTaskRunner { | 
|  | public: | 
|  | explicit NonOwningProxyTaskRunner(SingleThreadTaskRunner* target) | 
|  | : target_(target) { | 
|  | DCHECK(target_); | 
|  | } | 
|  |  | 
|  | // SingleThreadTaskRunner: | 
|  | bool RunsTasksInCurrentSequence() const override { | 
|  | return target_->RunsTasksInCurrentSequence(); | 
|  | } | 
|  | bool PostDelayedTask(const Location& from_here, | 
|  | OnceClosure task, | 
|  | TimeDelta delay) override { | 
|  | return target_->PostDelayedTask(from_here, std::move(task), delay); | 
|  | } | 
|  | bool PostNonNestableDelayedTask(const Location& from_here, | 
|  | OnceClosure task, | 
|  | TimeDelta delay) override { | 
|  | return target_->PostNonNestableDelayedTask(from_here, std::move(task), | 
|  | delay); | 
|  | } | 
|  |  | 
|  | private: | 
|  | friend class RefCountedThreadSafe<NonOwningProxyTaskRunner>; | 
|  | ~NonOwningProxyTaskRunner() override = default; | 
|  |  | 
|  | SingleThreadTaskRunner* const target_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(NonOwningProxyTaskRunner); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // TestMockTimeTaskRunner::TestOrderedPendingTask ----------------------------- | 
|  |  | 
|  | // Subclass of TestPendingTask which has a strictly monotonically increasing ID | 
|  | // for every task, so that tasks posted with the same 'time to run' can be run | 
|  | // in the order of being posted. | 
|  | struct TestMockTimeTaskRunner::TestOrderedPendingTask | 
|  | : public base::TestPendingTask { | 
|  | TestOrderedPendingTask(); | 
|  | TestOrderedPendingTask(const Location& location, | 
|  | OnceClosure task, | 
|  | TimeTicks post_time, | 
|  | TimeDelta delay, | 
|  | size_t ordinal, | 
|  | TestNestability nestability); | 
|  | TestOrderedPendingTask(TestOrderedPendingTask&&); | 
|  | ~TestOrderedPendingTask(); | 
|  |  | 
|  | TestOrderedPendingTask& operator=(TestOrderedPendingTask&&); | 
|  |  | 
|  | size_t ordinal; | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(TestOrderedPendingTask); | 
|  | }; | 
|  |  | 
|  | TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask() | 
|  | : ordinal(0) { | 
|  | } | 
|  |  | 
|  | TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask( | 
|  | TestOrderedPendingTask&&) = default; | 
|  |  | 
|  | TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask( | 
|  | const Location& location, | 
|  | OnceClosure task, | 
|  | TimeTicks post_time, | 
|  | TimeDelta delay, | 
|  | size_t ordinal, | 
|  | TestNestability nestability) | 
|  | : base::TestPendingTask(location, | 
|  | std::move(task), | 
|  | post_time, | 
|  | delay, | 
|  | nestability), | 
|  | ordinal(ordinal) {} | 
|  |  | 
|  | TestMockTimeTaskRunner::TestOrderedPendingTask::~TestOrderedPendingTask() = | 
|  | default; | 
|  |  | 
|  | TestMockTimeTaskRunner::TestOrderedPendingTask& | 
|  | TestMockTimeTaskRunner::TestOrderedPendingTask::operator=( | 
|  | TestOrderedPendingTask&&) = default; | 
|  |  | 
|  | // TestMockTimeTaskRunner ----------------------------------------------------- | 
|  |  | 
|  | // TODO(gab): This should also set the SequenceToken for the current thread. | 
|  | // Ref. TestMockTimeTaskRunner::RunsTasksInCurrentSequence(). | 
|  | TestMockTimeTaskRunner::ScopedContext::ScopedContext( | 
|  | scoped_refptr<TestMockTimeTaskRunner> scope) | 
|  | : on_destroy_(ThreadTaskRunnerHandle::OverrideForTesting(scope)) { | 
|  | scope->RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | TestMockTimeTaskRunner::ScopedContext::~ScopedContext() = default; | 
|  |  | 
|  | bool TestMockTimeTaskRunner::TemporalOrder::operator()( | 
|  | const TestOrderedPendingTask& first_task, | 
|  | const TestOrderedPendingTask& second_task) const { | 
|  | if (first_task.GetTimeToRun() == second_task.GetTimeToRun()) | 
|  | return first_task.ordinal > second_task.ordinal; | 
|  | return first_task.GetTimeToRun() > second_task.GetTimeToRun(); | 
|  | } | 
|  |  | 
|  | TestMockTimeTaskRunner::TestMockTimeTaskRunner(Type type) | 
|  | : TestMockTimeTaskRunner(Time::UnixEpoch(), TimeTicks(), type) {} | 
|  |  | 
|  | TestMockTimeTaskRunner::TestMockTimeTaskRunner(Time start_time, | 
|  | TimeTicks start_ticks, | 
|  | Type type) | 
|  | : now_(start_time), | 
|  | now_ticks_(start_ticks), | 
|  | tasks_lock_cv_(&tasks_lock_), | 
|  | mock_clock_(this) { | 
|  | if (type == Type::kBoundToThread) { | 
|  | RunLoop::RegisterDelegateForCurrentThread(this); | 
|  | thread_task_runner_handle_ = std::make_unique<ThreadTaskRunnerHandle>( | 
|  | MakeRefCounted<NonOwningProxyTaskRunner>(this)); | 
|  | } | 
|  | } | 
|  |  | 
|  | TestMockTimeTaskRunner::~TestMockTimeTaskRunner() = default; | 
|  |  | 
|  | void TestMockTimeTaskRunner::FastForwardBy(TimeDelta delta) { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | DCHECK_GE(delta, TimeDelta()); | 
|  |  | 
|  | const TimeTicks original_now_ticks = NowTicks(); | 
|  | ProcessAllTasksNoLaterThan(delta); | 
|  | ForwardClocksUntilTickTime(original_now_ticks + delta); | 
|  | } | 
|  |  | 
|  | void TestMockTimeTaskRunner::AdvanceMockTickClock(TimeDelta delta) { | 
|  | ForwardClocksUntilTickTime(NowTicks() + delta); | 
|  | } | 
|  |  | 
|  | void TestMockTimeTaskRunner::RunUntilIdle() { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | ProcessAllTasksNoLaterThan(TimeDelta()); | 
|  | } | 
|  |  | 
|  | void TestMockTimeTaskRunner::FastForwardUntilNoTasksRemain() { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | ProcessAllTasksNoLaterThan(TimeDelta::Max()); | 
|  | } | 
|  |  | 
|  | void TestMockTimeTaskRunner::ClearPendingTasks() { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | AutoLock scoped_lock(tasks_lock_); | 
|  | while (!tasks_.empty()) | 
|  | tasks_.pop(); | 
|  | } | 
|  |  | 
|  | Time TestMockTimeTaskRunner::Now() const { | 
|  | AutoLock scoped_lock(tasks_lock_); | 
|  | return now_; | 
|  | } | 
|  |  | 
|  | TimeTicks TestMockTimeTaskRunner::NowTicks() const { | 
|  | AutoLock scoped_lock(tasks_lock_); | 
|  | return now_ticks_; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<Clock> TestMockTimeTaskRunner::DeprecatedGetMockClock() const { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | return std::make_unique<LegacyMockClock>(this); | 
|  | } | 
|  |  | 
|  | Clock* TestMockTimeTaskRunner::GetMockClock() const { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | return &mock_clock_; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<TickClock> TestMockTimeTaskRunner::DeprecatedGetMockTickClock() | 
|  | const { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | return std::make_unique<LegacyMockTickClock>(this); | 
|  | } | 
|  |  | 
|  | const TickClock* TestMockTimeTaskRunner::GetMockTickClock() const { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | return &mock_clock_; | 
|  | } | 
|  |  | 
|  | base::circular_deque<TestPendingTask> | 
|  | TestMockTimeTaskRunner::TakePendingTasks() { | 
|  | AutoLock scoped_lock(tasks_lock_); | 
|  | base::circular_deque<TestPendingTask> tasks; | 
|  | while (!tasks_.empty()) { | 
|  | // It's safe to remove const and consume |task| here, since |task| is not | 
|  | // used for ordering the item. | 
|  | if (!tasks_.top().task.IsCancelled()) { | 
|  | tasks.push_back( | 
|  | std::move(const_cast<TestOrderedPendingTask&>(tasks_.top()))); | 
|  | } | 
|  | tasks_.pop(); | 
|  | } | 
|  | return tasks; | 
|  | } | 
|  |  | 
|  | bool TestMockTimeTaskRunner::HasPendingTask() { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | AutoLock scoped_lock(tasks_lock_); | 
|  | while (!tasks_.empty() && tasks_.top().task.IsCancelled()) | 
|  | tasks_.pop(); | 
|  | return !tasks_.empty(); | 
|  | } | 
|  |  | 
|  | size_t TestMockTimeTaskRunner::GetPendingTaskCount() { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | AutoLock scoped_lock(tasks_lock_); | 
|  | TaskPriorityQueue preserved_tasks; | 
|  | while (!tasks_.empty()) { | 
|  | if (!tasks_.top().task.IsCancelled()) { | 
|  | preserved_tasks.push( | 
|  | std::move(const_cast<TestOrderedPendingTask&>(tasks_.top()))); | 
|  | } | 
|  | tasks_.pop(); | 
|  | } | 
|  | tasks_.swap(preserved_tasks); | 
|  | return tasks_.size(); | 
|  | } | 
|  |  | 
|  | TimeDelta TestMockTimeTaskRunner::NextPendingTaskDelay() { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | AutoLock scoped_lock(tasks_lock_); | 
|  | while (!tasks_.empty() && tasks_.top().task.IsCancelled()) | 
|  | tasks_.pop(); | 
|  | return tasks_.empty() ? TimeDelta::Max() | 
|  | : tasks_.top().GetTimeToRun() - now_ticks_; | 
|  | } | 
|  |  | 
|  | // TODO(gab): Combine |thread_checker_| with a SequenceToken to differentiate | 
|  | // between tasks running in the scope of this TestMockTimeTaskRunner and other | 
|  | // task runners sharing this thread. http://crbug.com/631186 | 
|  | bool TestMockTimeTaskRunner::RunsTasksInCurrentSequence() const { | 
|  | return thread_checker_.CalledOnValidThread(); | 
|  | } | 
|  |  | 
|  | bool TestMockTimeTaskRunner::PostDelayedTask(const Location& from_here, | 
|  | OnceClosure task, | 
|  | TimeDelta delay) { | 
|  | AutoLock scoped_lock(tasks_lock_); | 
|  | tasks_.push(TestOrderedPendingTask(from_here, std::move(task), now_ticks_, | 
|  | delay, next_task_ordinal_++, | 
|  | TestPendingTask::NESTABLE)); | 
|  | tasks_lock_cv_.Signal(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool TestMockTimeTaskRunner::PostNonNestableDelayedTask( | 
|  | const Location& from_here, | 
|  | OnceClosure task, | 
|  | TimeDelta delay) { | 
|  | return PostDelayedTask(from_here, std::move(task), delay); | 
|  | } | 
|  |  | 
|  | void TestMockTimeTaskRunner::OnBeforeSelectingTask() { | 
|  | // Empty default implementation. | 
|  | } | 
|  |  | 
|  | void TestMockTimeTaskRunner::OnAfterTimePassed() { | 
|  | // Empty default implementation. | 
|  | } | 
|  |  | 
|  | void TestMockTimeTaskRunner::OnAfterTaskRun() { | 
|  | // Empty default implementation. | 
|  | } | 
|  |  | 
|  | void TestMockTimeTaskRunner::ProcessAllTasksNoLaterThan(TimeDelta max_delta) { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | DCHECK_GE(max_delta, TimeDelta()); | 
|  |  | 
|  | // Multiple test task runners can share the same thread for determinism in | 
|  | // unit tests. Make sure this TestMockTimeTaskRunner's tasks run in its scope. | 
|  | ScopedClosureRunner undo_override; | 
|  | if (!ThreadTaskRunnerHandle::IsSet() || | 
|  | ThreadTaskRunnerHandle::Get() != this) { | 
|  | undo_override = ThreadTaskRunnerHandle::OverrideForTesting(this); | 
|  | } | 
|  |  | 
|  | const TimeTicks original_now_ticks = NowTicks(); | 
|  | while (!quit_run_loop_) { | 
|  | OnBeforeSelectingTask(); | 
|  | TestPendingTask task_info; | 
|  | if (!DequeueNextTask(original_now_ticks, max_delta, &task_info)) | 
|  | break; | 
|  | if (task_info.task.IsCancelled()) | 
|  | continue; | 
|  | // If tasks were posted with a negative delay, task_info.GetTimeToRun() will | 
|  | // be less than |now_ticks_|. ForwardClocksUntilTickTime() takes care of not | 
|  | // moving the clock backwards in this case. | 
|  | ForwardClocksUntilTickTime(task_info.GetTimeToRun()); | 
|  | std::move(task_info.task).Run(); | 
|  | OnAfterTaskRun(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void TestMockTimeTaskRunner::ForwardClocksUntilTickTime(TimeTicks later_ticks) { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | { | 
|  | AutoLock scoped_lock(tasks_lock_); | 
|  | if (later_ticks <= now_ticks_) | 
|  | return; | 
|  |  | 
|  | now_ += later_ticks - now_ticks_; | 
|  | now_ticks_ = later_ticks; | 
|  | } | 
|  | OnAfterTimePassed(); | 
|  | } | 
|  |  | 
|  | bool TestMockTimeTaskRunner::DequeueNextTask(const TimeTicks& reference, | 
|  | const TimeDelta& max_delta, | 
|  | TestPendingTask* next_task) { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | AutoLock scoped_lock(tasks_lock_); | 
|  | if (!tasks_.empty() && | 
|  | (tasks_.top().GetTimeToRun() - reference) <= max_delta) { | 
|  | // It's safe to remove const and consume |task| here, since |task| is not | 
|  | // used for ordering the item. | 
|  | *next_task = std::move(const_cast<TestOrderedPendingTask&>(tasks_.top())); | 
|  | tasks_.pop(); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void TestMockTimeTaskRunner::Run(bool application_tasks_allowed) { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  |  | 
|  | // Since TestMockTimeTaskRunner doesn't process system messages: there's no | 
|  | // hope for anything but an application task to call Quit(). If this RunLoop | 
|  | // can't process application tasks (i.e. disallowed by default in nested | 
|  | // RunLoops) it's guaranteed to hang... | 
|  | DCHECK(application_tasks_allowed) | 
|  | << "This is a nested RunLoop instance and needs to be of " | 
|  | "Type::kNestableTasksAllowed."; | 
|  |  | 
|  | while (!quit_run_loop_) { | 
|  | RunUntilIdle(); | 
|  | if (quit_run_loop_ || ShouldQuitWhenIdle()) | 
|  | break; | 
|  |  | 
|  | // Peek into |tasks_| to perform one of two things: | 
|  | //   A) If there are no remaining tasks, wait until one is posted and | 
|  | //      restart from the top. | 
|  | //   B) If there is a remaining delayed task. Fast-forward to reach the next | 
|  | //      round of tasks. | 
|  | TimeDelta auto_fast_forward_by; | 
|  | { | 
|  | AutoLock scoped_lock(tasks_lock_); | 
|  | if (tasks_.empty()) { | 
|  | while (tasks_.empty()) | 
|  | tasks_lock_cv_.Wait(); | 
|  | continue; | 
|  | } | 
|  | auto_fast_forward_by = tasks_.top().GetTimeToRun() - now_ticks_; | 
|  | } | 
|  | FastForwardBy(auto_fast_forward_by); | 
|  | } | 
|  | quit_run_loop_ = false; | 
|  | } | 
|  |  | 
|  | void TestMockTimeTaskRunner::Quit() { | 
|  | DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | quit_run_loop_ = true; | 
|  | } | 
|  |  | 
|  | void TestMockTimeTaskRunner::EnsureWorkScheduled() { | 
|  | // Nothing to do: TestMockTimeTaskRunner::Run() will always process tasks and | 
|  | // doesn't need an extra kick on nested runs. | 
|  | } | 
|  |  | 
|  | TimeTicks TestMockTimeTaskRunner::MockClock::NowTicks() const { | 
|  | return task_runner_->NowTicks(); | 
|  | } | 
|  |  | 
|  | Time TestMockTimeTaskRunner::MockClock::Now() const { | 
|  | return task_runner_->Now(); | 
|  | } | 
|  |  | 
|  | }  // namespace base |