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