Mock MessagePump that advances time as needed.

This pump will be useful for writing SequenceManager tests.

Change-Id: I02446cfb00dc46b205f36cb6abb002db6b0df6af
Reviewed-on: https://chromium-review.googlesource.com/c/1445877
Commit-Queue: Carlos Caballero <carlscab@google.com>
Reviewed-by: Gabriel Charette <gab@chromium.org>
Reviewed-by: Alexander Timin <altimin@chromium.org>
Reviewed-by: Sami Kyöstilä <skyostil@chromium.org>
Reviewed-by: Alex Clarke <alexclarke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#629533}
diff --git a/base/BUILD.gn b/base/BUILD.gn
index d84c3353..9c48507 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2500,6 +2500,7 @@
     "task/sequence_manager/lazily_deallocated_deque_unittest.cc",
     "task/sequence_manager/sequence_manager_impl_unittest.cc",
     "task/sequence_manager/task_queue_selector_unittest.cc",
+    "task/sequence_manager/test/mock_time_message_pump_unittest.cc",
     "task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc",
     "task/sequence_manager/time_domain_unittest.cc",
     "task/sequence_manager/work_queue_sets_unittest.cc",
diff --git a/base/task/sequence_manager/test/mock_time_message_pump.cc b/base/task/sequence_manager/test/mock_time_message_pump.cc
new file mode 100644
index 0000000..66b6f2c
--- /dev/null
+++ b/base/task/sequence_manager/test/mock_time_message_pump.cc
@@ -0,0 +1,88 @@
+// Copyright 2019 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/task/sequence_manager/test/mock_time_message_pump.h"
+
+#include <algorithm>
+
+#include "base/auto_reset.h"
+#include "base/test/simple_test_tick_clock.h"
+
+namespace base {
+namespace sequence_manager {
+
+MockTimeMessagePump::MockTimeMessagePump(SimpleTestTickClock* clock)
+    : clock_(clock) {}
+
+MockTimeMessagePump::~MockTimeMessagePump() {}
+
+bool MockTimeMessagePump::MaybeAdvanceTime(TimeTicks target_time) {
+  auto now = clock_->NowTicks();
+
+  if (target_time <= now)
+    return true;
+
+  TimeTicks next_now;
+
+  if (!target_time.is_max()) {
+    next_now = std::min(allow_advance_until_, target_time);
+  } else if (allow_advance_until_ == TimeTicks::Max()) {
+    next_now = now;
+  } else {
+    next_now = allow_advance_until_;
+  }
+
+  if (now < next_now) {
+    clock_->SetNowTicks(next_now);
+    return true;
+  }
+  return false;
+}
+
+void MockTimeMessagePump::Run(Delegate* delegate) {
+  AutoReset<bool> auto_reset_keep_running(&keep_running_, true);
+
+  for (;;) {
+    Delegate::NextWorkInfo info = delegate->DoSomeWork();
+
+    if (!keep_running_ || quit_after_do_some_work_)
+      break;
+
+    if (info.is_immediate())
+      continue;
+
+    bool have_immediate_work = delegate->DoIdleWork();
+
+    if (!keep_running_)
+      break;
+
+    if (have_immediate_work)
+      continue;
+
+    if (MaybeAdvanceTime(info.delayed_run_time))
+      continue;
+
+    next_wake_up_time_ = info.delayed_run_time;
+
+    if (stop_when_message_pump_is_idle_)
+      return;
+
+    NOTREACHED() << "Pump would go to sleep. Probably not what you wanted, "
+                    "consider rewriting your test.";
+  }
+}
+
+void MockTimeMessagePump::Quit() {
+  keep_running_ = false;
+}
+
+void MockTimeMessagePump::ScheduleWork() {}
+
+void MockTimeMessagePump::ScheduleDelayedWork(
+    const TimeTicks& delayed_work_time) {
+  next_wake_up_time_ = delayed_work_time;
+}
+
+}  // namespace sequence_manager
+}  // namespace base
diff --git a/base/task/sequence_manager/test/mock_time_message_pump.h b/base/task/sequence_manager/test/mock_time_message_pump.h
new file mode 100644
index 0000000..223dfa5
--- /dev/null
+++ b/base/task/sequence_manager/test/mock_time_message_pump.h
@@ -0,0 +1,86 @@
+// Copyright 2019 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.
+
+#ifndef BASE_TASK_SEQUENCE_MANAGER_TEST_MOCK_TIME_MESSAGE_PUMP_H_
+#define BASE_TASK_SEQUENCE_MANAGER_TEST_MOCK_TIME_MESSAGE_PUMP_H_
+
+#include "base/callback.h"
+#include "base/message_loop/message_pump.h"
+#include "base/optional.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/time/time.h"
+
+namespace base {
+
+class SimpleTestTickClock;
+
+namespace sequence_manager {
+
+// MessagePump implementation that uses a SimpleTestTickClock to keep track of
+// time and will advance it as needed to keep running tasks.
+//
+// This pump will actually check fail if it ever has to go to sleep as this
+// would indicate that the unit test might block indefinitely.
+// TODO(carlscab): In the future we could consider sleeping if there is no
+// outstanding |delayed_work_time_|, because we could be woken up by concurrent
+// ScheduleWork() calls.
+class MockTimeMessagePump : public MessagePump {
+ public:
+  explicit MockTimeMessagePump(SimpleTestTickClock* clock);
+  ~MockTimeMessagePump() override;
+
+  // MessagePump implementation
+  void Run(Delegate* delegate) override;
+  void Quit() override;
+  void ScheduleWork() override;
+  void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
+
+  // Returns the time at which the pump would have to wake up to be perform
+  // work.
+  TimeTicks next_wake_up_time() const { return next_wake_up_time_; }
+
+  // Quits after the first call to Delegate::DoSomeWork(). Useful
+  // for tests that want to make sure certain things happen during a DoSomeWork
+  // call.
+  void SetQuitAfterDoSomeWork(bool quit_after_do_some_work) {
+    quit_after_do_some_work_ = quit_after_do_some_work;
+  }
+
+  // Allows this instance to advance the SimpleTestTickClock up to but not over
+  // |advance_until| when idle (i.e. when a regular pump would go to sleep).
+  // The clock will allways be advanced to |advance_until|, even if there are no
+  // tasks requiring it (i.e. delayed tasks to be run after
+  // |advance_until|) except for a value of TimeTicks::Max() which will advance
+  // the clock as long as there is pending delayed work.
+  void SetAllowTimeToAutoAdvanceUntil(TimeTicks advance_until) {
+    allow_advance_until_ = advance_until;
+  }
+
+  // Quit when this pump's Delegate is out of work (i.e. when a regular pump
+  // would go to sleep) and we are not allowed to advance the clock anymore.
+  void SetStopWhenMessagePumpIsIdle(bool stop_when_message_pump_is_idle) {
+    stop_when_message_pump_is_idle_ = stop_when_message_pump_is_idle;
+  }
+
+ private:
+  // Returns true if the clock was indeed advanced and thus we should attempt
+  // another iteration of the DoSomeWork-DoIdleWork-loop.
+  bool MaybeAdvanceTime(TimeTicks target_time);
+
+  SimpleTestTickClock* const clock_;
+  // This flag is set to false when Run should return.
+  bool keep_running_ = true;
+
+  bool stop_when_message_pump_is_idle_ = false;
+  bool quit_after_do_some_work_ = false;
+
+  TimeTicks next_wake_up_time_{TimeTicks::Max()};
+
+  TimeTicks allow_advance_until_ = TimeTicks::Min();
+};
+
+}  // namespace sequence_manager
+}  // namespace base
+
+#endif  // BASE_TASK_SEQUENCE_MANAGER_TEST_MOCK_TIME_MESSAGE_PUMP_H_
diff --git a/base/task/sequence_manager/test/mock_time_message_pump_unittest.cc b/base/task/sequence_manager/test/mock_time_message_pump_unittest.cc
new file mode 100644
index 0000000..dfa6e408
--- /dev/null
+++ b/base/task/sequence_manager/test/mock_time_message_pump_unittest.cc
@@ -0,0 +1,185 @@
+// Copyright 2019 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/task/sequence_manager/test/mock_time_message_pump.h"
+
+#include "base/message_loop/message_pump.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace sequence_manager {
+namespace {
+
+using ::testing::DoAll;
+using ::testing::Eq;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrictMock;
+
+class MockMessagePumpDelegate : public MessagePump::Delegate {
+ public:
+  MOCK_METHOD0(BeforeDoInternalWork, void());
+  MOCK_METHOD0(DoWork, bool());
+  MOCK_METHOD1(DoDelayedWork, bool(TimeTicks*));
+  MOCK_METHOD0(DoSomeWork, NextWorkInfo());
+  MOCK_METHOD0(DoIdleWork, bool());
+};
+
+MessagePump::Delegate::NextWorkInfo NextWorkInfo(TimeTicks delayed_run_time) {
+  MessagePump::Delegate::NextWorkInfo info;
+  info.delayed_run_time = delayed_run_time;
+  return info;
+}
+
+TEST(MockMessagePumpTest, KeepsRunningIfNotAllowedToAdvanceTime) {
+  SimpleTestTickClock mock_clock;
+  mock_clock.Advance(TimeDelta::FromHours(42));
+  StrictMock<MockMessagePumpDelegate> delegate;
+  MockTimeMessagePump pump(&mock_clock);
+  const auto kStartTime = mock_clock.NowTicks();
+  const auto kFutureTime = kStartTime + TimeDelta::FromSeconds(42);
+
+  EXPECT_CALL(delegate, DoSomeWork)
+      .WillOnce(Return(NextWorkInfo(TimeTicks())))
+      .WillOnce(Return(NextWorkInfo(TimeTicks())))
+      .WillOnce(Return(NextWorkInfo(kFutureTime)));
+  EXPECT_CALL(delegate, DoIdleWork).WillOnce(Invoke([&] {
+    pump.Quit();
+    return false;
+  }));
+
+  pump.Run(&delegate);
+
+  EXPECT_THAT(mock_clock.NowTicks(), Eq(kStartTime));
+}
+
+TEST(MockMessagePumpTest, AdvancesTimeAsAllowed) {
+  SimpleTestTickClock mock_clock;
+  mock_clock.Advance(TimeDelta::FromHours(42));
+  StrictMock<MockMessagePumpDelegate> delegate;
+  MockTimeMessagePump pump(&mock_clock);
+  const auto kStartTime = mock_clock.NowTicks();
+  const auto kEndTime = kStartTime + TimeDelta::FromSeconds(2);
+
+  pump.SetAllowTimeToAutoAdvanceUntil(kEndTime);
+  pump.SetStopWhenMessagePumpIsIdle(true);
+  EXPECT_CALL(delegate, DoSomeWork).Times(3).WillRepeatedly(Invoke([&]() {
+    return NextWorkInfo(mock_clock.NowTicks() + TimeDelta::FromSeconds(1));
+  }));
+  EXPECT_CALL(delegate, DoIdleWork).Times(3).WillRepeatedly(Return(false));
+
+  pump.Run(&delegate);
+
+  EXPECT_THAT(mock_clock.NowTicks(), Eq(kEndTime));
+}
+
+TEST(MockMessagePumpTest, CanQuitAfterMaybeDoWork) {
+  SimpleTestTickClock mock_clock;
+  mock_clock.Advance(TimeDelta::FromHours(42));
+  StrictMock<MockMessagePumpDelegate> delegate;
+  MockTimeMessagePump pump(&mock_clock);
+
+  pump.SetQuitAfterDoSomeWork(true);
+  EXPECT_CALL(delegate, DoSomeWork).WillOnce(Return(NextWorkInfo(TimeTicks())));
+
+  pump.Run(&delegate);
+}
+
+TEST(MockMessagePumpTest, AdvancesUntilAllowedTime) {
+  SimpleTestTickClock mock_clock;
+  mock_clock.Advance(TimeDelta::FromHours(42));
+  StrictMock<MockMessagePumpDelegate> delegate;
+  MockTimeMessagePump pump(&mock_clock);
+  const auto kStartTime = mock_clock.NowTicks();
+  const auto kEndTime = kStartTime + TimeDelta::FromSeconds(2);
+  const auto kNextDelayedWorkTime = kEndTime + TimeDelta::FromSeconds(2);
+
+  pump.SetAllowTimeToAutoAdvanceUntil(kEndTime);
+  pump.SetStopWhenMessagePumpIsIdle(true);
+  EXPECT_CALL(delegate, DoSomeWork)
+      .Times(2)
+      .WillRepeatedly(Return(NextWorkInfo(kNextDelayedWorkTime)));
+  EXPECT_CALL(delegate, DoIdleWork).Times(2).WillRepeatedly(Return(false));
+
+  pump.Run(&delegate);
+
+  EXPECT_THAT(mock_clock.NowTicks(), Eq(kEndTime));
+}
+
+TEST(MockMessagePumpTest, StoresNextWakeUpTime) {
+  SimpleTestTickClock mock_clock;
+  StrictMock<MockMessagePumpDelegate> delegate;
+  MockTimeMessagePump pump(&mock_clock);
+  const auto kStartTime = mock_clock.NowTicks();
+  const auto kEndTime = kStartTime;
+  const auto kNextDelayedWorkTime = kEndTime + TimeDelta::FromSeconds(2);
+
+  pump.SetAllowTimeToAutoAdvanceUntil(kEndTime);
+  pump.SetStopWhenMessagePumpIsIdle(true);
+  EXPECT_CALL(delegate, DoSomeWork)
+      .WillOnce(Return(NextWorkInfo(kNextDelayedWorkTime)));
+  EXPECT_CALL(delegate, DoIdleWork).WillOnce(Return(false));
+
+  pump.Run(&delegate);
+
+  EXPECT_THAT(pump.next_wake_up_time(), Eq(kNextDelayedWorkTime));
+}
+
+TEST(MockMessagePumpTest, StoresNextWakeUpTimeInScheduleDelayedWork) {
+  SimpleTestTickClock mock_clock;
+  StrictMock<MockMessagePumpDelegate> delegate;
+  MockTimeMessagePump pump(&mock_clock);
+  const auto kNextDelayedWorkTime =
+      mock_clock.NowTicks() + TimeDelta::FromSeconds(2);
+
+  pump.ScheduleDelayedWork(kNextDelayedWorkTime);
+
+  EXPECT_THAT(pump.next_wake_up_time(), Eq(kNextDelayedWorkTime));
+}
+
+TEST(MockMessagePumpTest, NextDelayedWorkTimeInThePastKeepsRunning) {
+  SimpleTestTickClock mock_clock;
+  mock_clock.Advance(TimeDelta::FromHours(42));
+  StrictMock<MockMessagePumpDelegate> delegate;
+  MockTimeMessagePump pump(&mock_clock);
+  const auto kNextDelayedWorkTime = mock_clock.NowTicks();
+  mock_clock.Advance(TimeDelta::FromHours(2));
+
+  pump.SetStopWhenMessagePumpIsIdle(true);
+
+  EXPECT_CALL(delegate, DoSomeWork)
+      .WillOnce(Return(NextWorkInfo(kNextDelayedWorkTime)))
+      .WillOnce(Return(NextWorkInfo(kNextDelayedWorkTime)))
+      .WillOnce(Return(NextWorkInfo(TimeTicks::Max())));
+  EXPECT_CALL(delegate, DoIdleWork).WillRepeatedly(Return(false));
+
+  pump.Run(&delegate);
+}
+
+TEST(MockMessagePumpTest,
+     AdvancesUntilAllowedTimeWhenNextDelayedWorkTimeIsMax) {
+  SimpleTestTickClock mock_clock;
+  mock_clock.Advance(TimeDelta::FromHours(42));
+  StrictMock<MockMessagePumpDelegate> delegate;
+  MockTimeMessagePump pump(&mock_clock);
+  const auto kAdvanceUntil =
+      mock_clock.NowTicks() + TimeDelta::FromSeconds(123);
+
+  pump.SetStopWhenMessagePumpIsIdle(true);
+  pump.SetAllowTimeToAutoAdvanceUntil(kAdvanceUntil);
+  EXPECT_CALL(delegate, DoSomeWork)
+      .WillRepeatedly(Return(NextWorkInfo(TimeTicks::Max())));
+  EXPECT_CALL(delegate, DoIdleWork).WillRepeatedly(Return(false));
+
+  pump.Run(&delegate);
+
+  EXPECT_THAT(mock_clock.NowTicks(), Eq(kAdvanceUntil));
+}
+
+}  // namespace
+}  // namespace sequence_manager
+}  // namespace base
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 513b172..7c3632a 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -32,6 +32,8 @@
     "../task/sequence_manager/test/fake_task.h",
     "../task/sequence_manager/test/mock_time_domain.cc",
     "../task/sequence_manager/test/mock_time_domain.h",
+    "../task/sequence_manager/test/mock_time_message_pump.cc",
+    "../task/sequence_manager/test/mock_time_message_pump.h",
     "../task/sequence_manager/test/sequence_manager_for_test.cc",
     "../task/sequence_manager/test/sequence_manager_for_test.h",
     "../task/sequence_manager/test/test_task_queue.cc",