[email protected] | ec04d3f | 2013-06-06 21:31:39 | [diff] [blame] | 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 | |
Francois Doray | 9f68b1a | 2017-12-25 13:41:58 | [diff] [blame] | 5 | // TestBrowserThreadBundle is a convenience class which allows usage of these |
| 6 | // APIs within its scope: |
| 7 | // - Same APIs as base::test::ScopedTaskEnvironment. |
| 8 | // - content::BrowserThread. |
[email protected] | ec04d3f | 2013-06-06 21:31:39 | [diff] [blame] | 9 | // |
Francois Doray | 9f68b1a | 2017-12-25 13:41:58 | [diff] [blame] | 10 | // Only tests that need the BrowserThread API should instantiate a |
| 11 | // TestBrowserThreadBundle. Use base::test::ScopedTaskEnvironment otherwise. |
| 12 | // |
| 13 | // By default, BrowserThread::UI/IO are backed by a single shared MessageLoop on |
| 14 | // the main thread. If a test truly needs BrowserThread::IO tasks to run on a |
| 15 | // separate thread, it can pass the REAL_IO_THREAD option to the constructor. |
Gabriel Charette | 52fa3ae | 2019-04-15 21:44:37 | [diff] [blame] | 16 | // ThreadPool tasks always run on dedicated threads. |
[email protected] | ec04d3f | 2013-06-06 21:31:39 | [diff] [blame] | 17 | // |
fdoray | 575c4b2 | 2017-04-05 19:25:51 | [diff] [blame] | 18 | // To synchronously run tasks from the shared MessageLoop: |
| 19 | // |
| 20 | // ... until there are no undelayed tasks in the shared MessageLoop: |
| 21 | // base::RunLoop::RunUntilIdle(); |
| 22 | // |
| 23 | // ... until there are no undelayed tasks in the shared MessageLoop, in |
Gabriel Charette | 52fa3ae | 2019-04-15 21:44:37 | [diff] [blame] | 24 | // ThreadPool (excluding tasks not posted from the shared MessageLoop's |
| 25 | // thread or ThreadPool): |
Gabriel Charette | 01507a2 | 2017-09-27 21:30:08 | [diff] [blame] | 26 | // content::RunAllTasksUntilIdle(); |
fdoray | 575c4b2 | 2017-04-05 19:25:51 | [diff] [blame] | 27 | // |
| 28 | // ... until a condition is met: |
| 29 | // base::RunLoop run_loop; |
| 30 | // // Runs until a task running in the shared MessageLoop calls |
| 31 | // // run_loop.Quit() or runs run_loop.QuitClosure() (&run_loop or |
| 32 | // // run_loop.QuitClosure() must be kept somewhere accessible by that task). |
| 33 | // run_loop.Run(); |
| 34 | // |
Gabriel Charette | 52fa3ae | 2019-04-15 21:44:37 | [diff] [blame] | 35 | // To wait until there are no pending undelayed tasks in ThreadPool, without |
Gabriel Charette | a1e0a37 | 2017-09-14 18:48:21 | [diff] [blame] | 36 | // running tasks from the shared MessageLoop: |
Gabriel Charette | 43fd370 | 2019-05-29 16:36:51 | [diff] [blame^] | 37 | // base::ThreadPoolInstance::Get()->FlushForTesting(); |
fdoray | 575c4b2 | 2017-04-05 19:25:51 | [diff] [blame] | 38 | // |
Francois Doray | 9f68b1a | 2017-12-25 13:41:58 | [diff] [blame] | 39 | // The destructor of TestBrowserThreadBundle runs remaining UI/IO tasks and |
Gabriel Charette | 52fa3ae | 2019-04-15 21:44:37 | [diff] [blame] | 40 | // remaining thread pool tasks. |
[email protected] | ec04d3f | 2013-06-06 21:31:39 | [diff] [blame] | 41 | // |
Francois Doray | 9f68b1a | 2017-12-25 13:41:58 | [diff] [blame] | 42 | // If a test needs to pump IO messages on the main thread, it should use the |
fdoray | 575c4b2 | 2017-04-05 19:25:51 | [diff] [blame] | 43 | // IO_MAINLOOP option. Most of the time, IO_MAINLOOP avoids needing to use a |
fdoray | ad58f83 | 2016-12-19 18:06:46 | [diff] [blame] | 44 | // REAL_IO_THREAD. |
aberent | 8f1ce40 | 2016-01-13 12:01:38 | [diff] [blame] | 45 | // |
| 46 | // For some tests it is important to emulate real browser startup. During real |
Gabriel Charette | 52fa3ae | 2019-04-15 21:44:37 | [diff] [blame] | 47 | // browser startup, the main MessageLoop and the ThreadPool are created |
Francois Doray | 69f889f | 2017-08-02 15:42:12 | [diff] [blame] | 48 | // before browser threads. Passing DONT_CREATE_BROWSER_THREADS to constructor |
| 49 | // will delay creating browser threads until the test explicitly calls |
| 50 | // CreateBrowserThreads(). |
aberent | 8f1ce40 | 2016-01-13 12:01:38 | [diff] [blame] | 51 | // |
Francois Doray | 69f889f | 2017-08-02 15:42:12 | [diff] [blame] | 52 | // DONT_CREATE_BROWSER_THREADS should only be used when the options specify at |
| 53 | // least one real thread other than the main thread. |
gab | 1f58e95 | 2017-05-04 23:28:51 | [diff] [blame] | 54 | // |
fdoray | 8c58052 | 2017-05-08 16:59:41 | [diff] [blame] | 55 | // TestBrowserThreadBundle may be instantiated in a scope where there is already |
| 56 | // a base::test::ScopedTaskEnvironment. In that case, it will use the |
Gabriel Charette | 52fa3ae | 2019-04-15 21:44:37 | [diff] [blame] | 57 | // MessageLoop and the ThreadPool provided by this |
fdoray | 8c58052 | 2017-05-08 16:59:41 | [diff] [blame] | 58 | // base::test::ScopedTaskEnvironment instead of creating its own. The ability to |
| 59 | // have a base::test::ScopedTaskEnvironment and a TestBrowserThreadBundle in the |
| 60 | // same scope is useful when a fixture that inherits from a fixture that |
| 61 | // provides a base::test::ScopedTaskEnvironment needs to add support for browser |
| 62 | // threads. |
| 63 | // |
gab | 1f58e95 | 2017-05-04 23:28:51 | [diff] [blame] | 64 | // Basic usage: |
| 65 | // |
| 66 | // class MyTestFixture : public testing::Test { |
| 67 | // public: |
| 68 | // (...) |
| 69 | // |
| 70 | // protected: |
| 71 | // // Must be the first member (or at least before any member that cares |
| 72 | // // about tasks) to be initialized first and destroyed last. protected |
| 73 | // // instead of private visibility will allow controlling the task |
| 74 | // // environment (e.g. clock) once such features are added (see |
| 75 | // // base::test::ScopedTaskEnvironment for details), until then it at least |
| 76 | // // doesn't hurt :). |
| 77 | // content::TestBrowserThreadBundle test_browser_thread_bundle_; |
| 78 | // |
| 79 | // // Other members go here (or further below in private section.) |
| 80 | // }; |
[email protected] | ec04d3f | 2013-06-06 21:31:39 | [diff] [blame] | 81 | |
sdefresne | 634eec9 | 2014-12-18 13:02:54 | [diff] [blame] | 82 | #ifndef CONTENT_PUBLIC_TEST_TEST_BROWSER_THREAD_BUNDLE_H_ |
| 83 | #define CONTENT_PUBLIC_TEST_TEST_BROWSER_THREAD_BUNDLE_H_ |
[email protected] | ec04d3f | 2013-06-06 21:31:39 | [diff] [blame] | 84 | |
dcheng | 6003e0b | 2016-04-09 18:42:34 | [diff] [blame] | 85 | #include <memory> |
| 86 | |
Alex Clarke | f0527a9b | 2019-01-23 10:43:25 | [diff] [blame] | 87 | #include "base/compiler_specific.h" |
avi | 652869c | 2015-12-25 01:48:45 | [diff] [blame] | 88 | #include "base/macros.h" |
Alex Clarke | 1052bb0 | 2018-12-14 09:44:16 | [diff] [blame] | 89 | #include "base/test/scoped_task_environment.h" |
Robert Liao | 0bde45e | 2017-06-22 21:16:03 | [diff] [blame] | 90 | #include "build/build_config.h" |
[email protected] | ec04d3f | 2013-06-06 21:31:39 | [diff] [blame] | 91 | |
| 92 | namespace base { |
Robert Liao | 0bde45e | 2017-06-22 21:16:03 | [diff] [blame] | 93 | #if defined(OS_WIN) |
| 94 | namespace win { |
| 95 | class ScopedCOMInitializer; |
| 96 | } // namespace win |
| 97 | #endif |
[email protected] | ec04d3f | 2013-06-06 21:31:39 | [diff] [blame] | 98 | } // namespace base |
| 99 | |
| 100 | namespace content { |
| 101 | |
| 102 | class TestBrowserThread; |
| 103 | |
Alex Clarke | f0527a9b | 2019-01-23 10:43:25 | [diff] [blame] | 104 | // Note: to drive these threads (e.g. run all tasks until idle or FastForwardBy) |
| 105 | // see base::test::ScopedTaskEnvironment which this is a subclass of. |
Alex Clarke | 1052bb0 | 2018-12-14 09:44:16 | [diff] [blame] | 106 | class TestBrowserThreadBundle : public base::test::ScopedTaskEnvironment { |
[email protected] | ec04d3f | 2013-06-06 21:31:39 | [diff] [blame] | 107 | public: |
Alex Clarke | f0527a9b | 2019-01-23 10:43:25 | [diff] [blame] | 108 | enum Options { REAL_IO_THREAD }; |
| 109 | |
| 110 | // The main thread will use a MessageLoopForIO (and support the |
| 111 | // base::FileDescriptorWatcher API on POSIX). |
| 112 | // TODO(alexclarke): Replace IO_MAINLOOP usage by MainThreadType::IO and |
| 113 | // remove this. |
| 114 | static constexpr MainThreadType IO_MAINLOOP = MainThreadType::IO; |
| 115 | |
| 116 | struct ValidTraits { |
| 117 | ValidTraits(ScopedTaskEnvironment::ValidTrait); |
| 118 | ValidTraits(Options); |
[email protected] | ec04d3f | 2013-06-06 21:31:39 | [diff] [blame] | 119 | }; |
| 120 | |
Alex Clarke | f0527a9b | 2019-01-23 10:43:25 | [diff] [blame] | 121 | // Constructor which accepts zero or more traits to configure the |
| 122 | // ScopedTaskEnvironment and optionally request a real IO thread. Unlike |
| 123 | // ScopedTaskEnvironment the default MainThreadType for |
| 124 | // TestBrowserThreadBundle is MainThreadType::UI. |
| 125 | template < |
| 126 | class... ArgTypes, |
| 127 | class CheckArgumentsAreValid = std::enable_if_t< |
| 128 | base::trait_helpers::AreValidTraits<ValidTraits, ArgTypes...>::value>> |
| 129 | NOINLINE TestBrowserThreadBundle(const ArgTypes... args) |
| 130 | : TestBrowserThreadBundle( |
| 131 | base::test::ScopedTaskEnvironment( |
Alex Clarke | 831ed1e6 | 2019-02-18 21:10:08 | [diff] [blame] | 132 | SubclassCreatesDefaultTaskRunner{}, |
Alex Clarke | f0527a9b | 2019-01-23 10:43:25 | [diff] [blame] | 133 | base::trait_helpers::GetEnum<MainThreadType, |
| 134 | MainThreadType::UI>(args...), |
| 135 | base::trait_helpers::Exclude<MainThreadType, Options>::Filter( |
| 136 | args)...), |
| 137 | UseRealIOThread( |
| 138 | base::trait_helpers::GetOptionalEnum<Options>(args...))) {} |
Alex Clarke | 1052bb0 | 2018-12-14 09:44:16 | [diff] [blame] | 139 | |
Gabriel Charette | 52fa3ae | 2019-04-15 21:44:37 | [diff] [blame] | 140 | // Runs all tasks posted to ThreadPool and main thread until idle. |
Gabriel Charette | 147335ea | 2018-03-22 15:59:19 | [diff] [blame] | 141 | // Note: At the moment, this will not process BrowserThread::IO if this |
| 142 | // TestBrowserThreadBundle is using a REAL_IO_THREAD. |
Gabriel Charette | 52fa3ae | 2019-04-15 21:44:37 | [diff] [blame] | 143 | // TODO(robliao): fix this by making ThreadPool aware of all MessageLoops. |
Gabriel Charette | 147335ea | 2018-03-22 15:59:19 | [diff] [blame] | 144 | // |
| 145 | // Note that this is not the cleanest way to run until idle as it will return |
| 146 | // early if it depends on an async condition that isn't guaranteed to have |
| 147 | // occurred yet. The best way to run until a condition is met is with RunLoop: |
| 148 | // |
| 149 | // void KickoffAsyncFoo(base::OnceClosure on_done); |
| 150 | // |
| 151 | // base::RunLoop run_loop; |
| 152 | // KickoffAsyncFoo(run_loop.QuitClosure()); |
| 153 | // run_loop.Run(); |
| 154 | // |
Gabriel Charette | d4c59d0 | 2018-03-21 20:12:26 | [diff] [blame] | 155 | |
Gabriel Charette | 147335ea | 2018-03-22 15:59:19 | [diff] [blame] | 156 | // Flush the IO thread. Replacement for RunLoop::RunUntilIdle() for tests that |
| 157 | // have a REAL_IO_THREAD. As with RunUntilIdle() above, prefer using |
| 158 | // RunLoop+QuitClosure() to await an async condition. |
| 159 | void RunIOThreadUntilIdle(); |
| 160 | |
Alex Clarke | 1052bb0 | 2018-12-14 09:44:16 | [diff] [blame] | 161 | ~TestBrowserThreadBundle() override; |
[email protected] | ec04d3f | 2013-06-06 21:31:39 | [diff] [blame] | 162 | |
| 163 | private: |
Alex Clarke | f0527a9b | 2019-01-23 10:43:25 | [diff] [blame] | 164 | // The template constructor has to be in the header but it delegates to this |
| 165 | // constructor to initialize all other members out-of-line. |
| 166 | TestBrowserThreadBundle( |
| 167 | base::test::ScopedTaskEnvironment&& scoped_task_environment, |
| 168 | bool real_io_thread); |
| 169 | |
aberent | 8f1ce40 | 2016-01-13 12:01:38 | [diff] [blame] | 170 | void Init(); |
[email protected] | ec04d3f | 2013-06-06 21:31:39 | [diff] [blame] | 171 | |
Alex Clarke | f0527a9b | 2019-01-23 10:43:25 | [diff] [blame] | 172 | static constexpr bool UseRealIOThread(base::Optional<Options> options) { |
| 173 | if (!options) |
| 174 | return false; |
| 175 | return *options == Options::REAL_IO_THREAD; |
| 176 | } |
| 177 | |
| 178 | constexpr bool HasIOMainLoop() const { |
| 179 | return main_thread_type() == MainThreadType::IO || |
| 180 | main_thread_type() == MainThreadType::IO_MOCK_TIME; |
| 181 | } |
| 182 | |
| 183 | const bool real_io_thread_; |
dcheng | 6003e0b | 2016-04-09 18:42:34 | [diff] [blame] | 184 | std::unique_ptr<TestBrowserThread> ui_thread_; |
dcheng | 6003e0b | 2016-04-09 18:42:34 | [diff] [blame] | 185 | std::unique_ptr<TestBrowserThread> io_thread_; |
[email protected] | ec04d3f | 2013-06-06 21:31:39 | [diff] [blame] | 186 | |
Robert Liao | 0bde45e | 2017-06-22 21:16:03 | [diff] [blame] | 187 | #if defined(OS_WIN) |
| 188 | std::unique_ptr<base::win::ScopedCOMInitializer> com_initializer_; |
| 189 | #endif |
| 190 | |
[email protected] | ec04d3f | 2013-06-06 21:31:39 | [diff] [blame] | 191 | DISALLOW_COPY_AND_ASSIGN(TestBrowserThreadBundle); |
| 192 | }; |
| 193 | |
| 194 | } // namespace content |
| 195 | |
sdefresne | 634eec9 | 2014-12-18 13:02:54 | [diff] [blame] | 196 | #endif // CONTENT_PUBLIC_TEST_TEST_BROWSER_THREAD_BUNDLE_H_ |