chromium/base/message_loop/message_pump_unittest.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/message_loop/message_pump.h"

#include <type_traits>

#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/message_loop/message_pump_for_io.h"
#include "base/message_loop/message_pump_for_ui.h"
#include "base/message_loop/message_pump_type.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_executor.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_ANDROID)
#include "base/android/input_hint_checker.h"
#include "base/test/test_support_android.h"
#endif

#if BUILDFLAG(IS_WIN)
#include <windows.h>
#endif

_;
AnyNumber;
AtMost;
Invoke;
Return;

namespace base {

namespace {

// On most platforms, the MessagePump impl controls when native work (e.g.
// handling input messages) gets its turn. Tests below verify that by expecting
// OnBeginWorkItem() calls that cover native work. In some configurations
// however, the platform owns the message loop and is the one yielding to
// Chrome's MessagePump to DoWork(). Under those configurations, it is not
// possible to precisely account for OnBeginWorkItem() calls as they can occur
// nondeterministically. For example, on some versions of iOS, the native loop
// can surprisingly go through multiple cycles of
// kCFRunLoopAfterWaiting=>kCFRunLoopBeforeWaiting before invoking Chrome's
// RunWork() for the first time, triggering multiple  ScopedDoWorkItem 's for
// potential native work before the first DoWork().
constexpr bool ChromeControlsNativeEventProcessing(MessagePumpType pump_type) {}

class MockMessagePumpDelegate : public MessagePump::Delegate {};

class MessagePumpTest : public ::testing::TestWithParam<MessagePumpType> {};

}  // namespace

TEST_P(MessagePumpTest, QuitStopsWork) {}

#if BUILDFLAG(IS_ANDROID)
class MockInputHintChecker : public android::InputHintChecker {
 public:
  MOCK_METHOD(bool, HasInputImplWithThrottling, (), (override));
};

TEST_P(MessagePumpTest, DetectingHasInputYieldsOnUi) {
  testing::InSequence sequence;
  MessagePumpType pump_type = GetParam();
  testing::StrictMock<MockMessagePumpDelegate> delegate(pump_type);
  testing::StrictMock<MockInputHintChecker> hint_checker_mock;
  android::InputHintChecker::ScopedOverrideInstance scoped_override_hint(
      &hint_checker_mock);
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(android::kYieldWithInputHint);
  android::InputHintChecker::InitializeFeatures();
  uint32_t initial_work_enters = GetAndroidNonDelayedWorkEnterCount();

  // Override the first DoWork() to return an immediate next.
  EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([] {
    auto work_info =
        MessagePump::Delegate::NextWorkInfo{.delayed_run_time = TimeTicks()};
    CHECK(work_info.is_immediate());
    return work_info;
  }));

  if (pump_type == MessagePumpType::UI) {
    // Override the following InputHintChecker::HasInput() to return true.
    EXPECT_CALL(hint_checker_mock, HasInputImplWithThrottling())
        .WillOnce(Invoke([] { return true; }));
  }

  // Override the second DoWork() to quit the loop.
  EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this] {
    message_pump_->Quit();
    return MessagePump::Delegate::NextWorkInfo{.delayed_run_time =
                                                   TimeTicks::Max()};
  }));

  // No immediate next_work_info remaining before the yield. Not expecting
  // to observe an input hint check.
  EXPECT_CALL(delegate, DoIdleWork()).Times(0);

  message_pump_->Run(&delegate);

  // Expect two calls to DoNonDelayedLooperWork(). The first one occurs as a
  // result of MessagePump::Run(). The second one is the result of yielding
  // after HasInput() returns true. For non-UI MessagePumpType the
  // MessagePump::Create() does not intercept entering DoNonDelayedLooperWork(),
  // so it remains 0 instead of 1.
  uint32_t work_loop_entered = (pump_type == MessagePumpType::UI) ? 2 : 0;
  EXPECT_EQ(initial_work_enters + work_loop_entered,
            GetAndroidNonDelayedWorkEnterCount());
}
#endif

TEST_P(MessagePumpTest, QuitStopsWorkWithNestedRunLoop) {}

TEST_P(MessagePumpTest, YieldToNativeRequestedSmokeTest) {}

TEST_P(MessagePumpTest, LeewaySmokeTest) {}

TEST_P(MessagePumpTest, RunWithoutScheduleWorkInvokesDoWork) {}

TEST_P(MessagePumpTest, NestedRunWithoutScheduleWorkInvokesDoWork) {}

INSTANTIATE_TEST_SUITE_P();

}  // namespace base