chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_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/task/sequence_manager/thread_controller_with_message_pump_impl.h"

#include <array>
#include <optional>
#include <queue>
#include <string>
#include <utility>
#include <vector>

#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/message_loop/message_pump.h"
#include "base/task/sequence_manager/task_queue.h"
#include "base/task/sequence_manager/thread_controller_power_monitor.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_features.h"
#include "base/test/bind.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

_;
Invoke;
ElementsAre;

namespace base::sequence_manager::internal {

namespace {

class ThreadControllerForTest : public ThreadControllerWithMessagePumpImpl {};

class MockMessagePump : public MessagePump {};

// TODO(crbug.com/40600768): Deduplicate FakeTaskRunners.
class FakeTaskRunner : public SingleThreadTaskRunner {};

class FakeSequencedTaskSource : public SequencedTaskSource {};

}  // namespace

class ThreadControllerWithMessagePumpTestBase : public testing::Test {};

class ThreadControllerWithMessagePumpTest
    : public ThreadControllerWithMessagePumpTestBase {};

class ThreadControllerWithMessagePumpNoBatchesTest
    : public ThreadControllerWithMessagePumpTestBase {};

TEST_F(ThreadControllerWithMessagePumpTest, ScheduleDelayedWork) {}

TEST_F(ThreadControllerWithMessagePumpTest, SetNextDelayedDoWork) {}

TEST_F(ThreadControllerWithMessagePumpTest, SetNextDelayedDoWork_CapAtOneDay) {}

TEST_F(ThreadControllerWithMessagePumpTest, DelayedWork_CapAtOneDay) {}

TEST_F(ThreadControllerWithMessagePumpTest, DoWorkDoesntScheduleDelayedWork) {}

TEST_F(ThreadControllerWithMessagePumpTest, NestedExecution) {}

TEST_F(ThreadControllerWithMessagePumpTest,
       NestedExecutionWithApplicationTasks) {}

TEST_F(ThreadControllerWithMessagePumpTest, SetDefaultTaskRunner) {}

TEST_F(ThreadControllerWithMessagePumpTest, EnsureWorkScheduled) {}

TEST_F(ThreadControllerWithMessagePumpTest, WorkBatching) {}

TEST_F(ThreadControllerWithMessagePumpTest, QuitInterruptsBatch) {}

TEST_F(ThreadControllerWithMessagePumpTest, PrioritizeYieldingToNative) {}

TEST_F(ThreadControllerWithMessagePumpTest, EarlyQuit) {}

TEST_F(ThreadControllerWithMessagePumpTest, NativeNestedMessageLoop) {}

TEST_F(ThreadControllerWithMessagePumpTest, RunWithTimeout) {}

#if BUILDFLAG(IS_WIN)
TEST_F(ThreadControllerWithMessagePumpTest, SetHighResolutionTimer) {
  MockCallback<OnceClosure> task;
  task_source_.AddTask(FROM_HERE, task.Get(), FromNow(Seconds(5)),
                       clock_.NowTicks());

  SingleThreadTaskRunner::CurrentDefaultHandle handle(
      MakeRefCounted<FakeTaskRunner>());

  EXPECT_CALL(*message_pump_, Run(_))
      .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
        // Should initially not be in high resolution.
        EXPECT_FALSE(
            thread_controller_.MainThreadOnlyForTesting().in_high_res_mode);

        // Ensures timer resolution is set to high resolution.
        task_source_.SetHasPendingHighResolutionTasks(true);
        delegate->DoIdleWork();
        EXPECT_TRUE(
            thread_controller_.MainThreadOnlyForTesting().in_high_res_mode);

        // Ensures time resolution is set back to low resolution.
        task_source_.SetHasPendingHighResolutionTasks(false);
        delegate->DoIdleWork();
        EXPECT_FALSE(
            thread_controller_.MainThreadOnlyForTesting().in_high_res_mode);

        EXPECT_CALL(*message_pump_, Quit());
        thread_controller_.Quit();
      }));

  RunLoop run_loop;
  run_loop.Run();
}
#endif  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(IS_WIN)
TEST_F(ThreadControllerWithMessagePumpTest,
       SetHighResolutionTimerWithPowerSuspend) {
  MockCallback<OnceClosure> task;
  task_source_.AddTask(FROM_HERE, task.Get(), FromNow(Seconds(5)),
                       clock_.NowTicks());

  SingleThreadTaskRunner::CurrentDefaultHandle handle(
      MakeRefCounted<FakeTaskRunner>());

  EXPECT_CALL(*message_pump_, Run(_))
      .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
        // Should initially not be in high resolution.
        EXPECT_FALSE(
            thread_controller_.MainThreadOnlyForTesting().in_high_res_mode);

        // The power suspend notification is sent.
        thread_controller_.ThreadControllerPowerMonitorForTesting()
            ->OnSuspend();

        // The timer resolution should NOT be updated during power suspend.
        task_source_.SetHasPendingHighResolutionTasks(true);
        delegate->DoIdleWork();
        EXPECT_FALSE(
            thread_controller_.MainThreadOnlyForTesting().in_high_res_mode);

        // The power resume notification is sent.
        thread_controller_.ThreadControllerPowerMonitorForTesting()->OnResume();

        // Ensures timer resolution is set to high resolution.
        delegate->DoIdleWork();
        EXPECT_TRUE(
            thread_controller_.MainThreadOnlyForTesting().in_high_res_mode);

        EXPECT_CALL(*message_pump_, Quit());
        thread_controller_.Quit();
      }));

  RunLoop run_loop;
  run_loop.Run();
}
#endif  // BUILDFLAG(IS_WIN)

