Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / base / message_loop / message_pump_glib_unittest.cc
blob7ddd4f08a0426922d4a77ec60032cc58f7b32239
1 // Copyright (c) 2012 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_loop/message_pump_glib.h"
7 #include <glib.h>
8 #include <math.h>
10 #include <algorithm>
11 #include <vector>
13 #include "base/bind.h"
14 #include "base/bind_helpers.h"
15 #include "base/callback.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/run_loop.h"
19 #include "base/threading/thread.h"
20 #include "testing/gtest/include/gtest/gtest.h"
22 namespace base {
23 namespace {
25 // This class injects dummy "events" into the GLib loop. When "handled" these
26 // events can run tasks. This is intended to mock gtk events (the corresponding
27 // GLib source runs at the same priority).
28 class EventInjector {
29 public:
30 EventInjector() : processed_events_(0) {
31 source_ = static_cast<Source*>(g_source_new(&SourceFuncs, sizeof(Source)));
32 source_->injector = this;
33 g_source_attach(source_, NULL);
34 g_source_set_can_recurse(source_, TRUE);
37 ~EventInjector() {
38 g_source_destroy(source_);
39 g_source_unref(source_);
42 int HandlePrepare() {
43 // If the queue is empty, block.
44 if (events_.empty())
45 return -1;
46 TimeDelta delta = events_[0].time - Time::NowFromSystemTime();
47 return std::max(0, static_cast<int>(ceil(delta.InMillisecondsF())));
50 bool HandleCheck() {
51 if (events_.empty())
52 return false;
53 return events_[0].time <= Time::NowFromSystemTime();
56 void HandleDispatch() {
57 if (events_.empty())
58 return;
59 Event event = events_[0];
60 events_.erase(events_.begin());
61 ++processed_events_;
62 if (!event.callback.is_null())
63 event.callback.Run();
64 else if (!event.task.is_null())
65 event.task.Run();
68 // Adds an event to the queue. When "handled", executes |callback|.
69 // delay_ms is relative to the last event if any, or to Now() otherwise.
70 void AddEvent(int delay_ms, const Closure& callback) {
71 AddEventHelper(delay_ms, callback, Closure());
74 void AddDummyEvent(int delay_ms) {
75 AddEventHelper(delay_ms, Closure(), Closure());
78 void AddEventAsTask(int delay_ms, const Closure& task) {
79 AddEventHelper(delay_ms, Closure(), task);
82 void Reset() {
83 processed_events_ = 0;
84 events_.clear();
87 int processed_events() const { return processed_events_; }
89 private:
90 struct Event {
91 Time time;
92 Closure callback;
93 Closure task;
96 struct Source : public GSource {
97 EventInjector* injector;
100 void AddEventHelper(
101 int delay_ms, const Closure& callback, const Closure& task) {
102 Time last_time;
103 if (!events_.empty())
104 last_time = (events_.end()-1)->time;
105 else
106 last_time = Time::NowFromSystemTime();
108 Time future = last_time + TimeDelta::FromMilliseconds(delay_ms);
109 EventInjector::Event event = {future, callback, task};
110 events_.push_back(event);
113 static gboolean Prepare(GSource* source, gint* timeout_ms) {
114 *timeout_ms = static_cast<Source*>(source)->injector->HandlePrepare();
115 return FALSE;
118 static gboolean Check(GSource* source) {
119 return static_cast<Source*>(source)->injector->HandleCheck();
122 static gboolean Dispatch(GSource* source,
123 GSourceFunc unused_func,
124 gpointer unused_data) {
125 static_cast<Source*>(source)->injector->HandleDispatch();
126 return TRUE;
129 Source* source_;
130 std::vector<Event> events_;
131 int processed_events_;
132 static GSourceFuncs SourceFuncs;
133 DISALLOW_COPY_AND_ASSIGN(EventInjector);
136 GSourceFuncs EventInjector::SourceFuncs = {
137 EventInjector::Prepare,
138 EventInjector::Check,
139 EventInjector::Dispatch,
140 NULL
143 void IncrementInt(int *value) {
144 ++*value;
147 // Checks how many events have been processed by the injector.
148 void ExpectProcessedEvents(EventInjector* injector, int count) {
149 EXPECT_EQ(injector->processed_events(), count);
152 // Posts a task on the current message loop.
153 void PostMessageLoopTask(const tracked_objects::Location& from_here,
154 const Closure& task) {
155 MessageLoop::current()->PostTask(from_here, task);
158 // Test fixture.
159 class MessagePumpGLibTest : public testing::Test {
160 public:
161 MessagePumpGLibTest() : loop_(NULL), injector_(NULL) { }
163 // Overridden from testing::Test:
164 void SetUp() override {
165 loop_ = new MessageLoop(MessageLoop::TYPE_UI);
166 injector_ = new EventInjector();
168 void TearDown() override {
169 delete injector_;
170 injector_ = NULL;
171 delete loop_;
172 loop_ = NULL;
175 MessageLoop* loop() const { return loop_; }
176 EventInjector* injector() const { return injector_; }
178 private:
179 MessageLoop* loop_;
180 EventInjector* injector_;
181 DISALLOW_COPY_AND_ASSIGN(MessagePumpGLibTest);
184 } // namespace
186 TEST_F(MessagePumpGLibTest, TestQuit) {
187 // Checks that Quit works and that the basic infrastructure is working.
189 // Quit from a task
190 RunLoop().RunUntilIdle();
191 EXPECT_EQ(0, injector()->processed_events());
193 injector()->Reset();
194 // Quit from an event
195 injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
196 loop()->Run();
197 EXPECT_EQ(1, injector()->processed_events());
200 TEST_F(MessagePumpGLibTest, TestEventTaskInterleave) {
201 // Checks that tasks posted by events are executed before the next event if
202 // the posted task queue is empty.
203 // MessageLoop doesn't make strong guarantees that it is the case, but the
204 // current implementation ensures it and the tests below rely on it.
205 // If changes cause this test to fail, it is reasonable to change it, but
206 // TestWorkWhileWaitingForEvents and TestEventsWhileWaitingForWork have to be
207 // changed accordingly, otherwise they can become flaky.
208 injector()->AddEventAsTask(0, Bind(&DoNothing));
209 Closure check_task =
210 Bind(&ExpectProcessedEvents, Unretained(injector()), 2);
211 Closure posted_task =
212 Bind(&PostMessageLoopTask, FROM_HERE, check_task);
213 injector()->AddEventAsTask(0, posted_task);
214 injector()->AddEventAsTask(0, Bind(&DoNothing));
215 injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
216 loop()->Run();
217 EXPECT_EQ(4, injector()->processed_events());
219 injector()->Reset();
220 injector()->AddEventAsTask(0, Bind(&DoNothing));
221 check_task =
222 Bind(&ExpectProcessedEvents, Unretained(injector()), 2);
223 posted_task = Bind(&PostMessageLoopTask, FROM_HERE, check_task);
224 injector()->AddEventAsTask(0, posted_task);
225 injector()->AddEventAsTask(10, Bind(&DoNothing));
226 injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
227 loop()->Run();
228 EXPECT_EQ(4, injector()->processed_events());
231 TEST_F(MessagePumpGLibTest, TestWorkWhileWaitingForEvents) {
232 int task_count = 0;
233 // Tests that we process tasks while waiting for new events.
234 // The event queue is empty at first.
235 for (int i = 0; i < 10; ++i) {
236 loop()->PostTask(FROM_HERE, Bind(&IncrementInt, &task_count));
238 // After all the previous tasks have executed, enqueue an event that will
239 // quit.
240 loop()->PostTask(
241 FROM_HERE,
242 Bind(&EventInjector::AddEvent, Unretained(injector()), 0,
243 MessageLoop::QuitWhenIdleClosure()));
244 loop()->Run();
245 ASSERT_EQ(10, task_count);
246 EXPECT_EQ(1, injector()->processed_events());
248 // Tests that we process delayed tasks while waiting for new events.
249 injector()->Reset();
250 task_count = 0;
251 for (int i = 0; i < 10; ++i) {
252 loop()->PostDelayedTask(
253 FROM_HERE,
254 Bind(&IncrementInt, &task_count),
255 TimeDelta::FromMilliseconds(10*i));
257 // After all the previous tasks have executed, enqueue an event that will
258 // quit.
259 // This relies on the fact that delayed tasks are executed in delay order.
260 // That is verified in message_loop_unittest.cc.
261 loop()->PostDelayedTask(
262 FROM_HERE,
263 Bind(&EventInjector::AddEvent, Unretained(injector()), 10,
264 MessageLoop::QuitWhenIdleClosure()),
265 TimeDelta::FromMilliseconds(150));
266 loop()->Run();
267 ASSERT_EQ(10, task_count);
268 EXPECT_EQ(1, injector()->processed_events());
271 TEST_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()->AddDummyEvent(0);
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 Closure check_task =
281 Bind(&ExpectProcessedEvents, Unretained(injector()), 11);
282 Closure posted_task =
283 Bind(&PostMessageLoopTask, FROM_HERE, check_task);
284 injector()->AddEventAsTask(10, posted_task);
286 // And then quit (relies on the condition tested by TestEventTaskInterleave).
287 injector()->AddEvent(10, MessageLoop::QuitWhenIdleClosure());
288 loop()->Run();
290 EXPECT_EQ(12, injector()->processed_events());
293 namespace {
295 // This class is a helper for the concurrent events / posted tasks test below.
296 // It will quit the main loop once enough tasks and events have been processed,
297 // while making sure there is always work to do and events in the queue.
298 class ConcurrentHelper : public RefCounted<ConcurrentHelper> {
299 public:
300 explicit ConcurrentHelper(EventInjector* injector)
301 : injector_(injector),
302 event_count_(kStartingEventCount),
303 task_count_(kStartingTaskCount) {
306 void FromTask() {
307 if (task_count_ > 0) {
308 --task_count_;
310 if (task_count_ == 0 && event_count_ == 0) {
311 MessageLoop::current()->QuitWhenIdle();
312 } else {
313 MessageLoop::current()->PostTask(
314 FROM_HERE, Bind(&ConcurrentHelper::FromTask, this));
318 void FromEvent() {
319 if (event_count_ > 0) {
320 --event_count_;
322 if (task_count_ == 0 && event_count_ == 0) {
323 MessageLoop::current()->QuitWhenIdle();
324 } else {
325 injector_->AddEventAsTask(
326 0, Bind(&ConcurrentHelper::FromEvent, this));
330 int event_count() const { return event_count_; }
331 int task_count() const { return task_count_; }
333 private:
334 friend class RefCounted<ConcurrentHelper>;
336 ~ConcurrentHelper() {}
338 static const int kStartingEventCount = 20;
339 static const int kStartingTaskCount = 20;
341 EventInjector* injector_;
342 int event_count_;
343 int task_count_;
346 } // namespace
348 TEST_F(MessagePumpGLibTest, TestConcurrentEventPostedTask) {
349 // Tests that posted tasks don't starve events, nor the opposite.
350 // We use the helper class above. We keep both event and posted task queues
351 // full, the helper verifies that both tasks and events get processed.
352 // If that is not the case, either event_count_ or task_count_ will not get
353 // to 0, and MessageLoop::QuitWhenIdle() will never be called.
354 scoped_refptr<ConcurrentHelper> helper = new ConcurrentHelper(injector());
356 // Add 2 events to the queue to make sure it is always full (when we remove
357 // the event before processing it).
358 injector()->AddEventAsTask(
359 0, Bind(&ConcurrentHelper::FromEvent, helper.get()));
360 injector()->AddEventAsTask(
361 0, Bind(&ConcurrentHelper::FromEvent, helper.get()));
363 // Similarly post 2 tasks.
364 loop()->PostTask(
365 FROM_HERE, Bind(&ConcurrentHelper::FromTask, helper.get()));
366 loop()->PostTask(
367 FROM_HERE, Bind(&ConcurrentHelper::FromTask, helper.get()));
369 loop()->Run();
370 EXPECT_EQ(0, helper->event_count());
371 EXPECT_EQ(0, helper->task_count());
374 namespace {
376 void AddEventsAndDrainGLib(EventInjector* injector) {
377 // Add a couple of dummy events
378 injector->AddDummyEvent(0);
379 injector->AddDummyEvent(0);
380 // Then add an event that will quit the main loop.
381 injector->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
383 // Post a couple of dummy tasks
384 MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing));
385 MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing));
387 // Drain the events
388 while (g_main_context_pending(NULL)) {
389 g_main_context_iteration(NULL, FALSE);
393 } // namespace
395 TEST_F(MessagePumpGLibTest, TestDrainingGLib) {
396 // Tests that draining events using GLib works.
397 loop()->PostTask(
398 FROM_HERE,
399 Bind(&AddEventsAndDrainGLib, Unretained(injector())));
400 loop()->Run();
402 EXPECT_EQ(3, injector()->processed_events());
405 namespace {
407 // Helper class that lets us run the GLib message loop.
408 class GLibLoopRunner : public RefCounted<GLibLoopRunner> {
409 public:
410 GLibLoopRunner() : quit_(false) { }
412 void RunGLib() {
413 while (!quit_) {
414 g_main_context_iteration(NULL, TRUE);
418 void RunLoop() {
419 while (!quit_) {
420 g_main_context_iteration(NULL, TRUE);
424 void Quit() {
425 quit_ = true;
428 void Reset() {
429 quit_ = false;
432 private:
433 friend class RefCounted<GLibLoopRunner>;
435 ~GLibLoopRunner() {}
437 bool quit_;
440 void TestGLibLoopInternal(EventInjector* injector) {
441 // Allow tasks to be processed from 'native' event loops.
442 MessageLoop::current()->SetNestableTasksAllowed(true);
443 scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
445 int task_count = 0;
446 // Add a couple of dummy events
447 injector->AddDummyEvent(0);
448 injector->AddDummyEvent(0);
449 // Post a couple of dummy tasks
450 MessageLoop::current()->PostTask(
451 FROM_HERE, Bind(&IncrementInt, &task_count));
452 MessageLoop::current()->PostTask(
453 FROM_HERE, Bind(&IncrementInt, &task_count));
454 // Delayed events
455 injector->AddDummyEvent(10);
456 injector->AddDummyEvent(10);
457 // Delayed work
458 MessageLoop::current()->PostDelayedTask(
459 FROM_HERE,
460 Bind(&IncrementInt, &task_count),
461 TimeDelta::FromMilliseconds(30));
462 MessageLoop::current()->PostDelayedTask(
463 FROM_HERE,
464 Bind(&GLibLoopRunner::Quit, runner.get()),
465 TimeDelta::FromMilliseconds(40));
467 // Run a nested, straight GLib message loop.
468 runner->RunGLib();
470 ASSERT_EQ(3, task_count);
471 EXPECT_EQ(4, injector->processed_events());
472 MessageLoop::current()->QuitWhenIdle();
475 void TestGtkLoopInternal(EventInjector* injector) {
476 // Allow tasks to be processed from 'native' event loops.
477 MessageLoop::current()->SetNestableTasksAllowed(true);
478 scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
480 int task_count = 0;
481 // Add a couple of dummy events
482 injector->AddDummyEvent(0);
483 injector->AddDummyEvent(0);
484 // Post a couple of dummy tasks
485 MessageLoop::current()->PostTask(
486 FROM_HERE, Bind(&IncrementInt, &task_count));
487 MessageLoop::current()->PostTask(
488 FROM_HERE, Bind(&IncrementInt, &task_count));
489 // Delayed events
490 injector->AddDummyEvent(10);
491 injector->AddDummyEvent(10);
492 // Delayed work
493 MessageLoop::current()->PostDelayedTask(
494 FROM_HERE,
495 Bind(&IncrementInt, &task_count),
496 TimeDelta::FromMilliseconds(30));
497 MessageLoop::current()->PostDelayedTask(
498 FROM_HERE,
499 Bind(&GLibLoopRunner::Quit, runner.get()),
500 TimeDelta::FromMilliseconds(40));
502 // Run a nested, straight Gtk message loop.
503 runner->RunLoop();
505 ASSERT_EQ(3, task_count);
506 EXPECT_EQ(4, injector->processed_events());
507 MessageLoop::current()->QuitWhenIdle();
510 } // namespace
512 TEST_F(MessagePumpGLibTest, TestGLibLoop) {
513 // Tests that events and posted tasks are correctly executed if the message
514 // loop is not run by MessageLoop::Run() but by a straight GLib loop.
515 // Note that in this case we don't make strong guarantees about niceness
516 // between events and posted tasks.
517 loop()->PostTask(
518 FROM_HERE,
519 Bind(&TestGLibLoopInternal, Unretained(injector())));
520 loop()->Run();
523 TEST_F(MessagePumpGLibTest, TestGtkLoop) {
524 // Tests that events and posted tasks are correctly executed if the message
525 // loop is not run by MessageLoop::Run() but by a straight Gtk loop.
526 // Note that in this case we don't make strong guarantees about niceness
527 // between events and posted tasks.
528 loop()->PostTask(
529 FROM_HERE,
530 Bind(&TestGtkLoopInternal, Unretained(injector())));
531 loop()->Run();
534 } // namespace base