test_mock_time_task_runner.cc 10.2 KB
Newer Older
1 2 3 4 5 6
// 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"

7 8
#include <utility>

9
#include "base/logging.h"
10
#include "base/macros.h"
dcheng's avatar
dcheng committed
11
#include "base/memory/ptr_util.h"
12
#include "base/memory/ref_counted.h"
13
#include "base/threading/thread_task_runner_handle.h"
14 15
#include "base/time/clock.h"
#include "base/time/tick_clock.h"
16 17 18 19 20

namespace base {

namespace {

21 22 23 24
// MockTickClock --------------------------------------------------------------

// TickClock that always returns the then-current mock time ticks of
// |task_runner| as the current time ticks.
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
class MockTickClock : public TickClock {
 public:
  explicit MockTickClock(
      scoped_refptr<const TestMockTimeTaskRunner> task_runner);

  // TickClock:
  TimeTicks NowTicks() override;

 private:
  scoped_refptr<const TestMockTimeTaskRunner> task_runner_;

  DISALLOW_COPY_AND_ASSIGN(MockTickClock);
};

MockTickClock::MockTickClock(
    scoped_refptr<const TestMockTimeTaskRunner> task_runner)
    : task_runner_(task_runner) {
}

44 45
TimeTicks MockTickClock::NowTicks() {
  return task_runner_->NowTicks();
46 47
}

48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
// MockClock ------------------------------------------------------------------

// Clock that always returns the then-current mock time of |task_runner| as the
// current time.
class MockClock : public Clock {
 public:
  explicit MockClock(scoped_refptr<const TestMockTimeTaskRunner> task_runner);

  // Clock:
  Time Now() override;

 private:
  scoped_refptr<const TestMockTimeTaskRunner> task_runner_;

  DISALLOW_COPY_AND_ASSIGN(MockClock);
};

MockClock::MockClock(scoped_refptr<const TestMockTimeTaskRunner> task_runner)
    : task_runner_(task_runner) {
}

Time MockClock::Now() {
  return task_runner_->Now();
71 72 73 74
}

}  // namespace

75 76 77 78 79 80 81 82 83
// 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 tracked_objects::Location& location,
84
                         OnceClosure task,
85 86 87 88
                         TimeTicks post_time,
                         TimeDelta delay,
                         size_t ordinal,
                         TestNestability nestability);
tzik's avatar
tzik committed
89
  TestOrderedPendingTask(TestOrderedPendingTask&&);
90 91
  ~TestOrderedPendingTask();

tzik's avatar
tzik committed
92 93
  TestOrderedPendingTask& operator=(TestOrderedPendingTask&&);

94
  size_t ordinal;
tzik's avatar
tzik committed
95 96 97

 private:
  DISALLOW_COPY_AND_ASSIGN(TestOrderedPendingTask);
98 99 100 101 102 103
};

TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask()
    : ordinal(0) {
}

tzik's avatar
tzik committed
104 105 106
TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask(
    TestOrderedPendingTask&&) = default;

107 108
TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask(
    const tracked_objects::Location& location,
109
    OnceClosure task,
110 111 112 113
    TimeTicks post_time,
    TimeDelta delay,
    size_t ordinal,
    TestNestability nestability)
114 115 116 117 118
    : base::TestPendingTask(location,
                            std::move(task),
                            post_time,
                            delay,
                            nestability),
tzik's avatar
tzik committed
119
      ordinal(ordinal) {}
120 121 122 123

TestMockTimeTaskRunner::TestOrderedPendingTask::~TestOrderedPendingTask() {
}

tzik's avatar
tzik committed
124 125 126 127
TestMockTimeTaskRunner::TestOrderedPendingTask&
TestMockTimeTaskRunner::TestOrderedPendingTask::operator=(
    TestOrderedPendingTask&&) = default;

128 129
// TestMockTimeTaskRunner -----------------------------------------------------

