• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/message_loop/message_pump.h"
6 
7 #include <type_traits>
8 
9 #include "base/functional/bind.h"
10 #include "base/logging.h"
11 #include "base/memory/raw_ptr.h"
12 #include "base/message_loop/message_pump_for_io.h"
13 #include "base/message_loop/message_pump_for_ui.h"
14 #include "base/message_loop/message_pump_type.h"
15 #include "base/run_loop.h"
16 #include "base/task/single_thread_task_executor.h"
17 #include "base/test/bind.h"
18 #include "base/test/test_timeouts.h"
19 #include "base/threading/thread.h"
20 #include "build/build_config.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 
24 #if BUILDFLAG(IS_WIN)
25 #include <windows.h>
26 #endif
27 
28 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)
29 #include "base/message_loop/message_pump_libevent.h"
30 #endif
31 
32 using ::testing::_;
33 using ::testing::AnyNumber;
34 using ::testing::AtMost;
35 using ::testing::Invoke;
36 using ::testing::Return;
37 
38 namespace base {
39 
40 namespace {
41 
42 // On most platforms, the MessagePump impl controls when native work (e.g.
43 // handling input messages) gets its turn. Tests below verify that by expecting
44 // OnBeginWorkItem() calls that cover native work. In some configurations
45 // however, the platform owns the message loop and is the one yielding to
46 // Chrome's MessagePump to DoWork(). Under those configurations, it is not
47 // possible to precisely account for OnBeginWorkItem() calls as they can occur
48 // nondeterministically. For example, on some versions of iOS, the native loop
49 // can surprisingly go through multiple cycles of
50 // kCFRunLoopAfterWaiting=>kCFRunLoopBeforeWaiting before invoking Chrome's
51 // RunWork() for the first time, triggering multiple  ScopedDoWorkItem 's for
52 // potential native work before the first DoWork().
ChromeControlsNativeEventProcessing(MessagePumpType pump_type)53 constexpr bool ChromeControlsNativeEventProcessing(MessagePumpType pump_type) {
54 #if BUILDFLAG(IS_MAC)
55   return pump_type != MessagePumpType::UI;
56 #elif BUILDFLAG(IS_IOS)
57   return false;
58 #else
59   return true;
60 #endif
61 }
62 
63 class MockMessagePumpDelegate : public MessagePump::Delegate {
64  public:
MockMessagePumpDelegate(MessagePumpType pump_type)65   explicit MockMessagePumpDelegate(MessagePumpType pump_type)
66       : check_work_items_(ChromeControlsNativeEventProcessing(pump_type)),
67         native_work_item_accounting_is_on_(
68             !ChromeControlsNativeEventProcessing(pump_type)) {}
69 
~MockMessagePumpDelegate()70   ~MockMessagePumpDelegate() override { ValidateNoOpenWorkItems(); }
71 
72   MockMessagePumpDelegate(const MockMessagePumpDelegate&) = delete;
73   MockMessagePumpDelegate& operator=(const MockMessagePumpDelegate&) = delete;
74 
BeforeWait()75   void BeforeWait() override {}
BeginNativeWorkBeforeDoWork()76   void BeginNativeWorkBeforeDoWork() override {}
77   MOCK_METHOD0(DoWork, MessagePump::Delegate::NextWorkInfo());
78   MOCK_METHOD0(DoIdleWork, bool());
79 
80   // Functions invoked directly by the message pump.
OnBeginWorkItem()81   void OnBeginWorkItem() override {
82     any_work_begun_ = true;
83 
84     if (check_work_items_) {
85       MockOnBeginWorkItem();
86     }
87 
88     ++work_item_count_;
89   }
90 
OnEndWorkItem(int run_level_depth)91   void OnEndWorkItem(int run_level_depth) override {
92     if (check_work_items_) {
93       MockOnEndWorkItem(run_level_depth);
94     }
95 
96     EXPECT_EQ(run_level_depth, work_item_count_);
97 
98     --work_item_count_;
99 
100     // It's not possible to close more scopes than there are open ones.
101     EXPECT_GE(work_item_count_, 0);
102   }
103 
RunDepth()104   int RunDepth() override { return work_item_count_; }
105 
ValidateNoOpenWorkItems()106   void ValidateNoOpenWorkItems() {
107     // Upon exiting there cannot be any open scopes.
108     EXPECT_EQ(work_item_count_, 0);
109 
110     if (native_work_item_accounting_is_on_) {
111 // Tests should trigger work beginning at least once except on iOS where
112 // they need a call to MessagePumpUIApplication::Attach() to do so when on
113 // the UI thread.
114 #if !BUILDFLAG(IS_IOS)
115       EXPECT_TRUE(any_work_begun_);
116 #endif
117     }
118   }
119 
120   // Mock functions for asserting.
121   MOCK_METHOD0(MockOnBeginWorkItem, void(void));
122   MOCK_METHOD1(MockOnEndWorkItem, void(int));
123 
124   // If native events are covered in the current configuration it's not
125   // possible to precisely test all assertions related to work items. This is
126   // because a number of speculative WorkItems are created during execution of
127   // such loops and it's not possible to determine their number before the
128   // execution of the test. In such configurations the functioning of the
129   // message pump is still verified by looking at the counts of opened and
130   // closed WorkItems.
131   const bool check_work_items_;
132   const bool native_work_item_accounting_is_on_;
133 
134   int work_item_count_ = 0;
135   bool any_work_begun_ = false;
136 };
137 
138 class MessagePumpTest : public ::testing::TestWithParam<MessagePumpType> {
139  public:
MessagePumpTest()140   MessagePumpTest() : message_pump_(MessagePump::Create(GetParam())) {}
141 
142  protected:
143 #if defined(USE_GLIB)
144   // Because of a GLIB implementation quirk, the pump doesn't do the same things
145   // between each DoWork. In this case, it won't set/clear a ScopedDoWorkItem
146   // because we run a chrome work item in the runloop outside of GLIB's control,
147   // so we oscillate between setting and not setting PreDoWorkExpectations.
148   std::map<MessagePump::Delegate*, int> do_work_counts;
149 #endif
AddPreDoWorkExpectations(testing::StrictMock<MockMessagePumpDelegate> & delegate)150   void AddPreDoWorkExpectations(
151       testing::StrictMock<MockMessagePumpDelegate>& delegate) {
152 #if BUILDFLAG(IS_WIN)
153     if (GetParam() == MessagePumpType::UI) {
154       // The Windows MessagePumpForUI may do native work from ::PeekMessage()
155       // and labels itself as such.
156       EXPECT_CALL(delegate, MockOnBeginWorkItem);
157       EXPECT_CALL(delegate, MockOnEndWorkItem);
158 
159       // If the above event was MessagePumpForUI's own kMsgHaveWork internal
160       // event, it will process another event to replace it (ref.
161       // ProcessPumpReplacementMessage).
162       EXPECT_CALL(delegate, MockOnBeginWorkItem).Times(AtMost(1));
163       EXPECT_CALL(delegate, MockOnEndWorkItem).Times(AtMost(1));
164     }
165 #endif  // BUILDFLAG(IS_WIN)
166 #if defined(USE_GLIB)
167     do_work_counts.try_emplace(&delegate, 0);
168     if (GetParam() == MessagePumpType::UI) {
169       if (++do_work_counts[&delegate] % 2) {
170         // The GLib MessagePump will do native work before chrome work on
171         // startup.
172         EXPECT_CALL(delegate, MockOnBeginWorkItem);
173         EXPECT_CALL(delegate, MockOnEndWorkItem);
174       }
175     }
176 #endif  // defined(USE_GLIB)
177   }
178 
AddPostDoWorkExpectations(testing::StrictMock<MockMessagePumpDelegate> & delegate)179   void AddPostDoWorkExpectations(
180       testing::StrictMock<MockMessagePumpDelegate>& delegate) {
181 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)
182     // MessagePumpLibEvent checks for native notifications once after processing
183     // a DoWork() but only instantiates a ScopedDoWorkItem that triggers
184     // MessagePumpLibevent::OnLibeventNotification() which this test does not
185     // so there are no post-work expectations at the moment.
186 #endif
187 #if defined(USE_GLIB)
188     if (GetParam() == MessagePumpType::UI) {
189       // The GLib MessagePump can create and destroy work items between DoWorks
190       // depending on internal state.
191       EXPECT_CALL(delegate, MockOnBeginWorkItem).Times(AtMost(1));
192       EXPECT_CALL(delegate, MockOnEndWorkItem).Times(AtMost(1));
193     }
194 #endif  // defined(USE_GLIB)
195   }
196 
197   std::unique_ptr<MessagePump> message_pump_;
198 };
199 
200 }  // namespace
201 
TEST_P(MessagePumpTest,QuitStopsWork)202 TEST_P(MessagePumpTest, QuitStopsWork) {
203   testing::InSequence sequence;
204   testing::StrictMock<MockMessagePumpDelegate> delegate(GetParam());
205 
206   AddPreDoWorkExpectations(delegate);
207 
208   // Not expecting any calls to DoIdleWork after quitting, nor any of the
209   // PostDoWorkExpectations, quitting should be instantaneous.
210   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this] {
211     message_pump_->Quit();
212     return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
213   }));
214 
215   // MessagePumpGlib uses a work item between a HandleDispatch() call and
216   // passing control back to the chrome loop, which handles the Quit() despite
217   // us not necessarily doing any native work during that time.
218 #if defined(USE_GLIB)
219   if (GetParam() == MessagePumpType::UI) {
220     AddPostDoWorkExpectations(delegate);
221   }
222 #endif
223 
224   EXPECT_CALL(delegate, DoIdleWork()).Times(0);
225 
226   message_pump_->ScheduleWork();
227   message_pump_->Run(&delegate);
228 }
229 
TEST_P(MessagePumpTest,QuitStopsWorkWithNestedRunLoop)230 TEST_P(MessagePumpTest, QuitStopsWorkWithNestedRunLoop) {
231   testing::InSequence sequence;
232   testing::StrictMock<MockMessagePumpDelegate> delegate(GetParam());
233   testing::StrictMock<MockMessagePumpDelegate> nested_delegate(GetParam());
234 
235   AddPreDoWorkExpectations(delegate);
236 
237   // We first schedule a call to DoWork, which runs a nested run loop. After
238   // the nested loop exits, we schedule another DoWork which quits the outer
239   // (original) run loop. The test verifies that there are no extra calls to
240   // DoWork after the outer loop quits.
241   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([&] {
242     message_pump_->Run(&nested_delegate);
243     // A null NextWorkInfo indicates immediate follow-up work.
244     return MessagePump::Delegate::NextWorkInfo();
245   }));
246 
247   AddPreDoWorkExpectations(nested_delegate);
248   EXPECT_CALL(nested_delegate, DoWork).WillOnce(Invoke([&] {
249     // Quit the nested run loop.
250     message_pump_->Quit();
251     // The underlying pump should process the next task in the first run-level
252     // regardless of whether the nested run-level indicates there's no more work
253     // (e.g. can happen when the only remaining tasks are non-nestable).
254     return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
255   }));
256 
257   // The `nested_delegate` will quit first.
258   AddPostDoWorkExpectations(nested_delegate);
259 
260   // Return a delayed task with |yield_to_native| set, and exit.
261   AddPostDoWorkExpectations(delegate);
262 
263   AddPreDoWorkExpectations(delegate);
264 
265   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this] {
266     message_pump_->Quit();
267     return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
268   }));
269 
270   message_pump_->ScheduleWork();
271   message_pump_->Run(&delegate);
272 }
273 
TEST_P(MessagePumpTest,YieldToNativeRequestedSmokeTest)274 TEST_P(MessagePumpTest, YieldToNativeRequestedSmokeTest) {
275   // The handling of the "yield_to_native" boolean in the NextWorkInfo is only
276   // implemented on the MessagePumpForUI on android. However since we inject a
277   // fake one for testing this is hard to test. This test ensures that setting
278   // this boolean doesn't cause any MessagePump to explode.
279   testing::StrictMock<MockMessagePumpDelegate> delegate(GetParam());
280 
281   testing::InSequence sequence;
282 
283   // Return an immediate task with |yield_to_native| set.
284   AddPreDoWorkExpectations(delegate);
285   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([] {
286     return MessagePump::Delegate::NextWorkInfo{TimeTicks(), TimeDelta(),
287                                                TimeTicks(),
288                                                /* yield_to_native = */ true};
289   }));
290   AddPostDoWorkExpectations(delegate);
291 
292   AddPreDoWorkExpectations(delegate);
293   // Return a delayed task with |yield_to_native| set, and exit.
294   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this] {
295     message_pump_->Quit();
296     auto now = TimeTicks::Now();
297     return MessagePump::Delegate::NextWorkInfo{now + Milliseconds(1),
298                                                TimeDelta(), now, true};
299   }));
300   EXPECT_CALL(delegate, DoIdleWork()).Times(AnyNumber());
301 
302   message_pump_->ScheduleWork();
303   message_pump_->Run(&delegate);
304 }
305 
TEST_P(MessagePumpTest,LeewaySmokeTest)306 TEST_P(MessagePumpTest, LeewaySmokeTest) {
307   // The handling of the "leeway" in the NextWorkInfo is only implemented on
308   // mac. However since we inject a fake one for testing this is hard to test.
309   // This test ensures that setting this boolean doesn't cause any MessagePump
310   // to explode.
311   testing::StrictMock<MockMessagePumpDelegate> delegate(GetParam());
312 
313   testing::InSequence sequence;
314 
315   AddPreDoWorkExpectations(delegate);
316   // Return a delayed task with |yield_to_native| set, and exit.
317   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this] {
318     message_pump_->Quit();
319     auto now = TimeTicks::Now();
320     return MessagePump::Delegate::NextWorkInfo{now + Milliseconds(1),
321                                                Milliseconds(8), now};
322   }));
323   EXPECT_CALL(delegate, DoIdleWork()).Times(AnyNumber());
324 
325   message_pump_->ScheduleWork();
326   message_pump_->Run(&delegate);
327 }
328 
TEST_P(MessagePumpTest,RunWithoutScheduleWorkInvokesDoWork)329 TEST_P(MessagePumpTest, RunWithoutScheduleWorkInvokesDoWork) {
330   testing::InSequence sequence;
331   testing::StrictMock<MockMessagePumpDelegate> delegate(GetParam());
332 
333   AddPreDoWorkExpectations(delegate);
334 
335   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this] {
336     message_pump_->Quit();
337     return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
338   }));
339 
340   AddPostDoWorkExpectations(delegate);
341 
342 #if BUILDFLAG(IS_IOS)
343   EXPECT_CALL(delegate, DoIdleWork).Times(AnyNumber());
344 #endif
345 
346   message_pump_->Run(&delegate);
347 }
348 
TEST_P(MessagePumpTest,NestedRunWithoutScheduleWorkInvokesDoWork)349 TEST_P(MessagePumpTest, NestedRunWithoutScheduleWorkInvokesDoWork) {
350   testing::InSequence sequence;
351   testing::StrictMock<MockMessagePumpDelegate> delegate(GetParam());
352   testing::StrictMock<MockMessagePumpDelegate> nested_delegate(GetParam());
353 
354   AddPreDoWorkExpectations(delegate);
355 
356   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this, &nested_delegate] {
357     message_pump_->Run(&nested_delegate);
358     message_pump_->Quit();
359     return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
360   }));
361 
362   AddPreDoWorkExpectations(nested_delegate);
363 
364   EXPECT_CALL(nested_delegate, DoWork).WillOnce(Invoke([this] {
365     message_pump_->Quit();
366     return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
367   }));
368 
369   // We quit `nested_delegate` before `delegate`
370   AddPostDoWorkExpectations(nested_delegate);
371 
372   AddPostDoWorkExpectations(delegate);
373 
374 #if BUILDFLAG(IS_IOS)
375   EXPECT_CALL(nested_delegate, DoIdleWork).Times(AnyNumber());
376   EXPECT_CALL(delegate, DoIdleWork).Times(AnyNumber());
377 #endif
378 
379   message_pump_->Run(&delegate);
380 }
381 
382 INSTANTIATE_TEST_SUITE_P(All,
383                          MessagePumpTest,
384                          ::testing::Values(MessagePumpType::DEFAULT,
385                                            MessagePumpType::UI,
386                                            MessagePumpType::IO));
387 
388 }  // namespace base
389