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