1 // Copyright (c) 2011 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.
5 #include "base/message_pump_glib.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/message_loop.h"
15 #include "base/threading/thread.h"
16 #include "testing/gtest/include/gtest/gtest.h"
20 // This class injects dummy "events" into the GLib loop. When "handled" these
21 // events can run tasks. This is intended to mock gtk events (the corresponding
22 // GLib source runs at the same priority).
25 EventInjector() : processed_events_(0) {
26 source_
= static_cast<Source
*>(g_source_new(&SourceFuncs
, sizeof(Source
)));
27 source_
->injector
= this;
28 g_source_attach(source_
, NULL
);
29 g_source_set_can_recurse(source_
, TRUE
);
33 g_source_destroy(source_
);
34 g_source_unref(source_
);
38 // If the queue is empty, block.
41 base::TimeDelta delta
= events_
[0].time
- base::Time::NowFromSystemTime();
42 return std::max(0, static_cast<int>(ceil(delta
.InMillisecondsF())));
48 return events_
[0].time
<= base::Time::NowFromSystemTime();
51 void HandleDispatch() {
54 Event event
= events_
[0];
55 events_
.erase(events_
.begin());
63 // Adds an event to the queue. When "handled", executes |task|.
64 // delay_ms is relative to the last event if any, or to Now() otherwise.
65 void AddEvent(int delay_ms
, Task
* task
) {
67 if (!events_
.empty()) {
68 last_time
= (events_
.end()-1)->time
;
70 last_time
= base::Time::NowFromSystemTime();
72 base::Time future
= last_time
+ base::TimeDelta::FromMilliseconds(delay_ms
);
73 EventInjector::Event event
= { future
, task
};
74 events_
.push_back(event
);
78 processed_events_
= 0;
82 int processed_events() const { return processed_events_
; }
90 struct Source
: public GSource
{
91 EventInjector
* injector
;
94 static gboolean
Prepare(GSource
* source
, gint
* timeout_ms
) {
95 *timeout_ms
= static_cast<Source
*>(source
)->injector
->HandlePrepare();
99 static gboolean
Check(GSource
* source
) {
100 return static_cast<Source
*>(source
)->injector
->HandleCheck();
103 static gboolean
Dispatch(GSource
* source
,
104 GSourceFunc unused_func
,
105 gpointer unused_data
) {
106 static_cast<Source
*>(source
)->injector
->HandleDispatch();
111 std::vector
<Event
> events_
;
112 int processed_events_
;
113 static GSourceFuncs SourceFuncs
;
114 DISALLOW_COPY_AND_ASSIGN(EventInjector
);
117 GSourceFuncs
EventInjector::SourceFuncs
= {
118 EventInjector::Prepare
,
119 EventInjector::Check
,
120 EventInjector::Dispatch
,
124 // Does nothing. This function can be called from a task.
128 void IncrementInt(int *value
) {
132 // Checks how many events have been processed by the injector.
133 void ExpectProcessedEvents(EventInjector
* injector
, int count
) {
134 EXPECT_EQ(injector
->processed_events(), count
);
137 // Quits the current message loop.
138 void QuitMessageLoop() {
139 MessageLoop::current()->Quit();
142 // Returns a new task that quits the main loop.
143 Task
* NewQuitTask() {
144 return NewRunnableFunction(QuitMessageLoop
);
147 // Posts a task on the current message loop.
148 void PostMessageLoopTask(const tracked_objects::Location
& from_here
,
150 MessageLoop::current()->PostTask(from_here
, task
);
154 class MessagePumpGLibTest
: public testing::Test
{
156 MessagePumpGLibTest() : loop_(NULL
), injector_(NULL
) { }
158 virtual void SetUp() {
159 loop_
= new MessageLoop(MessageLoop::TYPE_UI
);
160 injector_
= new EventInjector();
163 virtual void TearDown() {
170 MessageLoop
* loop() const { return loop_
; }
171 EventInjector
* injector() const { return injector_
; }
175 EventInjector
* injector_
;
176 DISALLOW_COPY_AND_ASSIGN(MessagePumpGLibTest
);
181 // EventInjector is expected to always live longer than the runnable methods.
182 DISABLE_RUNNABLE_METHOD_REFCOUNT(EventInjector
);
184 TEST_F(MessagePumpGLibTest
, TestQuit
) {
185 // Checks that Quit works and that the basic infrastructure is working.
188 loop()->PostTask(FROM_HERE
, NewQuitTask());
190 EXPECT_EQ(0, injector()->processed_events());
193 // Quit from an event
194 injector()->AddEvent(0, NewQuitTask());
196 EXPECT_EQ(1, injector()->processed_events());
199 TEST_F(MessagePumpGLibTest
, TestEventTaskInterleave
) {
200 // Checks that tasks posted by events are executed before the next event if
201 // the posted task queue is empty.
202 // MessageLoop doesn't make strong guarantees that it is the case, but the
203 // current implementation ensures it and the tests below rely on it.
204 // If changes cause this test to fail, it is reasonable to change it, but
205 // TestWorkWhileWaitingForEvents and TestEventsWhileWaitingForWork have to be
206 // changed accordingly, otherwise they can become flaky.
207 injector()->AddEvent(0, NewRunnableFunction(DoNothing
));
208 Task
* check_task
= NewRunnableFunction(ExpectProcessedEvents
, injector(), 2);
209 Task
* posted_task
= NewRunnableFunction(PostMessageLoopTask
,
210 FROM_HERE
, check_task
);
211 injector()->AddEvent(0, posted_task
);
212 injector()->AddEvent(0, NewRunnableFunction(DoNothing
));
213 injector()->AddEvent(0, NewQuitTask());
215 EXPECT_EQ(4, injector()->processed_events());
218 injector()->AddEvent(0, NewRunnableFunction(DoNothing
));
219 check_task
= NewRunnableFunction(ExpectProcessedEvents
, injector(), 2);
220 posted_task
= NewRunnableFunction(PostMessageLoopTask
, FROM_HERE
, check_task
);
221 injector()->AddEvent(0, posted_task
);
222 injector()->AddEvent(10, NewRunnableFunction(DoNothing
));
223 injector()->AddEvent(0, NewQuitTask());
225 EXPECT_EQ(4, injector()->processed_events());
228 TEST_F(MessagePumpGLibTest
, TestWorkWhileWaitingForEvents
) {
230 // Tests that we process tasks while waiting for new events.
231 // The event queue is empty at first.
232 for (int i
= 0; i
< 10; ++i
) {
233 loop()->PostTask(FROM_HERE
, NewRunnableFunction(IncrementInt
, &task_count
));
235 // After all the previous tasks have executed, enqueue an event that will
238 FROM_HERE
, NewRunnableMethod(injector(), &EventInjector::AddEvent
,
241 ASSERT_EQ(10, task_count
);
242 EXPECT_EQ(1, injector()->processed_events());
244 // Tests that we process delayed tasks while waiting for new events.
247 for (int i
= 0; i
< 10; ++i
) {
248 loop()->PostDelayedTask(
249 FROM_HERE
, NewRunnableFunction(IncrementInt
, &task_count
), 10*i
);
251 // After all the previous tasks have executed, enqueue an event that will
253 // This relies on the fact that delayed tasks are executed in delay order.
254 // That is verified in message_loop_unittest.cc.
255 loop()->PostDelayedTask(
256 FROM_HERE
, NewRunnableMethod(injector(), &EventInjector::AddEvent
,
257 10, NewQuitTask()), 150);
259 ASSERT_EQ(10, task_count
);
260 EXPECT_EQ(1, injector()->processed_events());
263 TEST_F(MessagePumpGLibTest
, TestEventsWhileWaitingForWork
) {
264 // Tests that we process events while waiting for work.
265 // The event queue is empty at first.
266 for (int i
= 0; i
< 10; ++i
) {
267 injector()->AddEvent(0, NULL
);
269 // After all the events have been processed, post a task that will check that
270 // the events have been processed (note: the task executes after the event
271 // that posted it has been handled, so we expect 11 at that point).
272 Task
* check_task
= NewRunnableFunction(ExpectProcessedEvents
, injector(), 11);
273 Task
* posted_task
= NewRunnableFunction(PostMessageLoopTask
,
274 FROM_HERE
, check_task
);
275 injector()->AddEvent(10, posted_task
);
277 // And then quit (relies on the condition tested by TestEventTaskInterleave).
278 injector()->AddEvent(10, NewQuitTask());
281 EXPECT_EQ(12, injector()->processed_events());
286 // This class is a helper for the concurrent events / posted tasks test below.
287 // It will quit the main loop once enough tasks and events have been processed,
288 // while making sure there is always work to do and events in the queue.
289 class ConcurrentHelper
: public base::RefCounted
<ConcurrentHelper
> {
291 explicit ConcurrentHelper(EventInjector
* injector
)
292 : injector_(injector
),
293 event_count_(kStartingEventCount
),
294 task_count_(kStartingTaskCount
) {
298 if (task_count_
> 0) {
301 if (task_count_
== 0 && event_count_
== 0) {
302 MessageLoop::current()->Quit();
304 MessageLoop::current()->PostTask(
305 FROM_HERE
, NewRunnableMethod(this, &ConcurrentHelper::FromTask
));
310 if (event_count_
> 0) {
313 if (task_count_
== 0 && event_count_
== 0) {
314 MessageLoop::current()->Quit();
317 0, NewRunnableMethod(this, &ConcurrentHelper::FromEvent
));
321 int event_count() const { return event_count_
; }
322 int task_count() const { return task_count_
; }
325 friend class base::RefCounted
<ConcurrentHelper
>;
327 ~ConcurrentHelper() {}
329 static const int kStartingEventCount
= 20;
330 static const int kStartingTaskCount
= 20;
332 EventInjector
* injector_
;
339 TEST_F(MessagePumpGLibTest
, TestConcurrentEventPostedTask
) {
340 // Tests that posted tasks don't starve events, nor the opposite.
341 // We use the helper class above. We keep both event and posted task queues
342 // full, the helper verifies that both tasks and events get processed.
343 // If that is not the case, either event_count_ or task_count_ will not get
344 // to 0, and MessageLoop::Quit() will never be called.
345 scoped_refptr
<ConcurrentHelper
> helper
= new ConcurrentHelper(injector());
347 // Add 2 events to the queue to make sure it is always full (when we remove
348 // the event before processing it).
349 injector()->AddEvent(
350 0, NewRunnableMethod(helper
.get(), &ConcurrentHelper::FromEvent
));
351 injector()->AddEvent(
352 0, NewRunnableMethod(helper
.get(), &ConcurrentHelper::FromEvent
));
354 // Similarly post 2 tasks.
356 FROM_HERE
, NewRunnableMethod(helper
.get(), &ConcurrentHelper::FromTask
));
358 FROM_HERE
, NewRunnableMethod(helper
.get(), &ConcurrentHelper::FromTask
));
361 EXPECT_EQ(0, helper
->event_count());
362 EXPECT_EQ(0, helper
->task_count());
367 void AddEventsAndDrainGLib(EventInjector
* injector
) {
368 // Add a couple of dummy events
369 injector
->AddEvent(0, NULL
);
370 injector
->AddEvent(0, NULL
);
371 // Then add an event that will quit the main loop.
372 injector
->AddEvent(0, NewQuitTask());
374 // Post a couple of dummy tasks
375 MessageLoop::current()->PostTask(FROM_HERE
, NewRunnableFunction(DoNothing
));
376 MessageLoop::current()->PostTask(FROM_HERE
, NewRunnableFunction(DoNothing
));
379 while (g_main_context_pending(NULL
)) {
380 g_main_context_iteration(NULL
, FALSE
);
386 TEST_F(MessagePumpGLibTest
, TestDrainingGLib
) {
387 // Tests that draining events using GLib works.
389 FROM_HERE
, NewRunnableFunction(AddEventsAndDrainGLib
, injector()));
392 EXPECT_EQ(3, injector()->processed_events());
398 void AddEventsAndDrainGtk(EventInjector
* injector
) {
399 // Add a couple of dummy events
400 injector
->AddEvent(0, NULL
);
401 injector
->AddEvent(0, NULL
);
402 // Then add an event that will quit the main loop.
403 injector
->AddEvent(0, NewQuitTask());
405 // Post a couple of dummy tasks
406 MessageLoop::current()->PostTask(FROM_HERE
, NewRunnableFunction(DoNothing
));
407 MessageLoop::current()->PostTask(FROM_HERE
, NewRunnableFunction(DoNothing
));
410 while (gtk_events_pending()) {
411 gtk_main_iteration();
417 TEST_F(MessagePumpGLibTest
, TestDrainingGtk
) {
418 // Tests that draining events using Gtk works.
420 FROM_HERE
, NewRunnableFunction(AddEventsAndDrainGtk
, injector()));
423 EXPECT_EQ(3, injector()->processed_events());
428 // Helper class that lets us run the GLib message loop.
429 class GLibLoopRunner
: public base::RefCounted
<GLibLoopRunner
> {
431 GLibLoopRunner() : quit_(false) { }
435 g_main_context_iteration(NULL
, TRUE
);
441 gtk_main_iteration();
454 friend class base::RefCounted
<GLibLoopRunner
>;
461 void TestGLibLoopInternal(EventInjector
* injector
) {
462 // Allow tasks to be processed from 'native' event loops.
463 MessageLoop::current()->SetNestableTasksAllowed(true);
464 scoped_refptr
<GLibLoopRunner
> runner
= new GLibLoopRunner();
467 // Add a couple of dummy events
468 injector
->AddEvent(0, NULL
);
469 injector
->AddEvent(0, NULL
);
470 // Post a couple of dummy tasks
471 MessageLoop::current()->PostTask(
472 FROM_HERE
, NewRunnableFunction(IncrementInt
, &task_count
));
473 MessageLoop::current()->PostTask(
474 FROM_HERE
, NewRunnableFunction(IncrementInt
, &task_count
));
476 injector
->AddEvent(10, NULL
);
477 injector
->AddEvent(10, NULL
);
479 MessageLoop::current()->PostDelayedTask(
480 FROM_HERE
, NewRunnableFunction(IncrementInt
, &task_count
), 30);
481 MessageLoop::current()->PostDelayedTask(
482 FROM_HERE
, NewRunnableMethod(runner
.get(), &GLibLoopRunner::Quit
), 40);
484 // Run a nested, straight GLib message loop.
487 ASSERT_EQ(3, task_count
);
488 EXPECT_EQ(4, injector
->processed_events());
489 MessageLoop::current()->Quit();
492 void TestGtkLoopInternal(EventInjector
* injector
) {
493 // Allow tasks to be processed from 'native' event loops.
494 MessageLoop::current()->SetNestableTasksAllowed(true);
495 scoped_refptr
<GLibLoopRunner
> runner
= new GLibLoopRunner();
498 // Add a couple of dummy events
499 injector
->AddEvent(0, NULL
);
500 injector
->AddEvent(0, NULL
);
501 // Post a couple of dummy tasks
502 MessageLoop::current()->PostTask(
503 FROM_HERE
, NewRunnableFunction(IncrementInt
, &task_count
));
504 MessageLoop::current()->PostTask(
505 FROM_HERE
, NewRunnableFunction(IncrementInt
, &task_count
));
507 injector
->AddEvent(10, NULL
);
508 injector
->AddEvent(10, NULL
);
510 MessageLoop::current()->PostDelayedTask(
511 FROM_HERE
, NewRunnableFunction(IncrementInt
, &task_count
), 30);
512 MessageLoop::current()->PostDelayedTask(
513 FROM_HERE
, NewRunnableMethod(runner
.get(), &GLibLoopRunner::Quit
), 40);
515 // Run a nested, straight Gtk message loop.
518 ASSERT_EQ(3, task_count
);
519 EXPECT_EQ(4, injector
->processed_events());
520 MessageLoop::current()->Quit();
525 TEST_F(MessagePumpGLibTest
, TestGLibLoop
) {
526 // Tests that events and posted tasks are correctly exectuted if the message
527 // loop is not run by MessageLoop::Run() but by a straight GLib loop.
528 // Note that in this case we don't make strong guarantees about niceness
529 // between events and posted tasks.
530 loop()->PostTask(FROM_HERE
,
531 NewRunnableFunction(TestGLibLoopInternal
, injector()));
535 TEST_F(MessagePumpGLibTest
, TestGtkLoop
) {
536 // Tests that events and posted tasks are correctly exectuted if the message
537 // loop is not run by MessageLoop::Run() but by a straight Gtk loop.
538 // Note that in this case we don't make strong guarantees about niceness
539 // between events and posted tasks.
540 loop()->PostTask(FROM_HERE
,
541 NewRunnableFunction(TestGtkLoopInternal
, injector()));