[Reland #3] Use the SequenceManager in ScopedTaskEnvironment

A reland of https://crrev.com/c/1324391

This is necessary because we want content::TestBrowserThreadBundle to
own a BrowserUIThreadScheduler, but that also owns a ScopedTaskEnvironment
and you can't have two SequenceManagers on the same thread.

This patch allows ScopedTaskEnvironment to optionally work with an
externally owned SequenceManager solving the problem.

This implements https://docs.google.com/document/d/1y08C6JQ9Yta3EQXzwIqqIIKHq9500WV6CWFZzZfDx7I/edit?usp=drivesdk,

We now have the ability to mock time on the UI and IO threads.

RE HttpServiceTest change HttpServiceTest.MultipleRequests was failing
mysteriously on fuschia on the bots only (not locally). IO_MOCK_TIME seems
to have fixed this.


TBR=gab@chromium.org,fdoray@chromium.org,wez@chromium.org

Bug: 863341, 891670, 708584
Change-Id: I95444a0a50f8b577c5fae62c12c8423c7e8f21d6
Reviewed-on: https://chromium-review.googlesource.com/c/1361863
Commit-Queue: Alex Clarke <alexclarke@chromium.org>
Reviewed-by: Alex Clarke <alexclarke@chromium.org>
Reviewed-by: Sami Kyöstilä <skyostil@chromium.org>
Cr-Commit-Position: refs/heads/master@{#615496}
diff --git a/base/test/scoped_task_environment.cc b/base/test/scoped_task_environment.cc
index b3c81c7..333956f 100644
--- a/base/test/scoped_task_environment.cc
+++ b/base/test/scoped_task_environment.cc
@@ -13,6 +13,8 @@
 #include "base/synchronization/condition_variable.h"
 #include "base/synchronization/lock.h"
 #include "base/task/post_task.h"
+#include "base/task/sequence_manager/sequence_manager_impl.h"
+#include "base/task/sequence_manager/time_domain.h"
 #include "base/task/task_scheduler/task_scheduler.h"
 #include "base/task/task_scheduler/task_scheduler_impl.h"
 #include "base/test/test_mock_time_task_runner.h"
@@ -36,20 +38,35 @@
 LazyInstance<ThreadLocalPointer<ScopedTaskEnvironment::LifetimeObserver>>::Leaky
     environment_lifetime_observer;
 
-std::unique_ptr<MessageLoop> CreateMessageLoopForMainThreadType(
+base::Optional<MessageLoop::Type> GetMessageLoopTypeForMainThreadType(
     ScopedTaskEnvironment::MainThreadType main_thread_type) {
   switch (main_thread_type) {
     case ScopedTaskEnvironment::MainThreadType::DEFAULT:
-      return std::make_unique<MessageLoop>(MessageLoop::TYPE_DEFAULT);
     case ScopedTaskEnvironment::MainThreadType::MOCK_TIME:
-      return nullptr;
+      return MessageLoop::TYPE_DEFAULT;
     case ScopedTaskEnvironment::MainThreadType::UI:
-      return std::make_unique<MessageLoop>(MessageLoop::TYPE_UI);
+    case ScopedTaskEnvironment::MainThreadType::UI_MOCK_TIME:
+      return MessageLoop::TYPE_UI;
     case ScopedTaskEnvironment::MainThreadType::IO:
-      return std::make_unique<MessageLoop>(MessageLoop::TYPE_IO);
+    case ScopedTaskEnvironment::MainThreadType::IO_MOCK_TIME:
+      return MessageLoop::TYPE_IO;
   }
   NOTREACHED();
-  return nullptr;
+  return base::nullopt;
+}
+
+std::unique_ptr<sequence_manager::SequenceManager>
+CreateSequenceManagerForMainThreadType(
+    ScopedTaskEnvironment::MainThreadType main_thread_type) {
+  auto type = GetMessageLoopTypeForMainThreadType(main_thread_type);
+  if (!type) {
+    return nullptr;
+  } else {
+    auto settings = base::sequence_manager::SequenceManager::Settings{
+        .message_loop_type = *type};
+    return sequence_manager::CreateSequenceManagerOnCurrentThreadWithPump(
+        MessageLoop::CreateMessagePumpForType(*type), std::move(settings));
+  }
 }
 
 class TickClockBasedClock : public Clock {
@@ -71,6 +88,135 @@
 
 }  // namespace
 
+class ScopedTaskEnvironment::MockTimeDomain
+    : public sequence_manager::TimeDomain,
+      public TickClock {
+ public:
+  MockTimeDomain() = default;
+  ~MockTimeDomain() override = default;
+
+  using TimeDomain::NextScheduledRunTime;
+
+  static std::unique_ptr<ScopedTaskEnvironment::MockTimeDomain> Create(
+      ScopedTaskEnvironment::MainThreadType main_thread_type) {
+    if (main_thread_type == MainThreadType::MOCK_TIME ||
+        main_thread_type == MainThreadType::UI_MOCK_TIME ||
+        main_thread_type == MainThreadType::IO_MOCK_TIME) {
+      return std::make_unique<ScopedTaskEnvironment::MockTimeDomain>();
+    }
+    return nullptr;
+  }
+
+  // sequence_manager::TimeDomain:
+
+  sequence_manager::LazyNow CreateLazyNow() const override {
+    base::AutoLock lock(now_ticks_lock_);
+    return sequence_manager::LazyNow(now_ticks_);
+  }
+
+  TimeTicks Now() const override {
+    // This can be called from any thread.
+    base::AutoLock lock(now_ticks_lock_);
+    return now_ticks_;
+  }
+
+  Optional<TimeDelta> DelayTillNextTask(
+      sequence_manager::LazyNow* lazy_now) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    // Make sure TimeDomain::NextScheduledRunTime has taken canceled tasks into
+    // account, ReclaimMemory sweeps canceled delayed tasks.
+    sequence_manager()->ReclaimMemory();
+    Optional<TimeTicks> run_time = NextScheduledRunTime();
+    // Check if we've run out of tasks.
+    if (!run_time)
+      return base::nullopt;
+
+    // Check if we have a task that should be running now.
+    if (run_time <= now_ticks_)
+      return base::TimeDelta();
+
+    // The next task is a future delayed task. Since we're using mock time, we
+    // don't want an actual OS level delayed wake up scheduled, so pretend we
+    // have no more work. This will result in MaybeFastForwardToNextTask getting
+    // called which lets us advance |now_ticks_|.
+    return base::nullopt;
+  }
+
+  // This method is called when the underlying message pump has run out of
+  // non-delayed work.
+  bool MaybeFastForwardToNextTask(bool quit_when_idle_requested) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    // If we're being externally controlled by a RunLoop in client code, check
+    // if the RunLoop is due to quit when idle, if so we don't want to advance
+    // mock time.
+    if (stop_when_message_pump_is_idle_ && quit_when_idle_requested)
+      return false;
+
+    // We don't need to call ReclaimMemory here because
+    // DelayTillNextTask will have dealt with cancelled delayed tasks for us.
+    Optional<TimeTicks> run_time = NextScheduledRunTime();
+    if (!run_time) {
+      // We've run out of tasks, but ScopedTaskEnvironment::FastForwardBy
+      // requires the virtual time to be consumed.
+      if (now_ticks_ < allow_advance_until_ && !allow_advance_until_.is_max())
+        SetTime(allow_advance_until_);
+      return false;
+    }
+
+    // Don't advance past |allow_advance_until_|.
+    DCHECK_GT(*run_time, now_ticks_);
+    TimeTicks time_to_advance_to = std::min(allow_advance_until_, *run_time);
+    if (time_to_advance_to <= now_ticks_)
+      return false;
+
+    SetTime(time_to_advance_to);
+
+    // Make sure a DoWork is scheduled.
+    return true;
+  }
+
+  const char* GetName() const override { return "MockTimeDomain"; }
+
+  // TickClock implementation:
+  TimeTicks NowTicks() const override { return Now(); }
+
+  // Allows time to advance when reaching idle, until
+  // |now_ticks_ == advance_until|. No-op if |advance_until <= now_ticks_|.
+  // Doesn't schedule work by itself.
+  void SetAllowTimeToAutoAdvanceUntil(TimeTicks advance_until) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    allow_advance_until_ = advance_until;
+  }
+
+  void SetStopWhenMessagePumpIsIdle(bool stop_when_message_pump_is_idle) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    stop_when_message_pump_is_idle_ = stop_when_message_pump_is_idle;
+  }
+
+ private:
+  void SetTime(TimeTicks time) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK_LE(time, allow_advance_until_);
+
+    base::AutoLock lock(now_ticks_lock_);
+    now_ticks_ = time;
+  }
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  // By default we want RunLoop.Run() to advance virtual time due to the API
+  // contract.
+  TimeTicks allow_advance_until_ = TimeTicks::Max();
+  bool stop_when_message_pump_is_idle_ = true;
+
+  // Protects |now_ticks_|
+  mutable Lock now_ticks_lock_;
+
+  // Only ever written to from the main sequence.
+  TimeTicks now_ticks_;
+};
+
 class ScopedTaskEnvironment::TestTaskTracker
     : public internal::TaskSchedulerImpl::TaskTrackerImpl {
  public:
@@ -119,31 +265,40 @@
 ScopedTaskEnvironment::ScopedTaskEnvironment(
     MainThreadType main_thread_type,
     ExecutionMode execution_control_mode)
+    : ScopedTaskEnvironment(
+          CreateSequenceManagerForMainThreadType(main_thread_type),
+          nullptr,
+          main_thread_type,
+          execution_control_mode) {}
+
+ScopedTaskEnvironment::ScopedTaskEnvironment(
+    sequence_manager::SequenceManager* sequence_manager,
+    MainThreadType main_thread_type,
+    ExecutionMode execution_control_mode)
+    : ScopedTaskEnvironment(nullptr,
+                            sequence_manager,
+                            main_thread_type,
+                            execution_control_mode) {}
+
+ScopedTaskEnvironment::ScopedTaskEnvironment(
+    std::unique_ptr<sequence_manager::SequenceManager> owned_sequence_manager,
+    sequence_manager::SequenceManager* sequence_manager,
+    MainThreadType main_thread_type,
+    ExecutionMode execution_control_mode)
     : execution_control_mode_(execution_control_mode),
-      message_loop_(CreateMessageLoopForMainThreadType(main_thread_type)),
-      mock_time_task_runner_(
-          main_thread_type == MainThreadType::MOCK_TIME
-              ? MakeRefCounted<TestMockTimeTaskRunner>(
-                    TestMockTimeTaskRunner::Type::kBoundToThread)
-              : nullptr),
-      slsm_for_mock_time_(
-          main_thread_type == MainThreadType::MOCK_TIME
-              ? std::make_unique<internal::SequenceLocalStorageMap>()
-              : nullptr),
-      slsm_registration_for_mock_time_(
-          main_thread_type == MainThreadType::MOCK_TIME
-              ? std::make_unique<
-                    internal::ScopedSetSequenceLocalStorageMapForCurrentThread>(
-                    slsm_for_mock_time_.get())
-              : nullptr),
-      mock_clock_(mock_time_task_runner_
-                      ? std::make_unique<TickClockBasedClock>(
-                            mock_time_task_runner_->GetMockTickClock())
-                      : nullptr),
+      mock_time_domain_(MockTimeDomain::Create(main_thread_type)),
+      owned_sequence_manager_(std::move(owned_sequence_manager)),
+      sequence_manager_(owned_sequence_manager_.get()
+                            ? owned_sequence_manager_.get()
+                            : sequence_manager),
+      task_queue_(CreateDefaultTaskQueue()),
+      mock_clock_(mock_time_domain_ ? std::make_unique<TickClockBasedClock>(
+                                          mock_time_domain_.get())
+                                    : nullptr),
 #if defined(OS_POSIX) || defined(OS_FUCHSIA)
       file_descriptor_watcher_(main_thread_type == MainThreadType::IO
                                    ? std::make_unique<FileDescriptorWatcher>(
-                                         message_loop_->task_runner())
+                                         task_queue_->task_runner())
                                    : nullptr),
 #endif  // defined(OS_POSIX) || defined(OS_FUCHSIA)
       task_tracker_(new TestTaskTracker()) {
@@ -154,6 +309,8 @@
          "someone has explicitly disabled it with "
          "DisableCheckForLeakedGlobals().";
 
+  sequence_manager_->SetDefaultTaskRunner(task_queue_->task_runner());
+
   // Instantiate a TaskScheduler with 2 threads in each of its 4 pools. Threads
   // stay alive even when they don't have work.
   // Each pool uses two threads to prevent deadlocks in unit tests that have a
@@ -220,6 +377,20 @@
   LifetimeObserver* observer = environment_lifetime_observer.Get().Get();
   if (observer)
     observer->OnScopedTaskEnvironmentDestroyed();
+
+  task_queue_ = nullptr;
+  if (mock_time_domain_)
+    sequence_manager_->UnregisterTimeDomain(mock_time_domain_.get());
+}
+
+scoped_refptr<sequence_manager::TaskQueue>
+ScopedTaskEnvironment::CreateDefaultTaskQueue() {
+  if (mock_time_domain_)
+    sequence_manager_->RegisterTimeDomain(mock_time_domain_.get());
+
+  return sequence_manager_->CreateTaskQueue(
+      sequence_manager::TaskQueue::Spec("scoped_task_environment_default")
+          .SetTimeDomain(mock_time_domain_.get()));
 }
 
 void ScopedTaskEnvironment::SetLifetimeObserver(
@@ -230,20 +401,29 @@
 
 scoped_refptr<base::SingleThreadTaskRunner>
 ScopedTaskEnvironment::GetMainThreadTaskRunner() {
-  if (message_loop_)
-    return message_loop_->task_runner();
-  DCHECK(mock_time_task_runner_);
-  return mock_time_task_runner_;
+  return task_queue_->task_runner();
 }
 
 bool ScopedTaskEnvironment::MainThreadHasPendingTask() const {
-  if (message_loop_)
-    return !message_loop_->IsIdleForTesting();
-  DCHECK(mock_time_task_runner_);
-  return mock_time_task_runner_->HasPendingTask();
+  sequence_manager::internal::SequenceManagerImpl* sequence_manager_impl =
+      static_cast<sequence_manager::internal::SequenceManagerImpl*>(
+          sequence_manager_);
+  // ReclaimMemory sweeps canceled delayed tasks.
+  sequence_manager_impl->ReclaimMemory();
+  // Unfortunately this API means different things depending on whether mock
+  // time is used or not. If MockTime is used then tests want to know if there
+  // are any delayed or non-delayed tasks, otherwise only non-delayed tasks are
+  // considered.
+  if (mock_time_domain_)
+    return sequence_manager_impl->HasTasks();
+  return !sequence_manager_impl->IsIdleForTesting();
 }
 
 void ScopedTaskEnvironment::RunUntilIdle() {
+  // Prevent virtual time from advancing while within this call.
+  if (mock_time_domain_)
+    mock_time_domain_->SetAllowTimeToAutoAdvanceUntil(TimeTicks());
+
   // TODO(gab): This can be heavily simplified to essentially:
   //     bool HasMainThreadTasks() {
   //      if (message_loop_)
@@ -321,31 +501,36 @@
   // parallel execution before returning unless in ExecutionMode::QUEUED.
   if (execution_control_mode_ != ExecutionMode::QUEUED)
     task_tracker_->AllowRunTasks();
+
+  if (mock_time_domain_)
+    mock_time_domain_->SetAllowTimeToAutoAdvanceUntil(TimeTicks::Max());
 }
 
 void ScopedTaskEnvironment::FastForwardBy(TimeDelta delta) {
-  DCHECK(mock_time_task_runner_);
-  mock_time_task_runner_->FastForwardBy(delta);
+  MessageLoopCurrent::ScopedNestableTaskAllower allow;
+  DCHECK(mock_time_domain_);
+  mock_time_domain_->SetStopWhenMessagePumpIsIdle(false);
+  mock_time_domain_->SetAllowTimeToAutoAdvanceUntil(mock_time_domain_->Now() +
+                                                    delta);
+  RunLoop().RunUntilIdle();
+  mock_time_domain_->SetStopWhenMessagePumpIsIdle(true);
+  mock_time_domain_->SetAllowTimeToAutoAdvanceUntil(TimeTicks::Max());
 }
 
 void ScopedTaskEnvironment::FastForwardUntilNoTasksRemain() {
-  DCHECK(mock_time_task_runner_);
-  mock_time_task_runner_->FastForwardUntilNoTasksRemain();
+  // TimeTicks::operator+(TimeDelta) uses saturated arithmetic so it's safe to
+  // pass in TimeDelta::Max().
+  FastForwardBy(TimeDelta::Max());
 }
 
 const TickClock* ScopedTaskEnvironment::GetMockTickClock() const {
-  DCHECK(mock_time_task_runner_);
-  return mock_time_task_runner_->GetMockTickClock();
-}
-
-std::unique_ptr<TickClock> ScopedTaskEnvironment::DeprecatedGetMockTickClock() {
-  DCHECK(mock_time_task_runner_);
-  return mock_time_task_runner_->DeprecatedGetMockTickClock();
+  DCHECK(mock_time_domain_);
+  return mock_time_domain_.get();
 }
 
 base::TimeTicks ScopedTaskEnvironment::NowTicks() const {
-  DCHECK(mock_time_task_runner_);
-  return mock_time_task_runner_->NowTicks();
+  DCHECK(mock_time_domain_);
+  return mock_time_domain_->Now();
 }
 
 const Clock* ScopedTaskEnvironment::GetMockClock() const {
@@ -354,13 +539,19 @@
 }
 
 size_t ScopedTaskEnvironment::GetPendingMainThreadTaskCount() const {
-  DCHECK(mock_time_task_runner_);
-  return mock_time_task_runner_->GetPendingTaskCount();
+  // ReclaimMemory sweeps canceled delayed tasks.
+  sequence_manager_->ReclaimMemory();
+  return sequence_manager_->GetPendingTaskCountForTesting();
 }
 
 TimeDelta ScopedTaskEnvironment::NextMainThreadPendingTaskDelay() const {
-  DCHECK(mock_time_task_runner_);
-  return mock_time_task_runner_->NextPendingTaskDelay();
+  // ReclaimMemory sweeps canceled delayed tasks.
+  sequence_manager_->ReclaimMemory();
+  DCHECK(mock_time_domain_);
+  Optional<TimeTicks> run_time = mock_time_domain_->NextScheduledRunTime();
+  if (run_time)
+    return *run_time - mock_time_domain_->Now();
+  return TimeDelta::Max();
 }
 
 ScopedTaskEnvironment::TestTaskTracker::TestTaskTracker()
diff --git a/base/test/scoped_task_environment.h b/base/test/scoped_task_environment.h
index fe77549..b86acbc 100644
--- a/base/test/scoped_task_environment.h
+++ b/base/test/scoped_task_environment.h
@@ -9,21 +9,16 @@
 #include "base/memory/ref_counted.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task/lazy_task_runner.h"
+#include "base/task/sequence_manager/sequence_manager.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 
 namespace base {
 
-namespace internal {
-class ScopedSetSequenceLocalStorageMapForCurrentThread;
-class SequenceLocalStorageMap;
-}  // namespace internal
 
 class Clock;
 class FileDescriptorWatcher;
-class MessageLoop;
 class TaskScheduler;
-class TestMockTimeTaskRunner;
 class TickClock;
 
 namespace test {
@@ -78,9 +73,18 @@
     MOCK_TIME,
     // The main thread pumps UI messages.
     UI,
+    // The main thread pumps UI messages and uses a mock clock for delayed tasks
+    // (controllable via FastForward*() methods).
+    // TODO(gab@): Enable mock time on all threads and make MOCK_TIME
+    // configurable independent of MainThreadType.
+    UI_MOCK_TIME,
     // The main thread pumps asynchronous IO messages and supports the
     // FileDescriptorWatcher API on POSIX.
     IO,
+    // The main thread pumps IO messages and uses a mock clock for delayed tasks
+    // (controllable via FastForward*() methods). In addition it supports the
+    // FileDescriptorWatcher API on POSIX.
+    IO_MOCK_TIME,
   };
 
   enum class ExecutionMode {
@@ -96,6 +100,13 @@
       MainThreadType main_thread_type = MainThreadType::DEFAULT,
       ExecutionMode execution_control_mode = ExecutionMode::ASYNC);
 
+  // Constructs a ScopedTaskEnvironment using a preexisting |sequence_manager|.
+  // |sequence_manager| must outlive this ScopedTaskEnvironment.
+  ScopedTaskEnvironment(
+      sequence_manager::SequenceManager* sequence_manager,
+      MainThreadType main_thread_type = MainThreadType::DEFAULT,
+      ExecutionMode execution_control_mode = ExecutionMode::ASYNC);
+
   // Waits until no undelayed TaskScheduler tasks remain. Then, unregisters the
   // TaskScheduler and the (Thread|Sequenced)TaskRunnerHandle.
   ~ScopedTaskEnvironment();
@@ -165,24 +176,25 @@
   TimeDelta NextMainThreadPendingTaskDelay() const;
 
  private:
+  class MockTimeDomain;
   class TestTaskTracker;
 
+  ScopedTaskEnvironment(
+      std::unique_ptr<sequence_manager::SequenceManager> owned_sequence_manager,
+      sequence_manager::SequenceManager* sequence_manager,
+      MainThreadType main_thread_type,
+      ExecutionMode execution_control_mode);
+
+  scoped_refptr<sequence_manager::TaskQueue> CreateDefaultTaskQueue();
+
   const ExecutionMode execution_control_mode_;
 
-  // Exactly one of these will be non-null to provide the task environment on
-  // the main thread. Users of this class should NOT rely on the presence of a
-  // MessageLoop beyond (Thread|Sequenced)TaskRunnerHandle and RunLoop as
-  // the backing implementation of each MainThreadType may change over time.
-  const std::unique_ptr<MessageLoop> message_loop_;
-  const scoped_refptr<TestMockTimeTaskRunner> mock_time_task_runner_;
+  const std::unique_ptr<MockTimeDomain> mock_time_domain_;
+  const std::unique_ptr<sequence_manager::SequenceManager>
+      owned_sequence_manager_;
+  sequence_manager::SequenceManager* const sequence_manager_;
 
-  // Non-null in MOCK_TIME, where an explicit SequenceLocalStorageMap needs to
-  // be provided. TODO(gab): This can be removed once mock time support is added
-  // to MessageLoop directly.
-  const std::unique_ptr<internal::SequenceLocalStorageMap> slsm_for_mock_time_;
-  const std::unique_ptr<
-      internal::ScopedSetSequenceLocalStorageMapForCurrentThread>
-      slsm_registration_for_mock_time_;
+  scoped_refptr<sequence_manager::TaskQueue> task_queue_;
 
   // Only set for instances with a MOCK_TIME MainThreadType.
   const std::unique_ptr<Clock> mock_clock_;
diff --git a/base/test/scoped_task_environment_unittest.cc b/base/test/scoped_task_environment_unittest.cc
index 2e89ade..b126704 100644
--- a/base/test/scoped_task_environment_unittest.cc
+++ b/base/test/scoped_task_environment_unittest.cc
@@ -373,6 +373,10 @@
     ScopedTaskEnvironmentTest,
     ::testing::Values(ScopedTaskEnvironment::MainThreadType::MOCK_TIME));
 INSTANTIATE_TEST_CASE_P(
+    MainThreadUiMockTime,
+    ScopedTaskEnvironmentTest,
+    ::testing::Values(ScopedTaskEnvironment::MainThreadType::UI_MOCK_TIME));
+INSTANTIATE_TEST_CASE_P(
     MainThreadUI,
     ScopedTaskEnvironmentTest,
     ::testing::Values(ScopedTaskEnvironment::MainThreadType::UI));
@@ -587,6 +591,10 @@
     MainThreadMockTime,
     ScopedTaskEnvironmentMockedTime,
     ::testing::Values(ScopedTaskEnvironment::MainThreadType::MOCK_TIME));
+INSTANTIATE_TEST_CASE_P(
+    MainThreadUiMockTime,
+    ScopedTaskEnvironmentMockedTime,
+    ::testing::Values(ScopedTaskEnvironment::MainThreadType::UI_MOCK_TIME));
 
 }  // namespace test
 }  // namespace base
diff --git a/webrunner/net_http/http_service_unittest.cc b/webrunner/net_http/http_service_unittest.cc
index b77d00f..5b6d20b0 100644
--- a/webrunner/net_http/http_service_unittest.cc
+++ b/webrunner/net_http/http_service_unittest.cc
@@ -35,7 +35,7 @@
  public:
   HttpServiceTest()
       : task_environment_(
-            base::test::ScopedTaskEnvironment::MainThreadType::IO),
+            base::test::ScopedTaskEnvironment::MainThreadType::IO_MOCK_TIME),
         binding_(&http_service_server_) {
     // Initialize the test server.
     test_server_.AddDefaultHandlers(