OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <vector> | 5 #include <vector> |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
9 #include "base/compiler_specific.h" | 9 #include "base/compiler_specific.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/memory/ref_counted.h" | 11 #include "base/memory/ref_counted.h" |
12 #include "base/message_loop/message_loop.h" | 12 #include "base/message_loop/message_loop.h" |
13 #include "base/message_loop/message_loop_proxy_impl.h" | 13 #include "base/message_loop/message_loop_proxy_impl.h" |
14 #include "base/message_loop/message_loop_test.h" | 14 #include "base/message_loop/message_loop_test.h" |
15 #include "base/pending_task.h" | 15 #include "base/pending_task.h" |
16 #include "base/posix/eintr_wrapper.h" | 16 #include "base/posix/eintr_wrapper.h" |
17 #include "base/run_loop.h" | 17 #include "base/run_loop.h" |
18 #include "base/synchronization/waitable_event.h" | 18 #include "base/synchronization/waitable_event.h" |
19 #include "base/thread_task_runner_handle.h" | 19 #include "base/thread_task_runner_handle.h" |
20 #include "base/threading/platform_thread.h" | 20 #include "base/threading/platform_thread.h" |
21 #include "base/threading/thread.h" | 21 #include "base/threading/thread.h" |
22 #include "testing/gtest/include/gtest/gtest.h" | 22 #include "testing/gtest/include/gtest/gtest.h" |
23 | 23 |
24 #if defined(OS_WIN) | |
25 #include "base/message_loop/message_pump_dispatcher.h" | |
26 #include "base/message_loop/message_pump_win.h" | |
27 #include "base/process/memory.h" | |
28 #include "base/strings/string16.h" | |
29 #include "base/win/scoped_handle.h" | |
30 #endif | |
31 | |
32 namespace base { | 24 namespace base { |
33 | 25 |
34 // TODO(darin): Platform-specific MessageLoop tests should be grouped together | 26 // TODO(darin): Platform-specific MessageLoop tests should be grouped together |
35 // to avoid chopping this file up with so many #ifdefs. | 27 // to avoid chopping this file up with so many #ifdefs. |
36 | 28 |
37 namespace { | 29 namespace { |
38 | 30 |
39 scoped_ptr<MessagePump> TypeDefaultMessagePumpFactory() { | 31 scoped_ptr<MessagePump> TypeDefaultMessagePumpFactory() { |
40 return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_DEFAULT); | 32 return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_DEFAULT); |
41 } | 33 } |
(...skipping 21 matching lines...) Expand all Loading... |
63 | 55 |
64 private: | 56 private: |
65 friend class RefCounted<Foo>; | 57 friend class RefCounted<Foo>; |
66 | 58 |
67 ~Foo() {} | 59 ~Foo() {} |
68 | 60 |
69 int test_count_; | 61 int test_count_; |
70 std::string result_; | 62 std::string result_; |
71 }; | 63 }; |
72 | 64 |
73 #if defined(OS_WIN) | |
74 | |
75 // This function runs slowly to simulate a large amount of work being done. | |
76 static void SlowFunc(TimeDelta pause, int* quit_counter) { | |
77 PlatformThread::Sleep(pause); | |
78 if (--(*quit_counter) == 0) | |
79 MessageLoop::current()->QuitWhenIdle(); | |
80 } | |
81 | |
82 // This function records the time when Run was called in a Time object, which is | |
83 // useful for building a variety of MessageLoop tests. | |
84 static void RecordRunTimeFunc(Time* run_time, int* quit_counter) { | |
85 *run_time = Time::Now(); | |
86 | |
87 // Cause our Run function to take some time to execute. As a result we can | |
88 // count on subsequent RecordRunTimeFunc()s running at a future time, | |
89 // without worry about the resolution of our system clock being an issue. | |
90 SlowFunc(TimeDelta::FromMilliseconds(10), quit_counter); | |
91 } | |
92 | |
93 void SubPumpFunc() { | |
94 MessageLoop::current()->SetNestableTasksAllowed(true); | |
95 MSG msg; | |
96 while (GetMessage(&msg, NULL, 0, 0)) { | |
97 TranslateMessage(&msg); | |
98 DispatchMessage(&msg); | |
99 } | |
100 MessageLoop::current()->QuitWhenIdle(); | |
101 } | |
102 | |
103 void RunTest_PostDelayedTask_SharedTimer_SubPump() { | |
104 MessageLoop loop(MessageLoop::TYPE_UI); | |
105 | |
106 // Test that the interval of the timer, used to run the next delayed task, is | |
107 // set to a value corresponding to when the next delayed task should run. | |
108 | |
109 // By setting num_tasks to 1, we ensure that the first task to run causes the | |
110 // run loop to exit. | |
111 int num_tasks = 1; | |
112 Time run_time; | |
113 | |
114 loop.PostTask(FROM_HERE, Bind(&SubPumpFunc)); | |
115 | |
116 // This very delayed task should never run. | |
117 loop.PostDelayedTask( | |
118 FROM_HERE, | |
119 Bind(&RecordRunTimeFunc, &run_time, &num_tasks), | |
120 TimeDelta::FromSeconds(1000)); | |
121 | |
122 // This slightly delayed task should run from within SubPumpFunc). | |
123 loop.PostDelayedTask( | |
124 FROM_HERE, | |
125 Bind(&PostQuitMessage, 0), | |
126 TimeDelta::FromMilliseconds(10)); | |
127 | |
128 Time start_time = Time::Now(); | |
129 | |
130 loop.Run(); | |
131 EXPECT_EQ(1, num_tasks); | |
132 | |
133 // Ensure that we ran in far less time than the slower timer. | |
134 TimeDelta total_time = Time::Now() - start_time; | |
135 EXPECT_GT(5000, total_time.InMilliseconds()); | |
136 | |
137 // In case both timers somehow run at nearly the same time, sleep a little | |
138 // and then run all pending to force them both to have run. This is just | |
139 // encouraging flakiness if there is any. | |
140 PlatformThread::Sleep(TimeDelta::FromMilliseconds(100)); | |
141 RunLoop().RunUntilIdle(); | |
142 | |
143 EXPECT_TRUE(run_time.is_null()); | |
144 } | |
145 | |
146 const wchar_t kMessageBoxTitle[] = L"MessageLoop Unit Test"; | |
147 | |
148 enum TaskType { | |
149 MESSAGEBOX, | |
150 ENDDIALOG, | |
151 RECURSIVE, | |
152 TIMEDMESSAGELOOP, | |
153 QUITMESSAGELOOP, | |
154 ORDERED, | |
155 PUMPS, | |
156 SLEEP, | |
157 RUNS, | |
158 }; | |
159 | |
160 // Saves the order in which the tasks executed. | |
161 struct TaskItem { | |
162 TaskItem(TaskType t, int c, bool s) | |
163 : type(t), | |
164 cookie(c), | |
165 start(s) { | |
166 } | |
167 | |
168 TaskType type; | |
169 int cookie; | |
170 bool start; | |
171 | |
172 bool operator == (const TaskItem& other) const { | |
173 return type == other.type && cookie == other.cookie && start == other.start; | |
174 } | |
175 }; | |
176 | |
177 std::ostream& operator <<(std::ostream& os, TaskType type) { | |
178 switch (type) { | |
179 case MESSAGEBOX: os << "MESSAGEBOX"; break; | |
180 case ENDDIALOG: os << "ENDDIALOG"; break; | |
181 case RECURSIVE: os << "RECURSIVE"; break; | |
182 case TIMEDMESSAGELOOP: os << "TIMEDMESSAGELOOP"; break; | |
183 case QUITMESSAGELOOP: os << "QUITMESSAGELOOP"; break; | |
184 case ORDERED: os << "ORDERED"; break; | |
185 case PUMPS: os << "PUMPS"; break; | |
186 case SLEEP: os << "SLEEP"; break; | |
187 default: | |
188 NOTREACHED(); | |
189 os << "Unknown TaskType"; | |
190 break; | |
191 } | |
192 return os; | |
193 } | |
194 | |
195 std::ostream& operator <<(std::ostream& os, const TaskItem& item) { | |
196 if (item.start) | |
197 return os << item.type << " " << item.cookie << " starts"; | |
198 else | |
199 return os << item.type << " " << item.cookie << " ends"; | |
200 } | |
201 | |
202 class TaskList { | |
203 public: | |
204 void RecordStart(TaskType type, int cookie) { | |
205 TaskItem item(type, cookie, true); | |
206 DVLOG(1) << item; | |
207 task_list_.push_back(item); | |
208 } | |
209 | |
210 void RecordEnd(TaskType type, int cookie) { | |
211 TaskItem item(type, cookie, false); | |
212 DVLOG(1) << item; | |
213 task_list_.push_back(item); | |
214 } | |
215 | |
216 size_t Size() { | |
217 return task_list_.size(); | |
218 } | |
219 | |
220 TaskItem Get(int n) { | |
221 return task_list_[n]; | |
222 } | |
223 | |
224 private: | |
225 std::vector<TaskItem> task_list_; | |
226 }; | |
227 | |
228 // MessageLoop implicitly start a "modal message loop". Modal dialog boxes, | |
229 // common controls (like OpenFile) and StartDoc printing function can cause | |
230 // implicit message loops. | |
231 void MessageBoxFunc(TaskList* order, int cookie, bool is_reentrant) { | |
232 order->RecordStart(MESSAGEBOX, cookie); | |
233 if (is_reentrant) | |
234 MessageLoop::current()->SetNestableTasksAllowed(true); | |
235 MessageBox(NULL, L"Please wait...", kMessageBoxTitle, MB_OK); | |
236 order->RecordEnd(MESSAGEBOX, cookie); | |
237 } | |
238 | |
239 // Will end the MessageBox. | |
240 void EndDialogFunc(TaskList* order, int cookie) { | |
241 order->RecordStart(ENDDIALOG, cookie); | |
242 HWND window = GetActiveWindow(); | |
243 if (window != NULL) { | |
244 EXPECT_NE(EndDialog(window, IDCONTINUE), 0); | |
245 // Cheap way to signal that the window wasn't found if RunEnd() isn't | |
246 // called. | |
247 order->RecordEnd(ENDDIALOG, cookie); | |
248 } | |
249 } | |
250 | |
251 void RecursiveFunc(TaskList* order, int cookie, int depth, | |
252 bool is_reentrant) { | |
253 order->RecordStart(RECURSIVE, cookie); | |
254 if (depth > 0) { | |
255 if (is_reentrant) | |
256 MessageLoop::current()->SetNestableTasksAllowed(true); | |
257 MessageLoop::current()->PostTask( | |
258 FROM_HERE, | |
259 Bind(&RecursiveFunc, order, cookie, depth - 1, is_reentrant)); | |
260 } | |
261 order->RecordEnd(RECURSIVE, cookie); | |
262 } | |
263 | |
264 void QuitFunc(TaskList* order, int cookie) { | |
265 order->RecordStart(QUITMESSAGELOOP, cookie); | |
266 MessageLoop::current()->QuitWhenIdle(); | |
267 order->RecordEnd(QUITMESSAGELOOP, cookie); | |
268 } | |
269 | |
270 void RecursiveFuncWin(MessageLoop* target, | |
271 HANDLE event, | |
272 bool expect_window, | |
273 TaskList* order, | |
274 bool is_reentrant) { | |
275 target->PostTask(FROM_HERE, | |
276 Bind(&RecursiveFunc, order, 1, 2, is_reentrant)); | |
277 target->PostTask(FROM_HERE, | |
278 Bind(&MessageBoxFunc, order, 2, is_reentrant)); | |
279 target->PostTask(FROM_HERE, | |
280 Bind(&RecursiveFunc, order, 3, 2, is_reentrant)); | |
281 // The trick here is that for recursive task processing, this task will be | |
282 // ran _inside_ the MessageBox message loop, dismissing the MessageBox | |
283 // without a chance. | |
284 // For non-recursive task processing, this will be executed _after_ the | |
285 // MessageBox will have been dismissed by the code below, where | |
286 // expect_window_ is true. | |
287 target->PostTask(FROM_HERE, | |
288 Bind(&EndDialogFunc, order, 4)); | |
289 target->PostTask(FROM_HERE, | |
290 Bind(&QuitFunc, order, 5)); | |
291 | |
292 // Enforce that every tasks are sent before starting to run the main thread | |
293 // message loop. | |
294 ASSERT_TRUE(SetEvent(event)); | |
295 | |
296 // Poll for the MessageBox. Don't do this at home! At the speed we do it, | |
297 // you will never realize one MessageBox was shown. | |
298 for (; expect_window;) { | |
299 HWND window = FindWindow(L"#32770", kMessageBoxTitle); | |
300 if (window) { | |
301 // Dismiss it. | |
302 for (;;) { | |
303 HWND button = FindWindowEx(window, NULL, L"Button", NULL); | |
304 if (button != NULL) { | |
305 EXPECT_EQ(0, SendMessage(button, WM_LBUTTONDOWN, 0, 0)); | |
306 EXPECT_EQ(0, SendMessage(button, WM_LBUTTONUP, 0, 0)); | |
307 break; | |
308 } | |
309 } | |
310 break; | |
311 } | |
312 } | |
313 } | |
314 | |
315 // TODO(darin): These tests need to be ported since they test critical | |
316 // message loop functionality. | |
317 | |
318 // A side effect of this test is the generation a beep. Sorry. | |
319 void RunTest_RecursiveDenial2(MessageLoop::Type message_loop_type) { | |
320 MessageLoop loop(message_loop_type); | |
321 | |
322 Thread worker("RecursiveDenial2_worker"); | |
323 Thread::Options options; | |
324 options.message_loop_type = message_loop_type; | |
325 ASSERT_EQ(true, worker.StartWithOptions(options)); | |
326 TaskList order; | |
327 win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL)); | |
328 worker.message_loop()->PostTask(FROM_HERE, | |
329 Bind(&RecursiveFuncWin, | |
330 MessageLoop::current(), | |
331 event.Get(), | |
332 true, | |
333 &order, | |
334 false)); | |
335 // Let the other thread execute. | |
336 WaitForSingleObject(event.Get(), INFINITE); | |
337 MessageLoop::current()->Run(); | |
338 | |
339 ASSERT_EQ(order.Size(), 17); | |
340 EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); | |
341 EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); | |
342 EXPECT_EQ(order.Get(2), TaskItem(MESSAGEBOX, 2, true)); | |
343 EXPECT_EQ(order.Get(3), TaskItem(MESSAGEBOX, 2, false)); | |
344 EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 3, true)); | |
345 EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 3, false)); | |
346 // When EndDialogFunc is processed, the window is already dismissed, hence no | |
347 // "end" entry. | |
348 EXPECT_EQ(order.Get(6), TaskItem(ENDDIALOG, 4, true)); | |
349 EXPECT_EQ(order.Get(7), TaskItem(QUITMESSAGELOOP, 5, true)); | |
350 EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 5, false)); | |
351 EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 1, true)); | |
352 EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, false)); | |
353 EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 3, true)); | |
354 EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 3, false)); | |
355 EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, true)); | |
356 EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 1, false)); | |
357 EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 3, true)); | |
358 EXPECT_EQ(order.Get(16), TaskItem(RECURSIVE, 3, false)); | |
359 } | |
360 | |
361 // A side effect of this test is the generation a beep. Sorry. This test also | |
362 // needs to process windows messages on the current thread. | |
363 void RunTest_RecursiveSupport2(MessageLoop::Type message_loop_type) { | |
364 MessageLoop loop(message_loop_type); | |
365 | |
366 Thread worker("RecursiveSupport2_worker"); | |
367 Thread::Options options; | |
368 options.message_loop_type = message_loop_type; | |
369 ASSERT_EQ(true, worker.StartWithOptions(options)); | |
370 TaskList order; | |
371 win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL)); | |
372 worker.message_loop()->PostTask(FROM_HERE, | |
373 Bind(&RecursiveFuncWin, | |
374 MessageLoop::current(), | |
375 event.Get(), | |
376 false, | |
377 &order, | |
378 true)); | |
379 // Let the other thread execute. | |
380 WaitForSingleObject(event.Get(), INFINITE); | |
381 MessageLoop::current()->Run(); | |
382 | |
383 ASSERT_EQ(order.Size(), 18); | |
384 EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); | |
385 EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); | |
386 EXPECT_EQ(order.Get(2), TaskItem(MESSAGEBOX, 2, true)); | |
387 // Note that this executes in the MessageBox modal loop. | |
388 EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 3, true)); | |
389 EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 3, false)); | |
390 EXPECT_EQ(order.Get(5), TaskItem(ENDDIALOG, 4, true)); | |
391 EXPECT_EQ(order.Get(6), TaskItem(ENDDIALOG, 4, false)); | |
392 EXPECT_EQ(order.Get(7), TaskItem(MESSAGEBOX, 2, false)); | |
393 /* The order can subtly change here. The reason is that when RecursiveFunc(1) | |
394 is called in the main thread, if it is faster than getting to the | |
395 PostTask(FROM_HERE, Bind(&QuitFunc) execution, the order of task | |
396 execution can change. We don't care anyway that the order isn't correct. | |
397 EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 5, true)); | |
398 EXPECT_EQ(order.Get(9), TaskItem(QUITMESSAGELOOP, 5, false)); | |
399 EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true)); | |
400 EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false)); | |
401 */ | |
402 EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 3, true)); | |
403 EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 3, false)); | |
404 EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 1, true)); | |
405 EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 1, false)); | |
406 EXPECT_EQ(order.Get(16), TaskItem(RECURSIVE, 3, true)); | |
407 EXPECT_EQ(order.Get(17), TaskItem(RECURSIVE, 3, false)); | |
408 } | |
409 | |
410 #endif // defined(OS_WIN) | |
411 | |
412 void PostNTasksThenQuit(int posts_remaining) { | 65 void PostNTasksThenQuit(int posts_remaining) { |
413 if (posts_remaining > 1) { | 66 if (posts_remaining > 1) { |
414 MessageLoop::current()->PostTask( | 67 MessageLoop::current()->PostTask( |
415 FROM_HERE, | 68 FROM_HERE, |
416 Bind(&PostNTasksThenQuit, posts_remaining - 1)); | 69 Bind(&PostNTasksThenQuit, posts_remaining - 1)); |
417 } else { | 70 } else { |
418 MessageLoop::current()->QuitWhenIdle(); | 71 MessageLoop::current()->QuitWhenIdle(); |
419 } | 72 } |
420 } | 73 } |
421 | 74 |
422 #if defined(OS_WIN) | |
423 | |
424 class DispatcherImpl : public MessagePumpDispatcher { | |
425 public: | |
426 DispatcherImpl() : dispatch_count_(0) {} | |
427 | |
428 uint32_t Dispatch(const NativeEvent& msg) override { | |
429 ::TranslateMessage(&msg); | |
430 ::DispatchMessage(&msg); | |
431 // Do not count WM_TIMER since it is not what we post and it will cause | |
432 // flakiness. | |
433 if (msg.message != WM_TIMER) | |
434 ++dispatch_count_; | |
435 // We treat WM_LBUTTONUP as the last message. | |
436 return msg.message == WM_LBUTTONUP ? POST_DISPATCH_QUIT_LOOP | |
437 : POST_DISPATCH_NONE; | |
438 } | |
439 | |
440 int dispatch_count_; | |
441 }; | |
442 | |
443 void MouseDownUp() { | |
444 PostMessage(NULL, WM_LBUTTONDOWN, 0, 0); | |
445 PostMessage(NULL, WM_LBUTTONUP, 'A', 0); | |
446 } | |
447 | |
448 void RunTest_Dispatcher(MessageLoop::Type message_loop_type) { | |
449 MessageLoop loop(message_loop_type); | |
450 | |
451 MessageLoop::current()->PostDelayedTask( | |
452 FROM_HERE, | |
453 Bind(&MouseDownUp), | |
454 TimeDelta::FromMilliseconds(100)); | |
455 DispatcherImpl dispatcher; | |
456 RunLoop run_loop(&dispatcher); | |
457 run_loop.Run(); | |
458 ASSERT_EQ(2, dispatcher.dispatch_count_); | |
459 } | |
460 | |
461 LRESULT CALLBACK MsgFilterProc(int code, WPARAM wparam, LPARAM lparam) { | |
462 if (code == MessagePumpForUI::kMessageFilterCode) { | |
463 MSG* msg = reinterpret_cast<MSG*>(lparam); | |
464 if (msg->message == WM_LBUTTONDOWN) | |
465 return TRUE; | |
466 } | |
467 return FALSE; | |
468 } | |
469 | |
470 void RunTest_DispatcherWithMessageHook(MessageLoop::Type message_loop_type) { | |
471 MessageLoop loop(message_loop_type); | |
472 | |
473 MessageLoop::current()->PostDelayedTask( | |
474 FROM_HERE, | |
475 Bind(&MouseDownUp), | |
476 TimeDelta::FromMilliseconds(100)); | |
477 HHOOK msg_hook = SetWindowsHookEx(WH_MSGFILTER, | |
478 MsgFilterProc, | |
479 NULL, | |
480 GetCurrentThreadId()); | |
481 DispatcherImpl dispatcher; | |
482 RunLoop run_loop(&dispatcher); | |
483 run_loop.Run(); | |
484 ASSERT_EQ(1, dispatcher.dispatch_count_); | |
485 UnhookWindowsHookEx(msg_hook); | |
486 } | |
487 | |
488 class TestIOHandler : public MessageLoopForIO::IOHandler { | |
489 public: | |
490 TestIOHandler(const wchar_t* name, HANDLE signal, bool wait); | |
491 | |
492 void OnIOCompleted(MessageLoopForIO::IOContext* context, | |
493 DWORD bytes_transfered, | |
494 DWORD error) override; | |
495 | |
496 void Init(); | |
497 void WaitForIO(); | |
498 OVERLAPPED* context() { return &context_.overlapped; } | |
499 DWORD size() { return sizeof(buffer_); } | |
500 | |
501 private: | |
502 char buffer_[48]; | |
503 MessageLoopForIO::IOContext context_; | |
504 HANDLE signal_; | |
505 win::ScopedHandle file_; | |
506 bool wait_; | |
507 }; | |
508 | |
509 TestIOHandler::TestIOHandler(const wchar_t* name, HANDLE signal, bool wait) | |
510 : signal_(signal), wait_(wait) { | |
511 memset(buffer_, 0, sizeof(buffer_)); | |
512 memset(&context_, 0, sizeof(context_)); | |
513 context_.handler = this; | |
514 | |
515 file_.Set(CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, | |
516 FILE_FLAG_OVERLAPPED, NULL)); | |
517 EXPECT_TRUE(file_.IsValid()); | |
518 } | |
519 | |
520 void TestIOHandler::Init() { | |
521 MessageLoopForIO::current()->RegisterIOHandler(file_.Get(), this); | |
522 | |
523 DWORD read; | |
524 EXPECT_FALSE(ReadFile(file_.Get(), buffer_, size(), &read, context())); | |
525 EXPECT_EQ(ERROR_IO_PENDING, GetLastError()); | |
526 if (wait_) | |
527 WaitForIO(); | |
528 } | |
529 | |
530 void TestIOHandler::OnIOCompleted(MessageLoopForIO::IOContext* context, | |
531 DWORD bytes_transfered, DWORD error) { | |
532 ASSERT_TRUE(context == &context_); | |
533 ASSERT_TRUE(SetEvent(signal_)); | |
534 } | |
535 | |
536 void TestIOHandler::WaitForIO() { | |
537 EXPECT_TRUE(MessageLoopForIO::current()->WaitForIOCompletion(300, this)); | |
538 EXPECT_TRUE(MessageLoopForIO::current()->WaitForIOCompletion(400, this)); | |
539 } | |
540 | |
541 void RunTest_IOHandler() { | |
542 win::ScopedHandle callback_called(CreateEvent(NULL, TRUE, FALSE, NULL)); | |
543 ASSERT_TRUE(callback_called.IsValid()); | |
544 | |
545 const wchar_t* kPipeName = L"\\\\.\\pipe\\iohandler_pipe"; | |
546 win::ScopedHandle server( | |
547 CreateNamedPipe(kPipeName, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL)); | |
548 ASSERT_TRUE(server.IsValid()); | |
549 | |
550 Thread thread("IOHandler test"); | |
551 Thread::Options options; | |
552 options.message_loop_type = MessageLoop::TYPE_IO; | |
553 ASSERT_TRUE(thread.StartWithOptions(options)); | |
554 | |
555 MessageLoop* thread_loop = thread.message_loop(); | |
556 ASSERT_TRUE(NULL != thread_loop); | |
557 | |
558 TestIOHandler handler(kPipeName, callback_called.Get(), false); | |
559 thread_loop->PostTask(FROM_HERE, Bind(&TestIOHandler::Init, | |
560 Unretained(&handler))); | |
561 // Make sure the thread runs and sleeps for lack of work. | |
562 PlatformThread::Sleep(TimeDelta::FromMilliseconds(100)); | |
563 | |
564 const char buffer[] = "Hello there!"; | |
565 DWORD written; | |
566 EXPECT_TRUE(WriteFile(server.Get(), buffer, sizeof(buffer), &written, NULL)); | |
567 | |
568 DWORD result = WaitForSingleObject(callback_called.Get(), 1000); | |
569 EXPECT_EQ(WAIT_OBJECT_0, result); | |
570 | |
571 thread.Stop(); | |
572 } | |
573 | |
574 void RunTest_WaitForIO() { | |
575 win::ScopedHandle callback1_called( | |
576 CreateEvent(NULL, TRUE, FALSE, NULL)); | |
577 win::ScopedHandle callback2_called( | |
578 CreateEvent(NULL, TRUE, FALSE, NULL)); | |
579 ASSERT_TRUE(callback1_called.IsValid()); | |
580 ASSERT_TRUE(callback2_called.IsValid()); | |
581 | |
582 const wchar_t* kPipeName1 = L"\\\\.\\pipe\\iohandler_pipe1"; | |
583 const wchar_t* kPipeName2 = L"\\\\.\\pipe\\iohandler_pipe2"; | |
584 win::ScopedHandle server1( | |
585 CreateNamedPipe(kPipeName1, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL)); | |
586 win::ScopedHandle server2( | |
587 CreateNamedPipe(kPipeName2, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL)); | |
588 ASSERT_TRUE(server1.IsValid()); | |
589 ASSERT_TRUE(server2.IsValid()); | |
590 | |
591 Thread thread("IOHandler test"); | |
592 Thread::Options options; | |
593 options.message_loop_type = MessageLoop::TYPE_IO; | |
594 ASSERT_TRUE(thread.StartWithOptions(options)); | |
595 | |
596 MessageLoop* thread_loop = thread.message_loop(); | |
597 ASSERT_TRUE(NULL != thread_loop); | |
598 | |
599 TestIOHandler handler1(kPipeName1, callback1_called.Get(), false); | |
600 TestIOHandler handler2(kPipeName2, callback2_called.Get(), true); | |
601 thread_loop->PostTask(FROM_HERE, Bind(&TestIOHandler::Init, | |
602 Unretained(&handler1))); | |
603 // TODO(ajwong): Do we really need such long Sleeps in ths function? | |
604 // Make sure the thread runs and sleeps for lack of work. | |
605 TimeDelta delay = TimeDelta::FromMilliseconds(100); | |
606 PlatformThread::Sleep(delay); | |
607 thread_loop->PostTask(FROM_HERE, Bind(&TestIOHandler::Init, | |
608 Unretained(&handler2))); | |
609 PlatformThread::Sleep(delay); | |
610 | |
611 // At this time handler1 is waiting to be called, and the thread is waiting | |
612 // on the Init method of handler2, filtering only handler2 callbacks. | |
613 | |
614 const char buffer[] = "Hello there!"; | |
615 DWORD written; | |
616 EXPECT_TRUE(WriteFile(server1.Get(), buffer, sizeof(buffer), &written, NULL)); | |
617 PlatformThread::Sleep(2 * delay); | |
618 EXPECT_EQ(WAIT_TIMEOUT, WaitForSingleObject(callback1_called.Get(), 0)) << | |
619 "handler1 has not been called"; | |
620 | |
621 EXPECT_TRUE(WriteFile(server2.Get(), buffer, sizeof(buffer), &written, NULL)); | |
622 | |
623 HANDLE objects[2] = { callback1_called.Get(), callback2_called.Get() }; | |
624 DWORD result = WaitForMultipleObjects(2, objects, TRUE, 1000); | |
625 EXPECT_EQ(WAIT_OBJECT_0, result); | |
626 | |
627 thread.Stop(); | |
628 } | |
629 | |
630 #endif // defined(OS_WIN) | |
631 | |
632 } // namespace | 75 } // namespace |
633 | 76 |
634 //----------------------------------------------------------------------------- | 77 //----------------------------------------------------------------------------- |
635 // Each test is run against each type of MessageLoop. That way we are sure | 78 // Each test is run against each type of MessageLoop. That way we are sure |
636 // that message loops work properly in all configurations. Of course, in some | 79 // that message loops work properly in all configurations. Of course, in some |
637 // cases, a unit test may only be for a particular type of loop. | 80 // cases, a unit test may only be for a particular type of loop. |
638 | 81 |
639 RUN_MESSAGE_LOOP_TESTS(Default, &TypeDefaultMessagePumpFactory); | 82 RUN_MESSAGE_LOOP_TESTS(Default, &TypeDefaultMessagePumpFactory); |
640 RUN_MESSAGE_LOOP_TESTS(UI, &TypeUIMessagePumpFactory); | 83 RUN_MESSAGE_LOOP_TESTS(UI, &TypeUIMessagePumpFactory); |
641 RUN_MESSAGE_LOOP_TESTS(IO, &TypeIOMessagePumpFactory); | 84 RUN_MESSAGE_LOOP_TESTS(IO, &TypeIOMessagePumpFactory); |
642 | 85 |
643 #if defined(OS_WIN) | |
644 TEST(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) { | |
645 RunTest_PostDelayedTask_SharedTimer_SubPump(); | |
646 } | |
647 | |
648 // This test occasionally hangs http://crbug.com/44567 | |
649 TEST(MessageLoopTest, DISABLED_RecursiveDenial2) { | |
650 RunTest_RecursiveDenial2(MessageLoop::TYPE_DEFAULT); | |
651 RunTest_RecursiveDenial2(MessageLoop::TYPE_UI); | |
652 RunTest_RecursiveDenial2(MessageLoop::TYPE_IO); | |
653 } | |
654 | |
655 TEST(MessageLoopTest, RecursiveSupport2) { | |
656 // This test requires a UI loop | |
657 RunTest_RecursiveSupport2(MessageLoop::TYPE_UI); | |
658 } | |
659 #endif // defined(OS_WIN) | |
660 | |
661 class DummyTaskObserver : public MessageLoop::TaskObserver { | 86 class DummyTaskObserver : public MessageLoop::TaskObserver { |
662 public: | 87 public: |
663 explicit DummyTaskObserver(int num_tasks) | 88 explicit DummyTaskObserver(int num_tasks) |
664 : num_tasks_started_(0), | 89 : num_tasks_started_(0), |
665 num_tasks_processed_(0), | 90 num_tasks_processed_(0), |
666 num_tasks_(num_tasks) {} | 91 num_tasks_(num_tasks) {} |
667 | 92 |
668 ~DummyTaskObserver() override {} | 93 ~DummyTaskObserver() override {} |
669 | 94 |
670 void WillProcessTask(const PendingTask& pending_task) override { | 95 void WillProcessTask(const PendingTask& pending_task) override { |
(...skipping 26 matching lines...) Expand all Loading... |
697 MessageLoop loop; | 122 MessageLoop loop; |
698 loop.AddTaskObserver(&observer); | 123 loop.AddTaskObserver(&observer); |
699 loop.PostTask(FROM_HERE, Bind(&PostNTasksThenQuit, kNumPosts)); | 124 loop.PostTask(FROM_HERE, Bind(&PostNTasksThenQuit, kNumPosts)); |
700 loop.Run(); | 125 loop.Run(); |
701 loop.RemoveTaskObserver(&observer); | 126 loop.RemoveTaskObserver(&observer); |
702 | 127 |
703 EXPECT_EQ(kNumPosts, observer.num_tasks_started()); | 128 EXPECT_EQ(kNumPosts, observer.num_tasks_started()); |
704 EXPECT_EQ(kNumPosts, observer.num_tasks_processed()); | 129 EXPECT_EQ(kNumPosts, observer.num_tasks_processed()); |
705 } | 130 } |
706 | 131 |
707 #if defined(OS_WIN) | |
708 TEST(MessageLoopTest, Dispatcher) { | |
709 // This test requires a UI loop | |
710 RunTest_Dispatcher(MessageLoop::TYPE_UI); | |
711 } | |
712 | |
713 TEST(MessageLoopTest, DispatcherWithMessageHook) { | |
714 // This test requires a UI loop | |
715 RunTest_DispatcherWithMessageHook(MessageLoop::TYPE_UI); | |
716 } | |
717 | |
718 TEST(MessageLoopTest, IOHandler) { | |
719 RunTest_IOHandler(); | |
720 } | |
721 | |
722 TEST(MessageLoopTest, WaitForIO) { | |
723 RunTest_WaitForIO(); | |
724 } | |
725 | |
726 TEST(MessageLoopTest, HighResolutionTimer) { | |
727 MessageLoop loop; | |
728 Time::EnableHighResolutionTimer(true); | |
729 | |
730 const TimeDelta kFastTimer = TimeDelta::FromMilliseconds(5); | |
731 const TimeDelta kSlowTimer = TimeDelta::FromMilliseconds(100); | |
732 | |
733 EXPECT_FALSE(loop.HasHighResolutionTasks()); | |
734 // Post a fast task to enable the high resolution timers. | |
735 loop.PostDelayedTask(FROM_HERE, Bind(&PostNTasksThenQuit, 1), | |
736 kFastTimer); | |
737 EXPECT_TRUE(loop.HasHighResolutionTasks()); | |
738 loop.Run(); | |
739 EXPECT_FALSE(loop.HasHighResolutionTasks()); | |
740 EXPECT_FALSE(Time::IsHighResolutionTimerInUse()); | |
741 // Check that a slow task does not trigger the high resolution logic. | |
742 loop.PostDelayedTask(FROM_HERE, Bind(&PostNTasksThenQuit, 1), | |
743 kSlowTimer); | |
744 EXPECT_FALSE(loop.HasHighResolutionTasks()); | |
745 loop.Run(); | |
746 EXPECT_FALSE(loop.HasHighResolutionTasks()); | |
747 Time::EnableHighResolutionTimer(false); | |
748 } | |
749 | |
750 #endif // defined(OS_WIN) | |
751 | |
752 #if defined(OS_POSIX) && !defined(OS_NACL) | 132 #if defined(OS_POSIX) && !defined(OS_NACL) |
753 | 133 |
754 namespace { | 134 namespace { |
755 | 135 |
756 class QuitDelegate : public MessageLoopForIO::Watcher { | 136 class QuitDelegate : public MessageLoopForIO::Watcher { |
757 public: | 137 public: |
758 void OnFileCanWriteWithoutBlocking(int fd) override { | 138 void OnFileCanWriteWithoutBlocking(int fd) override { |
759 MessageLoop::current()->QuitWhenIdle(); | 139 MessageLoop::current()->QuitWhenIdle(); |
760 } | 140 } |
761 void OnFileCanReadWithoutBlocking(int fd) override { | 141 void OnFileCanReadWithoutBlocking(int fd) override { |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
917 EXPECT_EQ(foo->result(), "a"); | 297 EXPECT_EQ(foo->result(), "a"); |
918 } | 298 } |
919 | 299 |
920 TEST(MessageLoopTest, IsType) { | 300 TEST(MessageLoopTest, IsType) { |
921 MessageLoop loop(MessageLoop::TYPE_UI); | 301 MessageLoop loop(MessageLoop::TYPE_UI); |
922 EXPECT_TRUE(loop.IsType(MessageLoop::TYPE_UI)); | 302 EXPECT_TRUE(loop.IsType(MessageLoop::TYPE_UI)); |
923 EXPECT_FALSE(loop.IsType(MessageLoop::TYPE_IO)); | 303 EXPECT_FALSE(loop.IsType(MessageLoop::TYPE_IO)); |
924 EXPECT_FALSE(loop.IsType(MessageLoop::TYPE_DEFAULT)); | 304 EXPECT_FALSE(loop.IsType(MessageLoop::TYPE_DEFAULT)); |
925 } | 305 } |
926 | 306 |
927 #if defined(OS_WIN) | |
928 void EmptyFunction() {} | |
929 | |
930 void PostMultipleTasks() { | |
931 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&EmptyFunction)); | |
932 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&EmptyFunction)); | |
933 } | |
934 | |
935 static const int kSignalMsg = WM_USER + 2; | |
936 | |
937 void PostWindowsMessage(HWND message_hwnd) { | |
938 PostMessage(message_hwnd, kSignalMsg, 0, 2); | |
939 } | |
940 | |
941 void EndTest(bool* did_run, HWND hwnd) { | |
942 *did_run = true; | |
943 PostMessage(hwnd, WM_CLOSE, 0, 0); | |
944 } | |
945 | |
946 int kMyMessageFilterCode = 0x5002; | |
947 | |
948 LRESULT CALLBACK TestWndProcThunk(HWND hwnd, UINT message, | |
949 WPARAM wparam, LPARAM lparam) { | |
950 if (message == WM_CLOSE) | |
951 EXPECT_TRUE(DestroyWindow(hwnd)); | |
952 if (message != kSignalMsg) | |
953 return DefWindowProc(hwnd, message, wparam, lparam); | |
954 | |
955 switch (lparam) { | |
956 case 1: | |
957 // First, we post a task that will post multiple no-op tasks to make sure | |
958 // that the pump's incoming task queue does not become empty during the | |
959 // test. | |
960 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&PostMultipleTasks)); | |
961 // Next, we post a task that posts a windows message to trigger the second | |
962 // stage of the test. | |
963 MessageLoop::current()->PostTask(FROM_HERE, | |
964 base::Bind(&PostWindowsMessage, hwnd)); | |
965 break; | |
966 case 2: | |
967 // Since we're about to enter a modal loop, tell the message loop that we | |
968 // intend to nest tasks. | |
969 MessageLoop::current()->SetNestableTasksAllowed(true); | |
970 bool did_run = false; | |
971 MessageLoop::current()->PostTask(FROM_HERE, | |
972 base::Bind(&EndTest, &did_run, hwnd)); | |
973 // Run a nested windows-style message loop and verify that our task runs. If | |
974 // it doesn't, then we'll loop here until the test times out. | |
975 MSG msg; | |
976 while (GetMessage(&msg, 0, 0, 0)) { | |
977 if (!CallMsgFilter(&msg, kMyMessageFilterCode)) | |
978 DispatchMessage(&msg); | |
979 // If this message is a WM_CLOSE, explicitly exit the modal loop. Posting | |
980 // a WM_QUIT should handle this, but unfortunately MessagePumpWin eats | |
981 // WM_QUIT messages even when running inside a modal loop. | |
982 if (msg.message == WM_CLOSE) | |
983 break; | |
984 } | |
985 EXPECT_TRUE(did_run); | |
986 MessageLoop::current()->Quit(); | |
987 break; | |
988 } | |
989 return 0; | |
990 } | |
991 | |
992 TEST(MessageLoopTest, AlwaysHaveUserMessageWhenNesting) { | |
993 MessageLoop loop(MessageLoop::TYPE_UI); | |
994 HINSTANCE instance = GetModuleFromAddress(&TestWndProcThunk); | |
995 WNDCLASSEX wc = {0}; | |
996 wc.cbSize = sizeof(wc); | |
997 wc.lpfnWndProc = TestWndProcThunk; | |
998 wc.hInstance = instance; | |
999 wc.lpszClassName = L"MessageLoopTest_HWND"; | |
1000 ATOM atom = RegisterClassEx(&wc); | |
1001 ASSERT_TRUE(atom); | |
1002 | |
1003 HWND message_hwnd = CreateWindow(MAKEINTATOM(atom), 0, 0, 0, 0, 0, 0, | |
1004 HWND_MESSAGE, 0, instance, 0); | |
1005 ASSERT_TRUE(message_hwnd) << GetLastError(); | |
1006 | |
1007 ASSERT_TRUE(PostMessage(message_hwnd, kSignalMsg, 0, 1)); | |
1008 | |
1009 loop.Run(); | |
1010 | |
1011 ASSERT_TRUE(UnregisterClass(MAKEINTATOM(atom), instance)); | |
1012 } | |
1013 #endif // defined(OS_WIN) | |
1014 | |
1015 } // namespace base | 307 } // namespace base |
OLD | NEW |