test_mock_time_task_runner.cc revision fe2f52931e8e50253d06329d7bf0a4164c7ba580
1// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/test/test_mock_time_task_runner.h"
6
7#include <utility>
8
9#include "base/logging.h"
10#include "base/macros.h"
11#include "base/memory/ptr_util.h"
12#include "base/memory/ref_counted.h"
13#include "base/threading/thread_task_runner_handle.h"
14#include "base/time/clock.h"
15#include "base/time/tick_clock.h"
16
17namespace base {
18
19namespace {
20
21// MockTickClock --------------------------------------------------------------
22
23// TickClock that always returns the then-current mock time ticks of
24// |task_runner| as the current time ticks.
25class MockTickClock : public TickClock {
26 public:
27  explicit MockTickClock(
28      scoped_refptr<const TestMockTimeTaskRunner> task_runner);
29
30  // TickClock:
31  TimeTicks NowTicks() override;
32
33 private:
34  scoped_refptr<const TestMockTimeTaskRunner> task_runner_;
35
36  DISALLOW_COPY_AND_ASSIGN(MockTickClock);
37};
38
39MockTickClock::MockTickClock(
40    scoped_refptr<const TestMockTimeTaskRunner> task_runner)
41    : task_runner_(task_runner) {
42}
43
44TimeTicks MockTickClock::NowTicks() {
45  return task_runner_->NowTicks();
46}
47
48// MockClock ------------------------------------------------------------------
49
50// Clock that always returns the then-current mock time of |task_runner| as the
51// current time.
52class MockClock : public Clock {
53 public:
54  explicit MockClock(scoped_refptr<const TestMockTimeTaskRunner> task_runner);
55
56  // Clock:
57  Time Now() override;
58
59 private:
60  scoped_refptr<const TestMockTimeTaskRunner> task_runner_;
61
62  DISALLOW_COPY_AND_ASSIGN(MockClock);
63};
64
65MockClock::MockClock(scoped_refptr<const TestMockTimeTaskRunner> task_runner)
66    : task_runner_(task_runner) {
67}
68
69Time MockClock::Now() {
70  return task_runner_->Now();
71}
72
73}  // namespace
74
75// TestMockTimeTaskRunner::TestOrderedPendingTask -----------------------------
76
77// Subclass of TestPendingTask which has a strictly monotonically increasing ID
78// for every task, so that tasks posted with the same 'time to run' can be run
79// in the order of being posted.
80struct TestMockTimeTaskRunner::TestOrderedPendingTask
81    : public base::TestPendingTask {
82  TestOrderedPendingTask();
83  TestOrderedPendingTask(const tracked_objects::Location& location,
84                         Closure task,
85                         TimeTicks post_time,
86                         TimeDelta delay,
87                         size_t ordinal,
88                         TestNestability nestability);
89  TestOrderedPendingTask(TestOrderedPendingTask&&);
90  ~TestOrderedPendingTask();
91
92  TestOrderedPendingTask& operator=(TestOrderedPendingTask&&);
93
94  size_t ordinal;
95
96 private:
97  DISALLOW_COPY_AND_ASSIGN(TestOrderedPendingTask);
98};
99
100TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask()
101    : ordinal(0) {
102}
103
104TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask(
105    TestOrderedPendingTask&&) = default;
106
107TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask(
108    const tracked_objects::Location& location,
109    Closure task,
110    TimeTicks post_time,
111    TimeDelta delay,
112    size_t ordinal,
113    TestNestability nestability)
114    : base::TestPendingTask(location,
115                            std::move(task),
116                            post_time,
117                            delay,
118                            nestability),
119      ordinal(ordinal) {}
120
121TestMockTimeTaskRunner::TestOrderedPendingTask::~TestOrderedPendingTask() {
122}
123
124TestMockTimeTaskRunner::TestOrderedPendingTask&
125TestMockTimeTaskRunner::TestOrderedPendingTask::operator=(
126    TestOrderedPendingTask&&) = default;
127
128// TestMockTimeTaskRunner -----------------------------------------------------
129
130// TODO(gab): This should also set the SequenceToken for the current thread.
131// Ref. TestMockTimeTaskRunner::RunsTasksOnCurrentThread().
132TestMockTimeTaskRunner::ScopedContext::ScopedContext(
133    scoped_refptr<TestMockTimeTaskRunner> scope)
134    : on_destroy_(ThreadTaskRunnerHandle::OverrideForTesting(scope)) {
135  scope->RunUntilIdle();
136}
137
138TestMockTimeTaskRunner::ScopedContext::~ScopedContext() = default;
139
140bool TestMockTimeTaskRunner::TemporalOrder::operator()(
141    const TestOrderedPendingTask& first_task,
142    const TestOrderedPendingTask& second_task) const {
143  if (first_task.GetTimeToRun() == second_task.GetTimeToRun())
144    return first_task.ordinal > second_task.ordinal;
145  return first_task.GetTimeToRun() > second_task.GetTimeToRun();
146}
147
148TestMockTimeTaskRunner::TestMockTimeTaskRunner()
149    : now_(Time::UnixEpoch()), next_task_ordinal_(0) {
150}
151
152TestMockTimeTaskRunner::TestMockTimeTaskRunner(Time start_time,
153                                               TimeTicks start_ticks)
154    : now_(Time::UnixEpoch()), now_ticks_(start_ticks), next_task_ordinal_(0) {}
155
156TestMockTimeTaskRunner::~TestMockTimeTaskRunner() {
157}
158
159void TestMockTimeTaskRunner::FastForwardBy(TimeDelta delta) {
160  DCHECK(thread_checker_.CalledOnValidThread());
161  DCHECK_GE(delta, TimeDelta());
162
163  const TimeTicks original_now_ticks = now_ticks_;
164  ProcessAllTasksNoLaterThan(delta);
165  ForwardClocksUntilTickTime(original_now_ticks + delta);
166}
167
168void TestMockTimeTaskRunner::RunUntilIdle() {
169  DCHECK(thread_checker_.CalledOnValidThread());
170  ProcessAllTasksNoLaterThan(TimeDelta());
171}
172
173void TestMockTimeTaskRunner::FastForwardUntilNoTasksRemain() {
174  DCHECK(thread_checker_.CalledOnValidThread());
175  ProcessAllTasksNoLaterThan(TimeDelta::Max());
176}
177
178void TestMockTimeTaskRunner::ClearPendingTasks() {
179  DCHECK(thread_checker_.CalledOnValidThread());
180  AutoLock scoped_lock(tasks_lock_);
181  while (!tasks_.empty())
182    tasks_.pop();
183}
184
185Time TestMockTimeTaskRunner::Now() const {
186  DCHECK(thread_checker_.CalledOnValidThread());
187  return now_;
188}
189
190TimeTicks TestMockTimeTaskRunner::NowTicks() const {
191  DCHECK(thread_checker_.CalledOnValidThread());
192  return now_ticks_;
193}
194
195std::unique_ptr<Clock> TestMockTimeTaskRunner::GetMockClock() const {
196  DCHECK(thread_checker_.CalledOnValidThread());
197  return MakeUnique<MockClock>(this);
198}
199
200std::unique_ptr<TickClock> TestMockTimeTaskRunner::GetMockTickClock() const {
201  DCHECK(thread_checker_.CalledOnValidThread());
202  return MakeUnique<MockTickClock>(this);
203}
204
205std::deque<TestPendingTask> TestMockTimeTaskRunner::TakePendingTasks() {
206  AutoLock scoped_lock(tasks_lock_);
207  std::deque<TestPendingTask> tasks;
208  while (!tasks_.empty()) {
209    // It's safe to remove const and consume |task| here, since |task| is not
210    // used for ordering the item.
211    tasks.push_back(
212        std::move(const_cast<TestOrderedPendingTask&>(tasks_.top())));
213    tasks_.pop();
214  }
215  return tasks;
216}
217
218bool TestMockTimeTaskRunner::HasPendingTask() const {
219  DCHECK(thread_checker_.CalledOnValidThread());
220  return !tasks_.empty();
221}
222
223size_t TestMockTimeTaskRunner::GetPendingTaskCount() const {
224  DCHECK(thread_checker_.CalledOnValidThread());
225  return tasks_.size();
226}
227
228TimeDelta TestMockTimeTaskRunner::NextPendingTaskDelay() const {
229  DCHECK(thread_checker_.CalledOnValidThread());
230  return tasks_.empty() ? TimeDelta::Max()
231                        : tasks_.top().GetTimeToRun() - now_ticks_;
232}
233
234// TODO(gab): Combine |thread_checker_| with a SequenceToken to differentiate
235// between tasks running in the scope of this TestMockTimeTaskRunner and other
236// task runners sharing this thread. http://crbug.com/631186
237bool TestMockTimeTaskRunner::RunsTasksOnCurrentThread() const {
238  return thread_checker_.CalledOnValidThread();
239}
240
241bool TestMockTimeTaskRunner::PostDelayedTask(
242    const tracked_objects::Location& from_here,
243    Closure task,
244    TimeDelta delay) {
245  AutoLock scoped_lock(tasks_lock_);
246  tasks_.push(TestOrderedPendingTask(from_here, std::move(task), now_ticks_,
247                                     delay, next_task_ordinal_++,
248                                     TestPendingTask::NESTABLE));
249  return true;
250}
251
252bool TestMockTimeTaskRunner::PostNonNestableDelayedTask(
253    const tracked_objects::Location& from_here,
254    Closure task,
255    TimeDelta delay) {
256  return PostDelayedTask(from_here, std::move(task), delay);
257}
258
259bool TestMockTimeTaskRunner::IsElapsingStopped() {
260  return false;
261}
262
263void TestMockTimeTaskRunner::OnBeforeSelectingTask() {
264  // Empty default implementation.
265}
266
267void TestMockTimeTaskRunner::OnAfterTimePassed() {
268  // Empty default implementation.
269}
270
271void TestMockTimeTaskRunner::OnAfterTaskRun() {
272  // Empty default implementation.
273}
274
275void TestMockTimeTaskRunner::ProcessAllTasksNoLaterThan(TimeDelta max_delta) {
276  DCHECK(thread_checker_.CalledOnValidThread());
277  DCHECK_GE(max_delta, TimeDelta());
278
279  // Multiple test task runners can share the same thread for determinism in
280  // unit tests. Make sure this TestMockTimeTaskRunner's tasks run in its scope.
281  ScopedClosureRunner undo_override;
282  if (!ThreadTaskRunnerHandle::IsSet() ||
283      ThreadTaskRunnerHandle::Get() != this) {
284    undo_override = ThreadTaskRunnerHandle::OverrideForTesting(this);
285  }
286
287  const TimeTicks original_now_ticks = now_ticks_;
288  while (!IsElapsingStopped()) {
289    OnBeforeSelectingTask();
290    TestPendingTask task_info;
291    if (!DequeueNextTask(original_now_ticks, max_delta, &task_info))
292      break;
293    // If tasks were posted with a negative delay, task_info.GetTimeToRun() will
294    // be less than |now_ticks_|. ForwardClocksUntilTickTime() takes care of not
295    // moving the clock backwards in this case.
296    ForwardClocksUntilTickTime(task_info.GetTimeToRun());
297    std::move(task_info.task).Run();
298    OnAfterTaskRun();
299  }
300}
301
302void TestMockTimeTaskRunner::ForwardClocksUntilTickTime(TimeTicks later_ticks) {
303  DCHECK(thread_checker_.CalledOnValidThread());
304  if (later_ticks <= now_ticks_)
305    return;
306
307  now_ += later_ticks - now_ticks_;
308  now_ticks_ = later_ticks;
309  OnAfterTimePassed();
310}
311
312bool TestMockTimeTaskRunner::DequeueNextTask(const TimeTicks& reference,
313                                             const TimeDelta& max_delta,
314                                             TestPendingTask* next_task) {
315  AutoLock scoped_lock(tasks_lock_);
316  if (!tasks_.empty() &&
317      (tasks_.top().GetTimeToRun() - reference) <= max_delta) {
318    // It's safe to remove const and consume |task| here, since |task| is not
319    // used for ordering the item.
320    *next_task = std::move(const_cast<TestOrderedPendingTask&>(tasks_.top()));
321    tasks_.pop();
322    return true;
323  }
324  return false;
325}
326
327}  // namespace base
328