TEST_F(ThreadControllerWithMessagePumpTest,
       ScheduleDelayedWorkWithPowerSuspend) {}

TEST_F(ThreadControllerWithMessagePumpTest,
       ThreadControllerActiveSingleApplicationTask) {}

TEST_F(ThreadControllerWithMessagePumpTest,
       ThreadControllerActiveMultipleApplicationTasks) {}

TEST_F(ThreadControllerWithMessagePumpTest,
       ThreadControllerActiveWakeUpForNothing) {}

TEST_F(ThreadControllerWithMessagePumpTest, DoWorkBatches) {}

TEST_F(ThreadControllerWithMessagePumpNoBatchesTest, DoWorkBatches) {}

TEST_F(ThreadControllerWithMessagePumpTest, DoWorkBatchesForSetTime) {}

TEST_F(ThreadControllerWithMessagePumpTest,
       ThreadControllerActiveAdvancedNesting) {}

TEST_F(ThreadControllerWithMessagePumpTest,
       ThreadControllerActiveNestedNativeLoop) {}

TEST_F(ThreadControllerWithMessagePumpTest,
       ThreadControllerActiveUnusedNativeLoop) {}

TEST_F(ThreadControllerWithMessagePumpTest,
       ThreadControllerActiveNestedNativeLoopWithoutAllowance) {}

TEST_F(ThreadControllerWithMessagePumpTest,
       ThreadControllerActiveMultipleNativeLoopsUnderOneApplicationTask) {}

TEST_F(ThreadControllerWithMessagePumpTest,
       ThreadControllerActiveNativeLoopsReachingIdle) {}

TEST_F(ThreadControllerWithMessagePumpTest,
       ThreadControllerActiveQuitNestedWhileApplicationIdle) {}

// This test verifies the edge case where the first task on the stack is native
// task which spins a native nested loop. That inner-loop should be allowed to
// execute application tasks as the outer-loop didn't consume
// |task_execution_allowed == true|. RunLevelTracker should support this use
// case as well.
TEST_F(ThreadControllerWithMessagePumpTest,
       ThreadControllerActiveNestedWithinNativeAllowsApplicationTasks) {}

// Same as ThreadControllerActiveNestedWithinNativeAllowsApplicationTasks but
// with a dummy ScopedAllowApplicationTasksInNativeNestedLoop that is a
// true=>true no-op for SetTaskExecutionAllowedInNativeNestedLoop(). This is a
// regression test against another discussed implementation for RunLevelTracker
// which would have used ScopedAllowApplicationTasksInNativeNestedLoop as a hint
// of nested native loops. Doing so would have been incorrect because it assumes
// that ScopedAllowApplicationTasksInNativeNestedLoop always toggles the
// allowance away-from and back-to |false|.
TEST_F(ThreadControllerWithMessagePumpTest,
       ThreadControllerActiveDummyScopedAllowApplicationTasks) {}

// Verify that the kScheduled phase is emitted when coming out of idle and
// `queue_time` is set on PendingTasks.
TEST_F(ThreadControllerWithMessagePumpTest, MessagePumpPhasesWithQueuingTime) {}

TEST_F(ThreadControllerWithMessagePumpNoBatchesTest,
       WorkIdIncrementedForEveryWorkItem) {}

TEST_F(ThreadControllerWithMessagePumpTest,
       WorkIdIncrementedForEveryWorkItemInBatches) {}

TEST_F(ThreadControllerWithMessagePumpTest, WorkIdIncrementedForIdleWork) {}

TEST_F(ThreadControllerWithMessagePumpTest, WorkIdIncrementedScopedDoWorkItem) {}

TEST_F(ThreadControllerWithMessagePumpTest,
       WorkIdIncrementedDelegateBeforeWait) {}

TEST_F(ThreadControllerWithMessagePumpTest, WorkIdIncrementedDelegateRun) {}

}  // namespace base::sequence_manager::internal