| 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 |