1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CONTENT_PUBLIC_TEST_BROWSER_TASK_ENVIRONMENT_H_ 6 #define CONTENT_PUBLIC_TEST_BROWSER_TASK_ENVIRONMENT_H_ 7 8 #include <memory> 9 10 #include "base/compiler_specific.h" 11 #include "base/macros.h" 12 #include "base/test/task_environment.h" 13 #include "build/build_config.h" 14 15 namespace base { 16 #if defined(OS_WIN) 17 namespace win { 18 class ScopedCOMInitializer; 19 } // namespace win 20 #endif 21 } // namespace base 22 23 namespace content { 24 25 class TestBrowserThread; 26 27 // BrowserTaskEnvironment is a convenience class which allows usage of these 28 // APIs within its scope: 29 // - Same APIs as base::test::TaskEnvironment. 30 // - content::BrowserThread. 31 // - Public APIs of base::test::TaskEnvironment. 32 // 33 // Only tests that need the BrowserThread API should instantiate a 34 // BrowserTaskEnvironment. Use base::test::SingleThhreadTaskEnvironment or 35 // base::test::TaskEnvironment otherwise. 36 // 37 // By default, BrowserThread::UI/IO are backed by a single shared message loop 38 // on the main thread. If a test truly needs BrowserThread::IO tasks to run on a 39 // separate thread, it can pass the REAL_IO_THREAD option to the constructor. 40 // ThreadPool tasks always run on dedicated threads. 41 // 42 // To synchronously run tasks from the shared message loop: 43 // 44 // ... until there are no undelayed tasks in the shared message loop: 45 // base::RunLoop::RunUntilIdle(); 46 // 47 // ... until there are no undelayed tasks in the shared message loop, in 48 // ThreadPool (excluding tasks not posted from the shared message loop's thread 49 // or ThreadPool): task_environment.RunUntilIdle(); 50 // 51 // ... until a condition is met: 52 // base::RunLoop run_loop; 53 // // Runs until a task running in the shared message loop calls 54 // // run_loop.Quit() or runs run_loop.QuitClosure() (&run_loop or 55 // // run_loop.QuitClosure() must be kept somewhere accessible by that task). 56 // run_loop.Run(); 57 // 58 // To wait until there are no pending undelayed tasks in ThreadPool, without 59 // running tasks from the shared message loop (this is rarely needed): 60 // base::ThreadPoolInstance::Get()->FlushForTesting(); 61 // 62 // The destructor of BrowserTaskEnvironment runs remaining UI/IO tasks and 63 // remaining thread pool tasks. 64 // 65 // If a test needs to pump IO messages on the main thread, it should use the 66 // IO_MAINLOOP option. Most of the time, IO_MAINLOOP avoids needing to use a 67 // REAL_IO_THREAD. 68 // 69 // For some tests it is important to emulate real browser startup. During real 70 // browser startup, the main message loop and the ThreadPool are created before 71 // browser threads. Passing DONT_CREATE_BROWSER_THREADS to the constructor will 72 // delay creating BrowserThreads until the test explicitly calls 73 // CreateBrowserThreads(). 74 // 75 // DONT_CREATE_BROWSER_THREADS should only be used in conjunction with 76 // REAL_IO_THREAD. 77 // 78 // Basic usage: 79 // 80 // class MyTestFixture : public testing::Test { 81 // public: 82 // (...) 83 // 84 // protected: 85 // // Must be the first member (or at least before any member that cares 86 // // about tasks) to be initialized first and destroyed last. protected 87 // // instead of private visibility will allow controlling the task 88 // // environment (e.g. clock --see base::test::TaskEnvironment for 89 // // details). 90 // content::BrowserTaskEnvironment task_environment_; 91 // 92 // // Other members go here (or further below in private section.) 93 // }; 94 // 95 // To add a BrowserTaskEnvironment to a ChromeFooBase test fixture when its 96 // FooBase base class already provides a base::test::TaskEnvironment: 97 // class FooBase { 98 // public: 99 // // Constructs a FooBase with |traits| being forwarded to its 100 // // TaskEnvironment. 101 // template <typename... TaskEnvironmentTraits> 102 // explicit FooBase(TaskEnvironmentTraits&&... traits) 103 // : task_environment_( 104 // base::in_place, 105 // std::forward<TaskEnvironmentTraits>(traits)...) {} 106 // 107 // // Alternatively a subclass may pass this tag to ask this FooBase not to 108 // // instantiate a TaskEnvironment. The subclass is then responsible 109 // // to instantiate one before FooBase::SetUp(). 110 // struct SubclassManagesTaskEnvironment {}; 111 // FooBase(SubclassManagesTaskEnvironment tag); 112 // 113 // protected: 114 // // Use this protected member directly from the test body to drive tasks 115 // // posted within a FooBase-based test. 116 // base::Optional<base::test::TaskEnvironment> task_environment_; 117 // }; 118 // 119 // class ChromeFooBase : public FooBase { 120 // public: 121 // explicit ChromeFooBase(TaskEnvironmentTraits&&... traits) 122 // : FooBase(FooBase::SubclassManagesTaskEnvironment()), 123 // task_environment_( 124 // std::forward<TaskEnvironmentTraits>(traits)...) {} 125 // 126 // protected: 127 // // Use this protected member directly to drive tasks posted within a 128 // // ChromeFooBase-based test. 129 // content::BrowserTaskEnvironment task_environment_; 130 // }; 131 // See views::ViewsTestBase / ChromeViewsTestBase for a real-world example. 132 class BrowserTaskEnvironment : public base::test::TaskEnvironment { 133 public: 134 enum Options { REAL_IO_THREAD }; 135 136 // The main thread will use a MessageLoopForIO (and support the 137 // base::FileDescriptorWatcher API on POSIX). 138 // TODO(alexclarke): Replace IO_MAINLOOP usage by MainThreadType::IO and 139 // remove this. 140 static constexpr MainThreadType IO_MAINLOOP = MainThreadType::IO; 141 142 struct ValidTraits { 143 ValidTraits(TaskEnvironment::ValidTraits); 144 ValidTraits(Options); 145 }; 146 147 // Constructor which accepts zero or more traits to configure the 148 // TaskEnvironment and optionally request a real IO thread. Unlike 149 // TaskEnvironment the default MainThreadType for 150 // BrowserTaskEnvironment is MainThreadType::UI. 151 template < 152 typename... TaskEnvironmentTraits, 153 class CheckArgumentsAreValid = std::enable_if_t< 154 base::trait_helpers::AreValidTraits<ValidTraits, 155 TaskEnvironmentTraits...>::value>> BrowserTaskEnvironment(TaskEnvironmentTraits...traits)156 NOINLINE explicit BrowserTaskEnvironment(TaskEnvironmentTraits... traits) 157 : BrowserTaskEnvironment( 158 base::test::TaskEnvironment( 159 SubclassCreatesDefaultTaskRunner{}, 160 base::trait_helpers::GetEnum<MainThreadType, 161 MainThreadType::UI>(traits...), 162 base::trait_helpers::Exclude<MainThreadType, Options>::Filter( 163 traits)...), 164 UseRealIOThread( 165 base::trait_helpers::GetOptionalEnum<Options>(traits...))) {} 166 167 // Flush the IO thread. Replacement for RunLoop::RunUntilIdle() for tests that 168 // have a REAL_IO_THREAD. As with TaskEnvironment::RunUntilIdle() prefer using 169 // RunLoop+QuitClosure() to await an async condition. 170 void RunIOThreadUntilIdle(); 171 172 ~BrowserTaskEnvironment() override; 173 174 private: 175 // The template constructor has to be in the header but it delegates to this 176 // constructor to initialize all other members out-of-line. 177 BrowserTaskEnvironment(base::test::TaskEnvironment&& scoped_task_environment, 178 bool real_io_thread); 179 180 void Init(); 181 UseRealIOThread(base::Optional<Options> options)182 static constexpr bool UseRealIOThread(base::Optional<Options> options) { 183 if (!options) 184 return false; 185 return *options == Options::REAL_IO_THREAD; 186 } 187 HasIOMainLoop()188 constexpr bool HasIOMainLoop() const { 189 return main_thread_type() == MainThreadType::IO; 190 } 191 192 const bool real_io_thread_; 193 std::unique_ptr<TestBrowserThread> ui_thread_; 194 std::unique_ptr<TestBrowserThread> io_thread_; 195 196 #if defined(OS_WIN) 197 std::unique_ptr<base::win::ScopedCOMInitializer> com_initializer_; 198 #endif 199 200 DISALLOW_COPY_AND_ASSIGN(BrowserTaskEnvironment); 201 }; 202 203 } // namespace content 204 205 #endif // CONTENT_PUBLIC_TEST_BROWSER_TASK_ENVIRONMENT_H_ 206