blob: 374a618db3496f2f8e85ddd72ff6c3fccad5e145 [file] [log] [blame]
initial.commitd7cae122008-07-26 21:49:381// Copyright 2008, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include "base/logging.h"
31#include "base/message_loop.h"
32#include "base/scoped_handle.h"
33#include "base/thread.h"
34#include "base/ref_counted.h"
35#include "testing/gtest/include/gtest/gtest.h"
36
37namespace {
38
39class MessageLoopTest : public testing::Test {
40 public:
41 virtual void SetUp() {
42 enable_recursive_task_ = MessageLoop::current()->NestableTasksAllowed();
43 }
44 virtual void TearDown() {
45 MessageLoop::current()->SetNestableTasksAllowed(enable_recursive_task_);
46 }
47 private:
48 bool enable_recursive_task_;
49};
50
51class Foo : public base::RefCounted<Foo> {
52 public:
53 Foo() : test_count_(0) {
54 }
55
56 void Test0() {
57 ++test_count_;
58 }
59
60 void Test1ConstRef(const std::string& a) {
61 ++test_count_;
62 result_.append(a);
63 }
64
65 void Test1Ptr(std::string* a) {
66 ++test_count_;
67 result_.append(*a);
68 }
69
70 void Test1Int(int a) {
71 test_count_ += a;
72 }
73
74 void Test2Ptr(std::string* a, std::string* b) {
75 ++test_count_;
76 result_.append(*a);
77 result_.append(*b);
78 }
79
80 void Test2Mixed(const std::string& a, std::string* b) {
81 ++test_count_;
82 result_.append(a);
83 result_.append(*b);
84 }
85
86 int test_count() const { return test_count_; }
87 const std::string& result() const { return result_; }
88
89 private:
90 int test_count_;
91 std::string result_;
92};
93
94class QuitMsgLoop : public base::RefCounted<QuitMsgLoop> {
95 public:
96 void QuitNow() {
97 MessageLoop::current()->Quit();
98 }
99};
100
101} // namespace
102
103TEST(MessageLoopTest, PostTask) {
104 // Add tests to message loop
105 scoped_refptr<Foo> foo = new Foo();
106 std::string a("a"), b("b"), c("c"), d("d");
107 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
108 foo.get(), &Foo::Test0));
109 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
110 foo.get(), &Foo::Test1ConstRef, a));
111 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
112 foo.get(), &Foo::Test1Ptr, &b));
113 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
114 foo.get(), &Foo::Test1Int, 100));
115 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
116 foo.get(), &Foo::Test2Ptr, &a, &c));
117 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
118 foo.get(), &Foo::Test2Mixed, a, &d));
119
120 // After all tests, post a message that will shut down the message loop
121 scoped_refptr<QuitMsgLoop> quit = new QuitMsgLoop();
122 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
123 quit.get(), &QuitMsgLoop::QuitNow));
124
125 // Now kick things off
126 MessageLoop::current()->Run();
127
128 EXPECT_EQ(foo->test_count(), 105);
129 EXPECT_EQ(foo->result(), "abacad");
130}
131
132TEST(MessageLoopTest, InvokeLater_SEH) {
133 // Add tests to message loop
134 scoped_refptr<Foo> foo = new Foo();
135 std::string a("a"), b("b"), c("c"), d("d");
136 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
137 foo.get(), &Foo::Test0));
138 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
139 foo.get(), &Foo::Test1ConstRef, a));
140 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
141 foo.get(), &Foo::Test1Ptr, &b));
142 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
143 foo.get(), &Foo::Test1Int, 100));
144 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
145 foo.get(), &Foo::Test2Ptr, &a, &c));
146 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
147 foo.get(), &Foo::Test2Mixed, a, &d));
148
149 // After all tests, post a message that will shut down the message loop
150 scoped_refptr<QuitMsgLoop> quit = new QuitMsgLoop();
151 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
152 quit.get(), &QuitMsgLoop::QuitNow));
153
154 // Now kick things off with the SEH block active.
155 MessageLoop::current()->set_exception_restoration(true);
156 MessageLoop::current()->Run();
157 MessageLoop::current()->set_exception_restoration(false);
158
159 EXPECT_EQ(foo->test_count(), 105);
160 EXPECT_EQ(foo->result(), "abacad");
161}
162
163namespace {
164
165class NestingTest : public Task {
166 public:
167 explicit NestingTest(int* depth) : depth_(depth) {
168 }
169 void Run() {
170 if (*depth_ > 0) {
171 *depth_ -= 1;
172 MessageLoop::current()->PostTask(FROM_HERE, new NestingTest(depth_));
173
174 MessageLoop::current()->SetNestableTasksAllowed(true);
175 MessageLoop::current()->Run();
176 }
177 MessageLoop::current()->Quit();
178 }
179 private:
180 int* depth_;
181};
182
183LONG WINAPI BadExceptionHandler(EXCEPTION_POINTERS *ex_info) {
184 ADD_FAILURE() << "bad exception handler";
185 ::ExitProcess(ex_info->ExceptionRecord->ExceptionCode);
186 return EXCEPTION_EXECUTE_HANDLER;
187}
188
189// This task throws an SEH exception: initially write to an invalid address.
190// If the right SEH filter is installed, it will fix the error.
191class CrasherTask : public Task {
192 public:
193 // Ctor. If trash_SEH_handler is true, the task will override the unhandled
194 // exception handler with one sure to crash this test.
195 explicit CrasherTask(bool trash_SEH_handler)
196 : trash_SEH_handler_(trash_SEH_handler) {
197 }
198 void Run() {
199 Sleep(1);
200 if (trash_SEH_handler_)
201 ::SetUnhandledExceptionFilter(&BadExceptionHandler);
202 // Generate a SEH fault. We do it in asm to make sure we know how to undo
203 // the damage.
204 __asm {
205 mov eax, dword ptr [CrasherTask::bad_array_]
206 mov byte ptr [eax], 66
207 }
208 MessageLoop::current()->Quit();
209 }
210 // Points the bad array to a valid memory location.
211 static void FixError() {
212 bad_array_ = &valid_store_;
213 }
214
215 private:
216 bool trash_SEH_handler_;
217 static volatile char* bad_array_;
218 static char valid_store_;
219};
220
221volatile char* CrasherTask::bad_array_ = 0;
222char CrasherTask::valid_store_ = 0;
223
224// This SEH filter fixes the problem and retries execution. Fixing requires
225// that the last instruction: mov eax, [CrasherTask::bad_array_] to be retried
226// so we move the instruction pointer 5 bytes back.
227LONG WINAPI HandleCrasherTaskException(EXCEPTION_POINTERS *ex_info) {
228 if (ex_info->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
229 return EXCEPTION_EXECUTE_HANDLER;
230
231 CrasherTask::FixError();
232 ex_info->ContextRecord->Eip -= 5;
233 return EXCEPTION_CONTINUE_EXECUTION;
234}
235
236} // namespace
237
238
239TEST(MessageLoopTest, Crasher) {
240 if (::IsDebuggerPresent())
241 return;
242
243 LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter =
244 ::SetUnhandledExceptionFilter(&HandleCrasherTaskException);
245
246 MessageLoop::current()->PostTask(FROM_HERE, new CrasherTask(false));
247 MessageLoop::current()->set_exception_restoration(true);
248 MessageLoop::current()->Run();
249 MessageLoop::current()->set_exception_restoration(false);
250
251 ::SetUnhandledExceptionFilter(old_SEH_filter);
252}
253
254
255TEST(MessageLoopTest, CrasherNasty) {
256 if (::IsDebuggerPresent())
257 return;
258
259 LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter =
260 ::SetUnhandledExceptionFilter(&HandleCrasherTaskException);
261
262 MessageLoop::current()->PostTask(FROM_HERE, new CrasherTask(true));
263 MessageLoop::current()->set_exception_restoration(true);
264 MessageLoop::current()->Run();
265 MessageLoop::current()->set_exception_restoration(false);
266
267 ::SetUnhandledExceptionFilter(old_SEH_filter);
268}
269
270TEST(MessageLoopTest, Nesting) {
271 int depth = 100;
272 MessageLoop::current()->PostTask(FROM_HERE, new NestingTest(&depth));
273 MessageLoop::current()->Run();
274 EXPECT_EQ(depth, 0);
275}
276
277namespace {
278
279const wchar_t* const kMessageBoxTitle = L"MessageLoop Unit Test";
280
281enum TaskType {
282 MESSAGEBOX,
283 ENDDIALOG,
284 RECURSIVE,
285 TIMEDMESSAGELOOP,
286 QUITMESSAGELOOP,
287 ORDERERD,
288 PUMPS,
289};
290
291// Saves the order in which the tasks executed.
292struct TaskItem {
293 TaskItem(TaskType t, int c, bool s)
294 : type(t),
295 cookie(c),
296 start(s) {
297 }
298
299 TaskType type;
300 int cookie;
301 bool start;
302
303 bool operator == (const TaskItem& other) const {
304 return type == other.type && cookie == other.cookie && start == other.start;
305 }
306};
307
308typedef std::vector<TaskItem> TaskList;
309
310std::ostream& operator <<(std::ostream& os, TaskType type) {
311 switch (type) {
312 case MESSAGEBOX: os << "MESSAGEBOX"; break;
313 case ENDDIALOG: os << "ENDDIALOG"; break;
314 case RECURSIVE: os << "RECURSIVE"; break;
315 case TIMEDMESSAGELOOP: os << "TIMEDMESSAGELOOP"; break;
316 case QUITMESSAGELOOP: os << "QUITMESSAGELOOP"; break;
317 case ORDERERD: os << "ORDERERD"; break;
318 case PUMPS: os << "PUMPS"; break;
319 default:
320 NOTREACHED();
321 os << "Unknown TaskType";
322 break;
323 }
324 return os;
325}
326
327std::ostream& operator <<(std::ostream& os, const TaskItem& item) {
328 if (item.start)
329 return os << item.type << " " << item.cookie << " starts";
330 else
331 return os << item.type << " " << item.cookie << " ends";
332}
333
334// Saves the order the tasks ran.
335class OrderedTasks : public Task {
336 public:
337 OrderedTasks(TaskList* order, int cookie)
338 : order_(order),
339 type_(ORDERERD),
340 cookie_(cookie) {
341 }
342 OrderedTasks(TaskList* order, TaskType type, int cookie)
343 : order_(order),
344 type_(type),
345 cookie_(cookie) {
346 }
347
348 void RunStart() {
349 TaskItem item(type_, cookie_, true);
350 DLOG(INFO) << item;
351 order_->push_back(item);
352 }
353 void RunEnd() {
354 TaskItem item(type_, cookie_, false);
355 DLOG(INFO) << item;
356 order_->push_back(item);
357 }
358
359 virtual void Run() {
360 RunStart();
361 RunEnd();
362 }
363
364 protected:
365 TaskList* order() const {
366 return order_;
367 }
368
369 int cookie() const {
370 return cookie_;
371 }
372
373 private:
374 TaskList* order_;
375 TaskType type_;
376 int cookie_;
377};
378
379// MessageLoop implicitly start a "modal message loop". Modal dialog boxes,
380// common controls (like OpenFile) and StartDoc printing function can cause
381// implicit message loops.
382class MessageBoxTask : public OrderedTasks {
383 public:
384 MessageBoxTask(TaskList* order, int cookie, bool is_reentrant)
385 : OrderedTasks(order, MESSAGEBOX, cookie),
386 is_reentrant_(is_reentrant) {
387 }
388
389 virtual void Run() {
390 RunStart();
391 if (is_reentrant_)
392 MessageLoop::current()->SetNestableTasksAllowed(true);
393 MessageBox(NULL, L"Please wait...", kMessageBoxTitle, MB_OK);
394 RunEnd();
395 }
396
397 private:
398 bool is_reentrant_;
399};
400
401// Will end the MessageBox.
402class EndDialogTask : public OrderedTasks {
403 public:
404 EndDialogTask(TaskList* order, int cookie)
405 : OrderedTasks(order, ENDDIALOG, cookie) {
406 }
407
408 virtual void Run() {
409 RunStart();
410 HWND window = GetActiveWindow();
411 if (window != NULL) {
412 EXPECT_NE(EndDialog(window, IDCONTINUE), 0);
413 // Cheap way to signal that the window wasn't found if RunEnd() isn't
414 // called.
415 RunEnd();
416 }
417 }
418};
419
420class RecursiveTask : public OrderedTasks {
421 public:
422 RecursiveTask(int depth, TaskList* order, int cookie, bool is_reentrant)
423 : OrderedTasks(order, RECURSIVE, cookie),
424 depth_(depth),
425 is_reentrant_(is_reentrant) {
426 }
427
428 virtual void Run() {
429 RunStart();
430 if (depth_ > 0) {
431 if (is_reentrant_)
432 MessageLoop::current()->SetNestableTasksAllowed(true);
433 MessageLoop::current()->PostTask(FROM_HERE,
434 new RecursiveTask(depth_ - 1, order(), cookie(), is_reentrant_));
435 }
436 RunEnd();
437 }
438
439 private:
440 int depth_;
441 bool is_reentrant_;
442};
443
444class QuitTask : public OrderedTasks {
445 public:
446 QuitTask(TaskList* order, int cookie)
447 : OrderedTasks(order, QUITMESSAGELOOP, cookie) {
448 }
449
450 virtual void Run() {
451 RunStart();
452 MessageLoop::current()->Quit();
453 RunEnd();
454 }
455};
456
457class Recursive2Tasks : public Task {
458 public:
459 Recursive2Tasks(MessageLoop* target,
460 HANDLE event,
461 bool expect_window,
462 TaskList* order,
463 bool is_reentrant)
464 : target_(target),
465 event_(event),
466 expect_window_(expect_window),
467 order_(order),
468 is_reentrant_(is_reentrant) {
469 }
470
471 virtual void Run() {
472 target_->PostTask(FROM_HERE,
473 new RecursiveTask(2, order_, 1, is_reentrant_));
474 target_->PostTask(FROM_HERE,
475 new MessageBoxTask(order_, 2, is_reentrant_));
476 target_->PostTask(FROM_HERE,
477 new RecursiveTask(2, order_, 3, is_reentrant_));
478 // The trick here is that for recursive task processing, this task will be
479 // ran _inside_ the MessageBox message loop, dismissing the MessageBox
480 // without a chance.
481 // For non-recursive task processing, this will be executed _after_ the
482 // MessageBox will have been dismissed by the code below, where
483 // expect_window_ is true.
484 target_->PostTask(FROM_HERE, new EndDialogTask(order_, 4));
485 target_->PostTask(FROM_HERE, new QuitTask(order_, 5));
486
487 // Enforce that every tasks are sent before starting to run the main thread
488 // message loop.
489 ASSERT_TRUE(SetEvent(event_));
490
491 // Poll for the MessageBox. Don't do this at home! At the speed we do it,
492 // you will never realize one MessageBox was shown.
493 for (; expect_window_;) {
494 HWND window = FindWindow(L"#32770", kMessageBoxTitle);
495 if (window) {
496 // Dismiss it.
497 for (;;) {
498 HWND button = FindWindowEx(window, NULL, L"Button", NULL);
499 if (button != NULL) {
500 EXPECT_TRUE(0 == SendMessage(button, WM_LBUTTONDOWN, 0, 0));
501 EXPECT_TRUE(0 == SendMessage(button, WM_LBUTTONUP, 0, 0));
502 break;
503 }
504 }
505 break;
506 }
507 }
508 }
509
510 private:
511 MessageLoop* target_;
512 HANDLE event_;
513 TaskList* order_;
514 bool expect_window_;
515 bool is_reentrant_;
516};
517
518} // namespace
519
520TEST(MessageLoop, RecursiveDenial1) {
521 EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
522 TaskList order;
523 MessageLoop::current()->PostTask(FROM_HERE,
524 new RecursiveTask(2, &order, 1, false));
525 MessageLoop::current()->PostTask(FROM_HERE,
526 new RecursiveTask(2, &order, 2, false));
527 MessageLoop::current()->PostTask(FROM_HERE, new QuitTask(&order, 3));
528
529 MessageLoop::current()->Run();
530
531 // FIFO order.
532 ASSERT_EQ(order.size(), 14);
533 EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true));
534 EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false));
535 EXPECT_EQ(order[ 2], TaskItem(RECURSIVE, 2, true));
536 EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 2, false));
537 EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true));
538 EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false));
539 EXPECT_EQ(order[ 6], TaskItem(RECURSIVE, 1, true));
540 EXPECT_EQ(order[ 7], TaskItem(RECURSIVE, 1, false));
541 EXPECT_EQ(order[ 8], TaskItem(RECURSIVE, 2, true));
542 EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 2, false));
543 EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true));
544 EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false));
545 EXPECT_EQ(order[12], TaskItem(RECURSIVE, 2, true));
546 EXPECT_EQ(order[13], TaskItem(RECURSIVE, 2, false));
547}
548
549
550TEST(MessageLoop, RecursiveSupport1) {
551 TaskList order;
552 MessageLoop::current()->PostTask(FROM_HERE,
553 new RecursiveTask(2, &order, 1, true));
554 MessageLoop::current()->PostTask(FROM_HERE,
555 new RecursiveTask(2, &order, 2, true));
556 MessageLoop::current()->PostTask(FROM_HERE,
557 new QuitTask(&order, 3));
558
559 MessageLoop::current()->Run();
560
561 // FIFO order.
562 ASSERT_EQ(order.size(), 14);
563 EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true));
564 EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false));
565 EXPECT_EQ(order[ 2], TaskItem(RECURSIVE, 2, true));
566 EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 2, false));
567 EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true));
568 EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false));
569 EXPECT_EQ(order[ 6], TaskItem(RECURSIVE, 1, true));
570 EXPECT_EQ(order[ 7], TaskItem(RECURSIVE, 1, false));
571 EXPECT_EQ(order[ 8], TaskItem(RECURSIVE, 2, true));
572 EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 2, false));
573 EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true));
574 EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false));
575 EXPECT_EQ(order[12], TaskItem(RECURSIVE, 2, true));
576 EXPECT_EQ(order[13], TaskItem(RECURSIVE, 2, false));
577}
578
579// A side effect of this test is the generation a beep. Sorry.
580TEST(MessageLoop, RecursiveDenial2) {
581 Thread worker("RecursiveDenial2_worker");
582 ASSERT_EQ(true, worker.Start());
583 TaskList order;
584 ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
585 worker.message_loop()->PostTask(FROM_HERE,
586 new Recursive2Tasks(MessageLoop::current(),
587 event,
588 true,
589 &order,
590 false));
591 // Let the other thread execute.
592 WaitForSingleObject(event, INFINITE);
593 MessageLoop::current()->Run();
594
595 ASSERT_EQ(order.size(), 17);
596 EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true));
597 EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false));
598 EXPECT_EQ(order[ 2], TaskItem(MESSAGEBOX, 2, true));
599 EXPECT_EQ(order[ 3], TaskItem(MESSAGEBOX, 2, false));
600 EXPECT_EQ(order[ 4], TaskItem(RECURSIVE, 3, true));
601 EXPECT_EQ(order[ 5], TaskItem(RECURSIVE, 3, false));
602 // When EndDialogTask is processed, the window is already dismissed, hence no
603 // "end" entry.
604 EXPECT_EQ(order[ 6], TaskItem(ENDDIALOG, 4, true));
605 EXPECT_EQ(order[ 7], TaskItem(QUITMESSAGELOOP, 5, true));
606 EXPECT_EQ(order[ 8], TaskItem(QUITMESSAGELOOP, 5, false));
607 EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 1, true));
608 EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, false));
609 EXPECT_EQ(order[11], TaskItem(RECURSIVE, 3, true));
610 EXPECT_EQ(order[12], TaskItem(RECURSIVE, 3, false));
611 EXPECT_EQ(order[13], TaskItem(RECURSIVE, 1, true));
612 EXPECT_EQ(order[14], TaskItem(RECURSIVE, 1, false));
613 EXPECT_EQ(order[15], TaskItem(RECURSIVE, 3, true));
614 EXPECT_EQ(order[16], TaskItem(RECURSIVE, 3, false));
615}
616
617// A side effect of this test is the generation a beep. Sorry.
618TEST(MessageLoop, RecursiveSupport2) {
619 Thread worker("RecursiveSupport2_worker");
620 ASSERT_EQ(true, worker.Start());
621 TaskList order;
622 ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
623 worker.message_loop()->PostTask(FROM_HERE,
624 new Recursive2Tasks(MessageLoop::current(),
625 event,
626 false,
627 &order,
628 true));
629 // Let the other thread execute.
630 WaitForSingleObject(event, INFINITE);
631 MessageLoop::current()->Run();
632
633 ASSERT_EQ(order.size(), 18);
634 EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true));
635 EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false));
636 EXPECT_EQ(order[ 2], TaskItem(MESSAGEBOX, 2, true));
637 // Note that this executes in the MessageBox modal loop.
638 EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 3, true));
639 EXPECT_EQ(order[ 4], TaskItem(RECURSIVE, 3, false));
640 EXPECT_EQ(order[ 5], TaskItem(ENDDIALOG, 4, true));
641 EXPECT_EQ(order[ 6], TaskItem(ENDDIALOG, 4, false));
642 EXPECT_EQ(order[ 7], TaskItem(MESSAGEBOX, 2, false));
643 /* The order can subtly change here. The reason is that when RecursiveTask(1)
644 is called in the main thread, if it is faster than getting to the
645 PostTask(FROM_HERE, QuitTask) execution, the order of task execution can
646 change. We don't care anyway that the order isn't correct.
647 EXPECT_EQ(order[ 8], TaskItem(QUITMESSAGELOOP, 5, true));
648 EXPECT_EQ(order[ 9], TaskItem(QUITMESSAGELOOP, 5, false));
649 EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true));
650 EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false));
651 */
652 EXPECT_EQ(order[12], TaskItem(RECURSIVE, 3, true));
653 EXPECT_EQ(order[13], TaskItem(RECURSIVE, 3, false));
654 EXPECT_EQ(order[14], TaskItem(RECURSIVE, 1, true));
655 EXPECT_EQ(order[15], TaskItem(RECURSIVE, 1, false));
656 EXPECT_EQ(order[16], TaskItem(RECURSIVE, 3, true));
657 EXPECT_EQ(order[17], TaskItem(RECURSIVE, 3, false));
658}
659
660class TaskThatPumps : public OrderedTasks {
661 public:
662 TaskThatPumps(TaskList* order, int cookie)
663 : OrderedTasks(order, PUMPS, cookie) {
664 }
665
666 virtual void Run() {
667 RunStart();
668 bool old_state = MessageLoop::current()->NestableTasksAllowed();
669 MessageLoop::current()->Quit();
670 MessageLoop::current()->SetNestableTasksAllowed(true);
671 MessageLoop::current()->Run();
672 MessageLoop::current()->SetNestableTasksAllowed(old_state);
673 RunEnd();
674 }
675
676 private:
677};
678
679
680// Tests that non nestable tasks run in FIFO if there are no nested loops.
681TEST(MessageLoop, NonNestableWithNoNesting) {
682 TaskList order;
683
684 Task* task = new OrderedTasks(&order, 1);
685 task->set_nestable(false);
686 MessageLoop::current()->PostTask(FROM_HERE, task);
687 MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 2));
688 MessageLoop::current()->PostTask(FROM_HERE, new QuitTask(&order, 3));
689 MessageLoop::current()->Run();
690
691 // FIFO order.
692 ASSERT_EQ(order.size(), 6);
693 EXPECT_EQ(order[ 0], TaskItem(ORDERERD, 1, true));
694 EXPECT_EQ(order[ 1], TaskItem(ORDERERD, 1, false));
695 EXPECT_EQ(order[ 2], TaskItem(ORDERERD, 2, true));
696 EXPECT_EQ(order[ 3], TaskItem(ORDERERD, 2, false));
697 EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true));
698 EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false));
699}
700
701// Tests that non nestable tasks don't run when there's code in the call stack.
702TEST(MessageLoop, NonNestableInNestedLoop) {
703 TaskList order;
704
705 MessageLoop::current()->PostTask(FROM_HERE,
706 new TaskThatPumps(&order, 1));
707 Task* task = new OrderedTasks(&order, 2);
708 task->set_nestable(false);
709 MessageLoop::current()->PostTask(FROM_HERE, task);
710 MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 3));
711 MessageLoop::current()->PostTask(FROM_HERE, new QuitTask(&order, 4));
712 Task* non_nestable_quit = new QuitTask(&order, 5);
713 non_nestable_quit->set_nestable(false);
714 MessageLoop::current()->PostTask(FROM_HERE, non_nestable_quit);
715
716
717 MessageLoop::current()->Run();
718
719 // FIFO order.
720 ASSERT_EQ(order.size(), 10);
721 EXPECT_EQ(order[ 0], TaskItem(PUMPS, 1, true));
722 EXPECT_EQ(order[ 1], TaskItem(ORDERERD, 3, true));
723 EXPECT_EQ(order[ 2], TaskItem(ORDERERD, 3, false));
724 EXPECT_EQ(order[ 3], TaskItem(QUITMESSAGELOOP, 4, true));
725 EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 4, false));
726 EXPECT_EQ(order[ 5], TaskItem(PUMPS, 1, false));
727 EXPECT_EQ(order[ 6], TaskItem(ORDERERD, 2, true));
728 EXPECT_EQ(order[ 7], TaskItem(ORDERERD, 2, false));
729 EXPECT_EQ(order[ 8], TaskItem(QUITMESSAGELOOP, 5, true));
730 EXPECT_EQ(order[ 9], TaskItem(QUITMESSAGELOOP, 5, false));
731}
732
733
734namespace {
735
736class AutoresetWatcher : public MessageLoop::Watcher {
737 public:
738 AutoresetWatcher(HANDLE signal, MessageLoop* message_loop)
739 : signal_(signal), message_loop_(message_loop) {}
740 virtual void OnObjectSignaled(HANDLE object);
741 private:
742 HANDLE signal_;
743 MessageLoop* message_loop_;
744};
745
746void AutoresetWatcher::OnObjectSignaled(HANDLE object) {
747 message_loop_->WatchObject(object, NULL);
748 ASSERT_TRUE(SetEvent(signal_));
749}
750
751class AutoresetTask : public Task {
752 public:
753 AutoresetTask(HANDLE object, MessageLoop::Watcher* watcher)
754 : object_(object), watcher_(watcher) {}
755 virtual void Run() {
756 MessageLoop::current()->WatchObject(object_, watcher_);
757 }
758
759 private:
760 HANDLE object_;
761 MessageLoop::Watcher* watcher_;
762};
763
764} // namespace
765
766TEST(MessageLoop, AutoresetEvents) {
767 SECURITY_ATTRIBUTES attributes;
768 attributes.nLength = sizeof(attributes);
769 attributes.bInheritHandle = false;
770 attributes.lpSecurityDescriptor = NULL;
771
772 // Init an autoreset and a manual reset events.
773 HANDLE autoreset = CreateEvent(&attributes, FALSE, FALSE, NULL);
774 HANDLE callback_called = CreateEvent(&attributes, TRUE, FALSE, NULL);
775 ASSERT_TRUE(NULL != autoreset);
776 ASSERT_TRUE(NULL != callback_called);
777
778 Thread thread("Autoreset test");
779 ASSERT_TRUE(thread.Start());
780
781 MessageLoop* message_loop = thread.message_loop();
782 ASSERT_TRUE(NULL != message_loop);
783
784 AutoresetWatcher watcher(callback_called, message_loop);
785 AutoresetTask* task = new AutoresetTask(autoreset, &watcher);
786 message_loop->PostTask(FROM_HERE, task);
787 Sleep(100); // Make sure the thread runs and sleeps for lack of work.
788
789 ASSERT_TRUE(SetEvent(autoreset));
790
791 DWORD result = WaitForSingleObject(callback_called, 1000);
792 EXPECT_EQ(WAIT_OBJECT_0, result);
793
794 thread.Stop();
795}
796
797namespace {
798
799class DispatcherImpl : public MessageLoop::Dispatcher {
800 public:
801 DispatcherImpl() : dispatch_count_(0) {}
802
803 virtual bool Dispatch(const MSG& msg) {
804 ::TranslateMessage(&msg);
805 ::DispatchMessage(&msg);
806 return (++dispatch_count_ != 2);
807 }
808
809 int dispatch_count_;
810};
811
812} // namespace
813
814TEST(MessageLoop, Dispatcher) {
815 class MyTask : public Task {
816 public:
817 virtual void Run() {
818 PostMessage(NULL, WM_LBUTTONDOWN, 0, 0);
819 PostMessage(NULL, WM_LBUTTONUP, 'A', 0);
820 }
821 };
822 Task* task = new MyTask();
823 MessageLoop::current()->PostDelayedTask(FROM_HERE, task, 100);
824 DispatcherImpl dispatcher;
825 MessageLoop::current()->Run(&dispatcher);
826 ASSERT_EQ(2, dispatcher.dispatch_count_);
827}