130 131 132 133 134 135 136 137 138 139
// TODO(gab): This should also set the SequenceToken for the current thread.
// Ref. TestMockTimeTaskRunner::RunsTasksOnCurrentThread().
TestMockTimeTaskRunner::ScopedContext::ScopedContext(
    scoped_refptr<TestMockTimeTaskRunner> scope)
    : on_destroy_(ThreadTaskRunnerHandle::OverrideForTesting(scope)) {
  scope->RunUntilIdle();
}

TestMockTimeTaskRunner::ScopedContext::~ScopedContext() = default;

140
bool TestMockTimeTaskRunner::TemporalOrder::operator()(
141 142 143 144
    const TestOrderedPendingTask& first_task,
    const TestOrderedPendingTask& second_task) const {
  if (first_task.GetTimeToRun() == second_task.GetTimeToRun())
    return first_task.ordinal > second_task.ordinal;
145 146 147
  return first_task.GetTimeToRun() > second_task.GetTimeToRun();
}

148 149
TestMockTimeTaskRunner::TestMockTimeTaskRunner()
    : now_(Time::UnixEpoch()), next_task_ordinal_(0) {
150 151
}

152 153 154 155
TestMockTimeTaskRunner::TestMockTimeTaskRunner(Time start_time,
                                               TimeTicks start_ticks)
    : now_(Time::UnixEpoch()), now_ticks_(start_ticks), next_task_ordinal_(0) {}

156 157 158 159 160
TestMockTimeTaskRunner::~TestMockTimeTaskRunner() {
}

void TestMockTimeTaskRunner::FastForwardBy(TimeDelta delta) {
  DCHECK(thread_checker_.CalledOnValidThread());
161
  DCHECK_GE(delta, TimeDelta());
162

163 164 165
  const TimeTicks original_now_ticks = now_ticks_;
  ProcessAllTasksNoLaterThan(delta);
  ForwardClocksUntilTickTime(original_now_ticks + delta);
166 167 168
}

void TestMockTimeTaskRunner::RunUntilIdle() {
169 170
  DCHECK(thread_checker_.CalledOnValidThread());
  ProcessAllTasksNoLaterThan(TimeDelta());
171 172 173
}

void TestMockTimeTaskRunner::FastForwardUntilNoTasksRemain() {
174 175
  DCHECK(thread_checker_.CalledOnValidThread());
  ProcessAllTasksNoLaterThan(TimeDelta::Max());
176 177
}

178 179 180 181 182 183 184
void TestMockTimeTaskRunner::ClearPendingTasks() {
  DCHECK(thread_checker_.CalledOnValidThread());
  AutoLock scoped_lock(tasks_lock_);
  while (!tasks_.empty())
    tasks_.pop();
}

185
Time TestMockTimeTaskRunner::Now() const {
186 187 188 189
  DCHECK(thread_checker_.CalledOnValidThread());
  return now_;
}

190 191 192 193 194
TimeTicks TestMockTimeTaskRunner::NowTicks() const {
  DCHECK(thread_checker_.CalledOnValidThread());
  return now_ticks_;
}

dcheng's avatar
dcheng committed
195
std::unique_ptr<Clock> TestMockTimeTaskRunner::GetMockClock() const {
196
  DCHECK(thread_checker_.CalledOnValidThread());
197
  return MakeUnique<MockClock>(this);
198 199
}

dcheng's avatar
dcheng committed
200
std::unique_ptr<TickClock> TestMockTimeTaskRunner::GetMockTickClock() const {
201
  DCHECK(thread_checker_.CalledOnValidThread());
202
  return MakeUnique<MockTickClock>(this);
203 204
}

205
std::deque<TestPendingTask> TestMockTimeTaskRunner::TakePendingTasks() {
206
  AutoLock scoped_lock(tasks_lock_);
207 208
  std::deque<TestPendingTask> tasks;
  while (!tasks_.empty()) {
tzik's avatar
tzik committed
209 210 211 212
    // It's safe to remove const and consume |task| here, since |task| is not
    // used for ordering the item.
    tasks.push_back(
        std::move(const_cast<TestOrderedPendingTask&>(tasks_.top())));
213 214 215 216 217
    tasks_.pop();
  }
  return tasks;
}

