message_pump_glib_unittest.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/message_pump_glib.h" 6 7#include <gtk/gtk.h> 8#include <math.h> 9 10#include <algorithm> 11#include <vector> 12 13#include "base/message_loop.h" 14#include "base/platform_thread.h" 15#include "base/ref_counted.h" 16#include "base/thread.h" 17#include "testing/gtest/include/gtest/gtest.h" 18 19namespace { 20 21// This class injects dummy "events" into the GLib loop. When "handled" these 22// events can run tasks. This is intended to mock gtk events (the corresponding 23// GLib source runs at the same priority). 24class EventInjector { 25 public: 26 EventInjector() : processed_events_(0) { 27 source_ = static_cast<Source*>(g_source_new(&SourceFuncs, sizeof(Source))); 28 source_->injector = this; 29 g_source_attach(source_, NULL); 30 g_source_set_can_recurse(source_, TRUE); 31 } 32 33 ~EventInjector() { 34 g_source_destroy(source_); 35 g_source_unref(source_); 36 } 37 38 int HandlePrepare() { 39 // If the queue is empty, block. 40 if (events_.empty()) 41 return -1; 42 base::TimeDelta delta = events_[0].time - base::Time::NowFromSystemTime(); 43 return std::max(0, static_cast<int>(ceil(delta.InMillisecondsF()))); 44 } 45 46 bool HandleCheck() { 47 if (events_.empty()) 48 return false; 49 Event event = events_[0]; 50 return events_[0].time <= base::Time::NowFromSystemTime(); 51 } 52 53 void HandleDispatch() { 54 if (events_.empty()) 55 return; 56 Event event = events_[0]; 57 events_.erase(events_.begin()); 58 ++processed_events_; 59 if (event.task) { 60 event.task->Run(); 61 delete event.task; 62 } 63 } 64 65 // Adds an event to the queue. When "handled", executes |task|. 66 // delay_ms is relative to the last event if any, or to Now() otherwise. 67 void AddEvent(int delay_ms, Task* task) { 68 base::Time last_time; 69 if (!events_.empty()) { 70 last_time = (events_.end()-1)->time; 71 } else { 72 last_time = base::Time::NowFromSystemTime(); 73 } 74 base::Time future = last_time + base::TimeDelta::FromMilliseconds(delay_ms); 75 EventInjector::Event event = { future, task }; 76 events_.push_back(event); 77 } 78 79 void Reset() { 80 processed_events_ = 0; 81 events_.clear(); 82 } 83 84 int processed_events() const { return processed_events_; } 85 86 private: 87 struct Event { 88 base::Time time; 89 Task* task; 90 }; 91 92 struct Source : public GSource { 93 EventInjector* injector; 94 }; 95 96 static gboolean Prepare(GSource* source, gint* timeout_ms) { 97 *timeout_ms = static_cast<Source*>(source)->injector->HandlePrepare(); 98 return FALSE; 99 } 100 101 static gboolean Check(GSource* source) { 102 return static_cast<Source*>(source)->injector->HandleCheck(); 103 } 104 105 static gboolean Dispatch(GSource* source, 106 GSourceFunc unused_func, 107 gpointer unused_data) { 108 static_cast<Source*>(source)->injector->HandleDispatch(); 109 return TRUE; 110 } 111 112 Source* source_; 113 std::vector<Event> events_; 114 int processed_events_; 115 static GSourceFuncs SourceFuncs; 116 DISALLOW_COPY_AND_ASSIGN(EventInjector); 117}; 118 119GSourceFuncs EventInjector::SourceFuncs = { 120 EventInjector::Prepare, 121 EventInjector::Check, 122 EventInjector::Dispatch, 123 NULL 124}; 125 126// Does nothing. This function can be called from a task. 127void DoNothing() { 128} 129 130void IncrementInt(int *value) { 131 ++*value; 132} 133 134// Checks how many events have been processed by the injector. 135void ExpectProcessedEvents(EventInjector* injector, int count) { 136 EXPECT_EQ(injector->processed_events(), count); 137} 138 139// Quits the current message loop. 140void QuitMessageLoop() { 141 MessageLoop::current()->Quit(); 142} 143 144// Returns a new task that quits the main loop. 145Task* NewQuitTask() { 146 return NewRunnableFunction(QuitMessageLoop); 147} 148 149// Posts a task on the current message loop. 150void PostMessageLoopTask(const tracked_objects::Location& from_here, 151 Task* task) { 152 MessageLoop::current()->PostTask(from_here, task); 153} 154 155// Test fixture. 156class MessagePumpGLibTest : public testing::Test { 157 public: 158 MessagePumpGLibTest() : loop_(NULL), injector_(NULL) { } 159 160 virtual void SetUp() { 161 loop_ = new MessageLoop(MessageLoop::TYPE_UI); 162 injector_ = new EventInjector(); 163 } 164 165 virtual void TearDown() { 166 delete injector_; 167 injector_ = NULL; 168 delete loop_; 169 loop_ = NULL; 170 } 171 172 MessageLoop* loop() const { return loop_; } 173 EventInjector* injector() const { return injector_; } 174 175 private: 176 MessageLoop* loop_; 177 EventInjector* injector_; 178 DISALLOW_COPY_AND_ASSIGN(MessagePumpGLibTest); 179}; 180 181} // namespace 182 183// EventInjector is expected to always live longer than the runnable methods. 184DISABLE_RUNNABLE_METHOD_REFCOUNT(EventInjector); 185 186TEST_F(MessagePumpGLibTest, TestQuit) { 187 // Checks that Quit works and that the basic infrastructure is working. 188 189 // Quit from a task 190 loop()->PostTask(FROM_HERE, NewQuitTask()); 191 loop()->Run(); 192 EXPECT_EQ(0, injector()->processed_events()); 193 194 injector()->Reset(); 195 // Quit from an event 196 injector()->AddEvent(0, NewQuitTask()); 197 loop()->Run(); 198 EXPECT_EQ(1, injector()->processed_events()); 199} 200 201TEST_F(MessagePumpGLibTest, TestEventTaskInterleave) { 202 // Checks that tasks posted by events are executed before the next event if 203 // the posted task queue is empty. 204 // MessageLoop doesn't make strong guarantees that it is the case, but the 205 // current implementation ensures it and the tests below rely on it. 206 // If changes cause this test to fail, it is reasonable to change it, but 207 // TestWorkWhileWaitingForEvents and TestEventsWhileWaitingForWork have to be 208 // changed accordingly, otherwise they can become flaky. 209 injector()->AddEvent(0, NewRunnableFunction(DoNothing)); 210 Task* check_task = NewRunnableFunction(ExpectProcessedEvents, injector(), 2); 211 Task* posted_task = NewRunnableFunction(PostMessageLoopTask, 212 FROM_HERE, check_task); 213 injector()->AddEvent(0, posted_task); 214 injector()->AddEvent(0, NewRunnableFunction(DoNothing)); 215 injector()->AddEvent(0, NewQuitTask()); 216 loop()->Run(); 217 EXPECT_EQ(4, injector()->processed_events()); 218 219 injector()->Reset(); 220 injector()->AddEvent(0, NewRunnableFunction(DoNothing)); 221 check_task = NewRunnableFunction(ExpectProcessedEvents, injector(), 2); 222 posted_task = NewRunnableFunction(PostMessageLoopTask, FROM_HERE, check_task); 223 injector()->AddEvent(0, posted_task); 224 injector()->AddEvent(10, NewRunnableFunction(DoNothing)); 225 injector()->AddEvent(0, NewQuitTask()); 226 loop()->Run(); 227 EXPECT_EQ(4, injector()->processed_events()); 228} 229 230TEST_F(MessagePumpGLibTest, TestWorkWhileWaitingForEvents) { 231 int task_count = 0; 232 // Tests that we process tasks while waiting for new events. 233 // The event queue is empty at first. 234 for (int i = 0; i < 10; ++i) { 235 loop()->PostTask(FROM_HERE, NewRunnableFunction(IncrementInt, &task_count)); 236 } 237 // After all the previous tasks have executed, enqueue an event that will 238 // quit. 239 loop()->PostTask( 240 FROM_HERE, NewRunnableMethod(injector(), &EventInjector::AddEvent, 241 0, NewQuitTask())); 242 loop()->Run(); 243 ASSERT_EQ(10, task_count); 244 EXPECT_EQ(1, injector()->processed_events()); 245 246 // Tests that we process delayed tasks while waiting for new events. 247 injector()->Reset(); 248 task_count = 0; 249 for (int i = 0; i < 10; ++i) { 250 loop()->PostDelayedTask( 251 FROM_HERE, NewRunnableFunction(IncrementInt, &task_count), 10*i); 252 } 253 // After all the previous tasks have executed, enqueue an event that will 254 // quit. 255 // This relies on the fact that delayed tasks are executed in delay order. 256 // That is verified in message_loop_unittest.cc. 257 loop()->PostDelayedTask( 258 FROM_HERE, NewRunnableMethod(injector(), &EventInjector::AddEvent, 259 10, NewQuitTask()), 150); 260 loop()->Run(); 261 ASSERT_EQ(10, task_count); 262 EXPECT_EQ(1, injector()->processed_events()); 263} 264 265TEST_F(MessagePumpGLibTest, TestEventsWhileWaitingForWork) { 266 // Tests that we process events while waiting for work. 267 // The event queue is empty at first. 268 for (int i = 0; i < 10; ++i) { 269 injector()->AddEvent(0, NULL); 270 } 271 // After all the events have been processed, post a task that will check that 272 // the events have been processed (note: the task executes after the event 273 // that posted it has been handled, so we expect 11 at that point). 274 Task* check_task = NewRunnableFunction(ExpectProcessedEvents, injector(), 11); 275 Task* posted_task = NewRunnableFunction(PostMessageLoopTask, 276 FROM_HERE, check_task); 277 injector()->AddEvent(10, posted_task); 278 279 // And then quit (relies on the condition tested by TestEventTaskInterleave). 280 injector()->AddEvent(10, NewQuitTask()); 281 loop()->Run(); 282 283 EXPECT_EQ(12, injector()->processed_events()); 284} 285 286namespace { 287 288// This class is a helper for the concurrent events / posted tasks test below. 289// It will quit the main loop once enough tasks and events have been processed, 290// while making sure there is always work to do and events in the queue. 291class ConcurrentHelper : public base::RefCounted<ConcurrentHelper> { 292 public: 293 explicit ConcurrentHelper(EventInjector* injector) 294 : injector_(injector), 295 event_count_(kStartingEventCount), 296 task_count_(kStartingTaskCount) { 297 } 298 299 void FromTask() { 300 if (task_count_ > 0) { 301 --task_count_; 302 } 303 if (task_count_ == 0 && event_count_ == 0) { 304 MessageLoop::current()->Quit(); 305 } else { 306 MessageLoop::current()->PostTask( 307 FROM_HERE, NewRunnableMethod(this, &ConcurrentHelper::FromTask)); 308 } 309 } 310 311 void FromEvent() { 312 if (event_count_ > 0) { 313 --event_count_; 314 } 315 if (task_count_ == 0 && event_count_ == 0) { 316 MessageLoop::current()->Quit(); 317 } else { 318 injector_->AddEvent( 319 0, NewRunnableMethod(this, &ConcurrentHelper::FromEvent)); 320 } 321 } 322 323 int event_count() const { return event_count_; } 324 int task_count() const { return task_count_; } 325 326 private: 327 friend class base::RefCounted<ConcurrentHelper>; 328 329 ~ConcurrentHelper() {} 330 331 static const int kStartingEventCount = 20; 332 static const int kStartingTaskCount = 20; 333 334 EventInjector* injector_; 335 int event_count_; 336 int task_count_; 337}; 338 339} // namespace 340 341TEST_F(MessagePumpGLibTest, TestConcurrentEventPostedTask) { 342 // Tests that posted tasks don't starve events, nor the opposite. 343 // We use the helper class above. We keep both event and posted task queues 344 // full, the helper verifies that both tasks and events get processed. 345 // If that is not the case, either event_count_ or task_count_ will not get 346 // to 0, and MessageLoop::Quit() will never be called. 347 scoped_refptr<ConcurrentHelper> helper = new ConcurrentHelper(injector()); 348 349 // Add 2 events to the queue to make sure it is always full (when we remove 350 // the event before processing it). 351 injector()->AddEvent( 352 0, NewRunnableMethod(helper.get(), &ConcurrentHelper::FromEvent)); 353 injector()->AddEvent( 354 0, NewRunnableMethod(helper.get(), &ConcurrentHelper::FromEvent)); 355 356 // Similarly post 2 tasks. 357 loop()->PostTask( 358 FROM_HERE, NewRunnableMethod(helper.get(), &ConcurrentHelper::FromTask)); 359 loop()->PostTask( 360 FROM_HERE, NewRunnableMethod(helper.get(), &ConcurrentHelper::FromTask)); 361 362 loop()->Run(); 363 EXPECT_EQ(0, helper->event_count()); 364 EXPECT_EQ(0, helper->task_count()); 365} 366 367namespace { 368 369void AddEventsAndDrainGLib(EventInjector* injector) { 370 // Add a couple of dummy events 371 injector->AddEvent(0, NULL); 372 injector->AddEvent(0, NULL); 373 // Then add an event that will quit the main loop. 374 injector->AddEvent(0, NewQuitTask()); 375 376 // Post a couple of dummy tasks 377 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableFunction(DoNothing)); 378 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableFunction(DoNothing)); 379 380 // Drain the events 381 while (g_main_context_pending(NULL)) { 382 g_main_context_iteration(NULL, FALSE); 383 } 384} 385 386} // namespace 387 388TEST_F(MessagePumpGLibTest, TestDrainingGLib) { 389 // Tests that draining events using GLib works. 390 loop()->PostTask( 391 FROM_HERE, NewRunnableFunction(AddEventsAndDrainGLib, injector())); 392 loop()->Run(); 393 394 EXPECT_EQ(3, injector()->processed_events()); 395} 396 397 398namespace { 399 400void AddEventsAndDrainGtk(EventInjector* injector) { 401 // Add a couple of dummy events 402 injector->AddEvent(0, NULL); 403 injector->AddEvent(0, NULL); 404 // Then add an event that will quit the main loop. 405 injector->AddEvent(0, NewQuitTask()); 406 407 // Post a couple of dummy tasks 408 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableFunction(DoNothing)); 409 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableFunction(DoNothing)); 410 411 // Drain the events 412 while (gtk_events_pending()) { 413 gtk_main_iteration(); 414 } 415} 416 417} // namespace 418 419TEST_F(MessagePumpGLibTest, TestDrainingGtk) { 420 // Tests that draining events using Gtk works. 421 loop()->PostTask( 422 FROM_HERE, NewRunnableFunction(AddEventsAndDrainGtk, injector())); 423 loop()->Run(); 424 425 EXPECT_EQ(3, injector()->processed_events()); 426} 427 428namespace { 429 430// Helper class that lets us run the GLib message loop. 431class GLibLoopRunner : public base::RefCounted<GLibLoopRunner> { 432 public: 433 GLibLoopRunner() : quit_(false) { } 434 435 void RunGLib() { 436 while (!quit_) { 437 g_main_context_iteration(NULL, TRUE); 438 } 439 } 440 441 void RunGtk() { 442 while (!quit_) { 443 gtk_main_iteration(); 444 } 445 } 446 447 void Quit() { 448 quit_ = true; 449 } 450 451 void Reset() { 452 quit_ = false; 453 } 454 455 private: 456 friend class base::RefCounted<GLibLoopRunner>; 457 458 ~GLibLoopRunner() {} 459 460 bool quit_; 461}; 462 463void TestGLibLoopInternal(EventInjector* injector) { 464 // Allow tasks to be processed from 'native' event loops. 465 MessageLoop::current()->SetNestableTasksAllowed(true); 466 scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner(); 467 468 int task_count = 0; 469 // Add a couple of dummy events 470 injector->AddEvent(0, NULL); 471 injector->AddEvent(0, NULL); 472 // Post a couple of dummy tasks 473 MessageLoop::current()->PostTask( 474 FROM_HERE, NewRunnableFunction(IncrementInt, &task_count)); 475 MessageLoop::current()->PostTask( 476 FROM_HERE, NewRunnableFunction(IncrementInt, &task_count)); 477 // Delayed events 478 injector->AddEvent(10, NULL); 479 injector->AddEvent(10, NULL); 480 // Delayed work 481 MessageLoop::current()->PostDelayedTask( 482 FROM_HERE, NewRunnableFunction(IncrementInt, &task_count), 30); 483 MessageLoop::current()->PostDelayedTask( 484 FROM_HERE, NewRunnableMethod(runner.get(), &GLibLoopRunner::Quit), 40); 485 486 // Run a nested, straight GLib message loop. 487 runner->RunGLib(); 488 489 ASSERT_EQ(3, task_count); 490 EXPECT_EQ(4, injector->processed_events()); 491 MessageLoop::current()->Quit(); 492} 493 494void TestGtkLoopInternal(EventInjector* injector) { 495 // Allow tasks to be processed from 'native' event loops. 496 MessageLoop::current()->SetNestableTasksAllowed(true); 497 scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner(); 498 499 int task_count = 0; 500 // Add a couple of dummy events 501 injector->AddEvent(0, NULL); 502 injector->AddEvent(0, NULL); 503 // Post a couple of dummy tasks 504 MessageLoop::current()->PostTask( 505 FROM_HERE, NewRunnableFunction(IncrementInt, &task_count)); 506 MessageLoop::current()->PostTask( 507 FROM_HERE, NewRunnableFunction(IncrementInt, &task_count)); 508 // Delayed events 509 injector->AddEvent(10, NULL); 510 injector->AddEvent(10, NULL); 511 // Delayed work 512 MessageLoop::current()->PostDelayedTask( 513 FROM_HERE, NewRunnableFunction(IncrementInt, &task_count), 30); 514 MessageLoop::current()->PostDelayedTask( 515 FROM_HERE, NewRunnableMethod(runner.get(), &GLibLoopRunner::Quit), 40); 516 517 // Run a nested, straight Gtk message loop. 518 runner->RunGtk(); 519 520 ASSERT_EQ(3, task_count); 521 EXPECT_EQ(4, injector->processed_events()); 522 MessageLoop::current()->Quit(); 523} 524 525} // namespace 526 527TEST_F(MessagePumpGLibTest, TestGLibLoop) { 528 // Tests that events and posted tasks are correctly exectuted if the message 529 // loop is not run by MessageLoop::Run() but by a straight GLib loop. 530 // Note that in this case we don't make strong guarantees about niceness 531 // between events and posted tasks. 532 loop()->PostTask(FROM_HERE, 533 NewRunnableFunction(TestGLibLoopInternal, injector())); 534 loop()->Run(); 535} 536 537TEST_F(MessagePumpGLibTest, TestGtkLoop) { 538 // Tests that events and posted tasks are correctly exectuted if the message 539 // loop is not run by MessageLoop::Run() but by a straight Gtk loop. 540 // Note that in this case we don't make strong guarantees about niceness 541 // between events and posted tasks. 542 loop()->PostTask(FROM_HERE, 543 NewRunnableFunction(TestGtkLoopInternal, injector())); 544 loop()->Run(); 545} 546