| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef BASE_TEST_TASK_ENVIRONMENT_H_ |
| #define BASE_TEST_TASK_ENVIRONMENT_H_ |
| |
| #include <memory> |
| |
| #include "base/compiler_specific.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/memory/raw_ptr_exclusion.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/observer_list_types.h" |
| #include "base/run_loop.h" |
| #include "base/task/lazy_thread_pool_task_runner.h" |
| #include "base/task/sequence_manager/sequence_manager.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/test/scoped_run_loop_timeout.h" |
| #include "base/threading/thread_checker.h" |
| #include "base/time/time.h" |
| #include "base/traits_bag.h" |
| #include "build/build_config.h" |
| |
| namespace base { |
| |
| class Clock; |
| class FileDescriptorWatcher; |
| class TickClock; |
| |
| namespace subtle { |
| class ScopedTimeClockOverrides; |
| } |
| |
| namespace test { |
| |
| // This header exposes SingleThreadTaskEnvironment and TaskEnvironment. |
| // |
| // SingleThreadTaskEnvironment enables the following APIs within its scope: |
| // - (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle on the main |
| // thread |
| // - RunLoop on the main thread |
| // |
| // TaskEnvironment additionally enables: |
| // - posting to base::ThreadPool through base/task/thread_pool.h. |
| // |
| // Hint: For content::BrowserThreads, use content::BrowserTaskEnvironment. |
| // |
| // Tests should prefer SingleThreadTaskEnvironment over TaskEnvironment when the |
| // former is sufficient. |
| // |
| // Tasks posted to the (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle |
| // run synchronously when RunLoop::Run(UntilIdle) or |
| // TaskEnvironment::RunUntil(Idle|Quit) is called on the main thread. |
| // |
| // The TaskEnvironment requires TestTimeouts::Initialize() to be called in order |
| // to run posted tasks, so that it can watch for problematic long-running tasks. |
| // |
| // The TimeSource trait can be used to request that delayed tasks be under the |
| // manual control of RunLoop::Run() and TaskEnvironment::FastForward*() methods. |
| // |
| // If a TaskEnvironment's ThreadPoolExecutionMode is QUEUED, ThreadPool tasks |
| // run when RunUntilIdle(), RunUntilQuit(), or ~TaskEnvironment is called. If |
| // ThreadPoolExecutionMode is ASYNC, they run as they are posted. |
| // |
| // All TaskEnvironment methods must be called from the main thread. |
| // |
| // Usage: |
| // |
| // class MyTestFixture : public testing::Test { |
| // public: |
| // (...) |
| // |
| // // protected rather than private visibility will allow controlling the |
| // // task environment (e.g. RunUntilIdle(), FastForwardBy(), etc.). from the |
| // // test body. |
| // protected: |
| // // Must generally be the first member to be initialized first and |
| // // destroyed last (some members that require single-threaded |
| // // initialization and tear down may need to come before -- e.g. |
| // // base::test::ScopedFeatureList). Extra traits, like TimeSource, are |
| // // best provided inline when declaring the TaskEnvironment, as |
| // // such: |
| // base::test::TaskEnvironment task_environment_{ |
| // base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| // |
| // // Other members go here (or further below in private section.) |
| // }; |
| class TaskEnvironment { |
| protected: |
| // This enables a two-phase initialization for sub classes such as |
| // content::BrowserTaskEnvironment which need to provide the default task |
| // queue because they instantiate a scheduler on the same thread. Subclasses |
| // using this trait must invoke DeferredInitFromSubclass() before running the |
| // task environment. |
| struct SubclassCreatesDefaultTaskRunner {}; |
| |
| public: |
| enum class TimeSource { |
| // Delayed tasks and Time/TimeTicks::Now() use the real-time system clock. |
| SYSTEM_TIME, |
| |
| // Delayed tasks use a mock clock which only advances when reaching "idle" |
| // during a RunLoop::Run() call on the main thread or a FastForward*() call |
| // to this TaskEnvironment. "idle" is defined as the main thread and thread |
| // pool being out of ready tasks. In that situation : time advances to the |
| // soonest delay between main thread and thread pool delayed tasks, |
| // according to the semantics of the current Run*() or FastForward*() call. |
| // |
| // This also mocks Time/TimeTicks::Now() with the same mock clock. |
| // Time::Now() and TimeTicks::Now() (with respect to its origin) start |
| // without submillisecond components. |
| // |
| // Warning some platform APIs are still real-time, e.g.: |
| // * PlatformThread::Sleep |
| // * WaitableEvent::TimedWait |
| // * ConditionVariable::TimedWait |
| // * Delayed tasks on unmanaged base::Thread's and other custom task |
| // runners. |
| MOCK_TIME, |
| |
| DEFAULT = SYSTEM_TIME |
| }; |
| |
| // This type will determine what types of messages will get pumped by the main |
| // thread. |
| // Note: If your test needs to use a custom MessagePump you should |
| // consider using a SingleThreadTaskExecutor instead. |
| enum class MainThreadType { |
| // The main thread doesn't pump system messages. |
| DEFAULT, |
| // The main thread pumps UI messages. |
| UI, |
| // The main thread pumps asynchronous IO messages and supports the |
| // FileDescriptorWatcher API on POSIX. |
| IO, |
| }; |
| |
| // Note that this is irrelevant (and ignored) under |
| // ThreadingMode::MAIN_THREAD_ONLY |
| enum class ThreadPoolExecutionMode { |
| // Thread pool tasks are queued and only executed when RunUntilIdle(), |
| // FastForwardBy(), or FastForwardUntilNoTasksRemain() are explicitly |
| // called. Note: RunLoop::Run() does *not* unblock the ThreadPool in this |
| // mode (it strictly runs only the main thread). |
| QUEUED, |
| // Thread pool tasks run as they are posted. RunUntilIdle() can still be |
| // used to block until done. |
| // Note that regardless of this trait, delayed tasks are always "queued" |
| // under TimeSource::MOCK_TIME mode. |
| ASYNC, |
| DEFAULT = ASYNC |
| }; |
| |
| enum class ThreadingMode { |
| // ThreadPool will be initialized, thus adding support for multi-threaded |
| // tests. |
| MULTIPLE_THREADS, |
| // No thread pool will be initialized. Useful for tests that want to run |
| // single threaded. Prefer using SingleThreadTaskEnvironment over this |
| // trait. |
| MAIN_THREAD_ONLY, |
| DEFAULT = MULTIPLE_THREADS |
| }; |
| |
| // On Windows, sets the COM environment for the ThreadPoolInstance. Ignored |
| // on other platforms. |
| enum class ThreadPoolCOMEnvironment { |
| // Do not initialize COM for the pool's workers. |
| NONE, |
| |
| // Place the pool's workers in a COM MTA. |
| COM_MTA, |
| |
| // Enable the MTA by default in unit tests to match the browser process's |
| // ThreadPoolInstance configuration. |
| // |
| // This has the adverse side-effect of enabling the MTA in non-browser unit |
| // tests as well but the downside there is not as bad as not having it in |
| // browser unit tests. It just means some COM asserts may pass in unit |
| // tests where they wouldn't in integration tests or prod. That's okay |
| // because unit tests are already generally very loose on allowing I/O, |
| // waits, etc. Such misuse will still be caught in later phases (and COM |
| // usage should already be pretty much inexistent in sandboxed processes). |
| DEFAULT = COM_MTA, |
| }; |
| |
| // List of traits that are valid inputs for the constructor below. |
| struct ValidTraits { |
| ValidTraits(TimeSource); |
| ValidTraits(MainThreadType); |
| ValidTraits(ThreadPoolExecutionMode); |
| ValidTraits(SubclassCreatesDefaultTaskRunner); |
| ValidTraits(ThreadingMode); |
| ValidTraits(ThreadPoolCOMEnvironment); |
| }; |
| |
| // Constructor accepts zero or more traits which customize the testing |
| // environment. |
| template <typename... TaskEnvironmentTraits, |
| class CheckArgumentsAreValid = std::enable_if_t< |
| trait_helpers::AreValidTraits<ValidTraits, |
| TaskEnvironmentTraits...>::value>> |
| NOINLINE explicit TaskEnvironment(TaskEnvironmentTraits... traits) |
| : TaskEnvironment(sequence_manager::SequenceManager::PrioritySettings:: |
| CreateDefault(), |
| traits...) {} |
| |
| TaskEnvironment(const TaskEnvironment&) = delete; |
| TaskEnvironment& operator=(const TaskEnvironment&) = delete; |
| |
| // Waits until no undelayed ThreadPool tasks remain. Then, unregisters the |
| // ThreadPoolInstance and the |
| // (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle. |
| virtual ~TaskEnvironment(); |
| |
| // Returns a TaskRunner that schedules tasks on the main thread. |
| scoped_refptr<base::SingleThreadTaskRunner> GetMainThreadTaskRunner(); |
| |
| // Returns whether the main thread's TaskRunner has pending tasks. This will |
| // always return true if called right after RunUntilIdle. |
| bool MainThreadIsIdle() const; |
| |
| // Returns a RepeatingClosure that ends the next call to RunUntilQuit(). The |
| // quit closures must be obtained from the thread owning the TaskEnvironment |
| // but may then be invoked from any thread. To avoid a potential race |
| // condition, do not call QuitClosure() while RunUntilQuit() is running. |
| RepeatingClosure QuitClosure(); |
| |
| // Runs tasks on both the main thread and the thread pool, until a quit |
| // closure is executed. When RunUntilQuit() returns, all previous quit |
| // closures are invalidated, and will have no effect on future calls. Be sure |
| // to create a new quit closure before calling RunUntilQuit() again. |
| void RunUntilQuit(); |
| |
| // Runs tasks until both the |
| // (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle and the |
| // ThreadPool's non-delayed queues are empty. While RunUntilIdle() is quite |
| // practical and sometimes even necessary -- for example, to flush all tasks |
| // bound to Unretained() state before destroying test members -- it should be |
| // used with caution per the following warnings: |
| // |
| // WARNING #1: This may run long (flakily timeout) and even never return! Do |
| // not use this when repeating tasks such as animated web pages |
| // are present. |
| // WARNING #2: This may return too early! For example, if used to run until an |
| // incoming event has occurred but that event depends on a task in |
| // a different queue -- e.g. a standalone base::Thread or a system |
| // event. |
| // |
| // As such, prefer RunLoop::Run() with an explicit RunLoop::QuitClosure() when |
| // possible. |
| void RunUntilIdle(); |
| |
| // Only valid for instances using TimeSource::MOCK_TIME. Fast-forwards |
| // virtual time by |delta|, causing all tasks on the main thread and thread |
| // pool with a remaining delay less than or equal to |delta| to be executed in |
| // their natural order before this returns. |delta| must be non-negative. Upon |
| // returning from this method, NowTicks() will be >= the initial |NowTicks() + |
| // delta|. It is guaranteed to be == iff tasks executed in this |
| // FastForwardBy() didn't result in nested calls to time-advancing-methods. |
| void FastForwardBy(TimeDelta delta); |
| |
| // Only valid for instances using TimeSource::MOCK_TIME. |
| // Short for FastForwardBy(TimeDelta::Max()). |
| // |
| // WARNING: This has the same caveat as RunUntilIdle() and is even more likely |
| // to spin forever (any RepeatingTimer will cause this). |
| void FastForwardUntilNoTasksRemain(); |
| |
| // Only valid for instances using TimeSource::MOCK_TIME. Advances virtual time |
| // by |delta|. Unlike FastForwardBy, this does not run tasks. Prefer |
| // FastForwardBy() when possible but this can be useful when testing blocked |
| // pending tasks where being idle (required to fast-forward) is not possible. |
| // |
| // Delayed tasks that are ripe as a result of this will be scheduled. |
| // RunUntilIdle() can be used after this call to ensure those tasks have run. |
| // Note: AdvanceClock(delta) + RunUntilIdle() is slightly different from |
| // FastForwardBy(delta) in that time passes instantly before running any task |
| // (whereas FastForwardBy() will advance the clock in the smallest increments |
| // possible at a time). Hence FastForwardBy() is more realistic but |
| // AdvanceClock() can be useful when testing edge case scenarios that |
| // specifically handle more time than expected to have passed. |
| void AdvanceClock(TimeDelta delta); |
| |
| bool UsesMockTime() const { return !!mock_clock_; } |
| |
| // Only valid for instances using TimeSource::MOCK_TIME. Returns a |
| // TickClock whose time is updated by FastForward(By|UntilNoTasksRemain). |
| const TickClock* GetMockTickClock() const; |
| |
| // Only valid for instances using TimeSource::MOCK_TIME. Returns a |
| // Clock whose time is updated by FastForward(By|UntilNoTasksRemain). The |
| // initial value is implementation defined and should be queried by tests that |
| // depend on it. |
| // TickClock should be used instead of Clock to measure elapsed time in a |
| // process. See time.h. |
| const Clock* GetMockClock() const; |
| |
| // Only valid for instances using TimeSource::MOCK_TIME. Returns the current |
| // virtual tick time (based on a realistic Now(), sampled when this |
| // TaskEnvironment was created, and manually advanced from that point on). |
| // This is always equivalent to base::TimeTicks::Now() under |
| // TimeSource::MOCK_TIME. |
| base::TimeTicks NowTicks() const; |
| |
| // Only valid for instances using TimeSource::MOCK_TIME. Returns the number of |
| // pending tasks (delayed and non-delayed) of the main thread's TaskRunner. |
| // When debugging, you can use DescribeCurrentTasks() to see what those are. |
| size_t GetPendingMainThreadTaskCount() const; |
| |
| // Only valid for instances using TimeSource::MOCK_TIME. |
| // Returns the delay until the next pending task of the main thread's |
| // TaskRunner if there is one, otherwise it returns TimeDelta::Max(). |
| TimeDelta NextMainThreadPendingTaskDelay() const; |
| |
| // Only valid for instances using TimeSource::MOCK_TIME. |
| // Returns true iff the next task is delayed. Returns false if the next task |
| // is immediate or if there is no next task. |
| bool NextTaskIsDelayed() const; |
| |
| // For debugging purposes: Dumps information about pending tasks on the main |
| // thread, and currently running tasks on the thread pool. |
| void DescribeCurrentTasks() const; |
| |
| // Detach ThreadCheckers (will rebind on next usage), useful for the odd test |
| // suite which doesn't run on the main thread but still has exclusive access |
| // to driving this TaskEnvironment (e.g. WaylandClientTestSuiteServer). |
| void DetachFromThread(); |
| |
| class TestTaskTracker; |
| // Callers outside of TaskEnvironment may not use the returned pointer. They |
| // should just use base::ThreadPoolInstance::Get(). |
| static TestTaskTracker* CreateThreadPool(); |
| |
| class DestructionObserver : public CheckedObserver { |
| public: |
| DestructionObserver() = default; |
| ~DestructionObserver() override = default; |
| |
| DestructionObserver(const DestructionObserver&) = delete; |
| DestructionObserver& operator=(const DestructionObserver&) = delete; |
| |
| virtual void WillDestroyCurrentTaskEnvironment() = 0; |
| }; |
| |
| // Adds/removes a DestructionObserver to any TaskEnvironment. Observers are |
| // notified when any TaskEnvironment goes out of scope (other than with a move |
| // operation). Must be called on the main thread. |
| static void AddDestructionObserver(DestructionObserver* observer); |
| static void RemoveDestructionObserver(DestructionObserver* observer); |
| |
| // Instantiating a ParallelExecutionFence waits for all currently running |
| // ThreadPool tasks before the constructor returns and from then on prevents |
| // additional tasks from running during its lifetime. |
| // |
| // Must be instantiated from the test main thread. |
| class ParallelExecutionFence { |
| public: |
| // Instantiates a ParallelExecutionFence, crashes with an optional |
| // |error_message| if not invoked from test main thread. |
| explicit ParallelExecutionFence(const char* error_message = ""); |
| ~ParallelExecutionFence(); |
| |
| ParallelExecutionFence(const ParallelExecutionFence&) = delete; |
| ParallelExecutionFence& operator=(const ParallelExecutionFence& other) = |
| delete; |
| |
| private: |
| bool previously_allowed_to_run_ = false; |
| }; |
| |
| // The number of foreground workers in the ThreadPool managed by a |
| // TaskEnvironment instance. This can be used to determine the maximum |
| // parallelism in tests that require each parallel task it spawns to be |
| // running at once. Having multiple threads prevents deadlocks should some |
| // blocking APIs not use ScopedBlockingCall. It also allows enough concurrency |
| // to allow TSAN to spot data races. |
| static constexpr int kNumForegroundThreadPoolThreads = 4; |
| |
| protected: |
| template <typename... TaskEnvironmentTraits, |
| class CheckArgumentsAreValid = std::enable_if_t< |
| trait_helpers::AreValidTraits<ValidTraits, |
| TaskEnvironmentTraits...>::value>> |
| NOINLINE static TaskEnvironment CreateTaskEnvironmentWithPriorities( |
| sequence_manager::SequenceManager::PrioritySettings priority_settings, |
| TaskEnvironmentTraits... traits) { |
| return TaskEnvironment(std::move(priority_settings), traits...); |
| } |
| |
| // Constructor accepts zero or more traits which customize the testing |
| // environment. |
| template <typename... TaskEnvironmentTraits, |
| class CheckArgumentsAreValid = std::enable_if_t< |
| trait_helpers::AreValidTraits<ValidTraits, |
| TaskEnvironmentTraits...>::value>> |
| NOINLINE explicit TaskEnvironment( |
| sequence_manager::SequenceManager::PrioritySettings priority_settings, |
| TaskEnvironmentTraits... traits) |
| : TaskEnvironment( |
| std::move(priority_settings), |
| trait_helpers::GetEnum<TimeSource, TimeSource::DEFAULT>(traits...), |
| trait_helpers::GetEnum<MainThreadType, MainThreadType::DEFAULT>( |
| traits...), |
| trait_helpers::GetEnum<ThreadPoolExecutionMode, |
| ThreadPoolExecutionMode::DEFAULT>(traits...), |
| trait_helpers::GetEnum<ThreadingMode, ThreadingMode::DEFAULT>( |
| traits...), |
| trait_helpers::GetEnum<ThreadPoolCOMEnvironment, |
| ThreadPoolCOMEnvironment::DEFAULT>( |
| traits...), |
| trait_helpers::HasTrait<SubclassCreatesDefaultTaskRunner, |
| TaskEnvironmentTraits...>(), |
| trait_helpers::NotATraitTag()) {} |
| |
| TaskEnvironment(TaskEnvironment&& other); |
| |
| constexpr MainThreadType main_thread_type() const { |
| return main_thread_type_; |
| } |
| |
| constexpr ThreadPoolExecutionMode thread_pool_execution_mode() const { |
| return thread_pool_execution_mode_; |
| } |
| |
| // Returns the MockTimeDomain driving this TaskEnvironment if this instance is |
| // using TimeSource::MOCK_TIME, nullptr otherwise. |
| sequence_manager::TimeDomain* GetMockTimeDomain() const; |
| |
| sequence_manager::SequenceManager* sequence_manager() const; |
| |
| void DeferredInitFromSubclass( |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner); |
| |
| // Derived classes may need to control when the task environment goes away |
| // (e.g. ~FooTaskEnvironment() may want to effectively trigger |
| // ~TaskEnvironment() before its members are destroyed). |
| void DestroyTaskEnvironment(); |
| |
| private: |
| class MockTimeDomain; |
| |
| void InitializeThreadPool(); |
| void ShutdownAndJoinThreadPool(); |
| void DestroyThreadPool(); |
| |
| void CompleteInitialization(); |
| |
| // The template constructor has to be in the header but it delegates to this |
| // constructor to initialize all other members out-of-line. |
| TaskEnvironment( |
| sequence_manager::SequenceManager::PrioritySettings priority_settings, |
| TimeSource time_source, |
| MainThreadType main_thread_type, |
| ThreadPoolExecutionMode thread_pool_execution_mode, |
| ThreadingMode threading_mode, |
| ThreadPoolCOMEnvironment thread_pool_com_environment, |
| bool subclass_creates_default_taskrunner, |
| trait_helpers::NotATraitTag tag); |
| |
| const MainThreadType main_thread_type_; |
| const ThreadPoolExecutionMode thread_pool_execution_mode_; |
| const ThreadingMode threading_mode_; |
| const ThreadPoolCOMEnvironment thread_pool_com_environment_; |
| const bool subclass_creates_default_taskrunner_; |
| |
| std::unique_ptr<sequence_manager::SequenceManager> sequence_manager_; |
| |
| // Manages the clock under TimeSource::MOCK_TIME modes. Null in |
| // TimeSource::SYSTEM_TIME mode. |
| std::unique_ptr<MockTimeDomain> mock_time_domain_; |
| |
| // Overrides Time/TimeTicks::Now() under TimeSource::MOCK_TIME mode. |
| // Null in other modes. |
| std::unique_ptr<subtle::ScopedTimeClockOverrides> time_overrides_; |
| |
| scoped_refptr<sequence_manager::TaskQueue> task_queue_; |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| |
| // Only set for instances using TimeSource::MOCK_TIME. |
| std::unique_ptr<Clock> mock_clock_; |
| |
| #if defined(STARBOARD) |
| #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) |
| // Enables the FileDescriptorWatcher API iff running a MainThreadType::IO. |
| std::unique_ptr<FileDescriptorWatcher> file_descriptor_watcher_; |
| #endif |
| |
| // Owned by the ThreadPoolInstance. |
| // This field is not a raw_ptr<> because it was filtered by the rewriter for: |
| // #union |
| RAW_PTR_EXCLUSION TestTaskTracker* task_tracker_ = nullptr; |
| |
| // Ensures destruction of lazy TaskRunners when this is destroyed. |
| std::unique_ptr<base::internal::ScopedLazyTaskRunnerListForTesting> |
| scoped_lazy_task_runner_list_for_testing_; |
| |
| // Sets RunLoop::Run() to LOG(FATAL) if not Quit() in a timely manner. |
| std::unique_ptr<ScopedRunLoopTimeout> run_loop_timeout_; |
| |
| std::unique_ptr<bool> owns_instance_ = std::make_unique<bool>(true); |
| |
| std::unique_ptr<RunLoop> run_until_quit_loop_; |
| |
| // Used to verify thread-affinity of operations that must occur on the main |
| // thread. This is the case for anything that modifies or drives the |
| // |sequence_manager_|. |
| THREAD_CHECKER(main_thread_checker_); |
| }; |
| |
| // SingleThreadTaskEnvironment takes the same traits as TaskEnvironment and is |
| // used the exact same way. It's a short-form for |
| // TaskEnvironment{TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY, ...}; |
| class SingleThreadTaskEnvironment : public TaskEnvironment { |
| public: |
| template <class... ArgTypes> |
| SingleThreadTaskEnvironment(ArgTypes... args) |
| : TaskEnvironment(ThreadingMode::MAIN_THREAD_ONLY, args...) {} |
| }; |
| |
| } // namespace test |
| } // namespace base |
| |
| #endif // BASE_TEST_TASK_ENVIRONMENT_H_ |