218 219 220 221 222
bool TestMockTimeTaskRunner::HasPendingTask() const {
  DCHECK(thread_checker_.CalledOnValidThread());
  return !tasks_.empty();
}

223 224 225 226 227
size_t TestMockTimeTaskRunner::GetPendingTaskCount() const {
  DCHECK(thread_checker_.CalledOnValidThread());
  return tasks_.size();
}

228 229
TimeDelta TestMockTimeTaskRunner::NextPendingTaskDelay() const {
  DCHECK(thread_checker_.CalledOnValidThread());
230 231
  return tasks_.empty() ? TimeDelta::Max()
                        : tasks_.top().GetTimeToRun() - now_ticks_;
232 233
}

234 235 236
// 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
237 238 239 240 241 242
bool TestMockTimeTaskRunner::RunsTasksOnCurrentThread() const {
  return thread_checker_.CalledOnValidThread();
}

bool TestMockTimeTaskRunner::PostDelayedTask(
    const tracked_objects::Location& from_here,
243
    OnceClosure task,
244
    TimeDelta delay) {
245
  AutoLock scoped_lock(tasks_lock_);
246 247
  tasks_.push(TestOrderedPendingTask(from_here, std::move(task), now_ticks_,
                                     delay, next_task_ordinal_++,
248
                                     TestPendingTask::NESTABLE));
249 250 251 252 253
  return true;
}

bool TestMockTimeTaskRunner::PostNonNestableDelayedTask(
    const tracked_objects::Location& from_here,
254
    OnceClosure task,
255
    TimeDelta delay) {
256
  return PostDelayedTask(from_here, std::move(task), delay);
257 258
}

binjin's avatar
binjin committed
259 260 261 262
bool TestMockTimeTaskRunner::IsElapsingStopped() {
  return false;
}

263 264 265 266 267 268 269 270 271 272 273 274
void TestMockTimeTaskRunner::OnBeforeSelectingTask() {
  // Empty default implementation.
}

void TestMockTimeTaskRunner::OnAfterTimePassed() {
  // Empty default implementation.
}

void TestMockTimeTaskRunner::OnAfterTaskRun() {
  // Empty default implementation.
}

275
void TestMockTimeTaskRunner::ProcessAllTasksNoLaterThan(TimeDelta max_delta) {
276
  DCHECK(thread_checker_.CalledOnValidThread());
277
  DCHECK_GE(max_delta, TimeDelta());
278 279 280 281 282 283 284 285 286

  // 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);
  }

287
  const TimeTicks original_now_ticks = now_ticks_;
binjin's avatar
binjin committed
288
  while (!IsElapsingStopped()) {
289 290 291 292 293 294 295 296
    OnBeforeSelectingTask();
    TestPendingTask task_info;
    if (!DequeueNextTask(original_now_ticks, max_delta, &task_info))
      break;
    // 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());
tzik's avatar
tzik committed
297
    std::move(task_info.task).Run();
298 299 300 301 302
    OnAfterTaskRun();
  }
}

void TestMockTimeTaskRunner::ForwardClocksUntilTickTime(TimeTicks later_ticks) {
303
  DCHECK(thread_checker_.CalledOnValidThread());
304 305 306 307 308 309 310 311 312 313
  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,
314
                                             TestPendingTask* next_task) {
315
  AutoLock scoped_lock(tasks_lock_);
316 317
  if (!tasks_.empty() &&
      (tasks_.top().GetTimeToRun() - reference) <= max_delta) {
tzik's avatar
tzik committed
318 319 320
    // 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()));
321 322 323 324 325 326 327
    tasks_.pop();
    return true;
  }
  return false;
}

}  // namespace base