blob: 451c6f5caa244de927b4d123113951d19bd5c1a8 [file] [log] [blame]
ajwong@chromium.org22a8a0d2012-01-04 09:57:39 +09001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
deanm@chromium.orgbec47f42009-06-15 19:30:44 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
brettw@chromium.org710ecb92013-06-19 05:27:52 +09005#include "base/message_loop/message_pump_glib.h"
deanm@chromium.orgbec47f42009-06-15 19:30:44 +09006
erg@chromium.orgb09a8892012-06-29 04:57:26 +09007#include <glib.h>
agl@chromium.org927bd072009-06-16 04:53:08 +09008#include <math.h>
9
deanm@chromium.orgbec47f42009-06-15 19:30:44 +090010#include <algorithm>
11#include <vector>
agl@chromium.org927bd072009-06-16 04:53:08 +090012
jhawkins@chromium.org598ca1c2012-01-01 11:14:47 +090013#include "base/bind.h"
14#include "base/bind_helpers.h"
dcheng@chromium.org70297102011-12-10 08:06:02 +090015#include "base/callback.h"
avia6a6a682015-12-27 07:15:14 +090016#include "base/macros.h"
levin@chromium.org5c528682011-03-28 10:54:15 +090017#include "base/memory/ref_counted.h"
avi@chromium.orga043a862013-07-18 17:12:40 +090018#include "base/message_loop/message_loop.h"
tfarina@chromium.org7d4a0ec2013-02-07 01:56:19 +090019#include "base/run_loop.h"
fdoray851719c2016-08-26 00:36:37 +090020#include "base/single_thread_task_runner.h"
brettw@chromium.org5b5f5e02011-01-01 10:01:06 +090021#include "base/threading/thread.h"
fdoray96ab6552016-07-19 08:47:16 +090022#include "base/threading/thread_task_runner_handle.h"
deanm@chromium.orgbec47f42009-06-15 19:30:44 +090023#include "testing/gtest/include/gtest/gtest.h"
24
tfarina@chromium.org7d4a0ec2013-02-07 01:56:19 +090025namespace base {
deanm@chromium.orgbec47f42009-06-15 19:30:44 +090026namespace {
27
28// This class injects dummy "events" into the GLib loop. When "handled" these
29// events can run tasks. This is intended to mock gtk events (the corresponding
30// GLib source runs at the same priority).
31class EventInjector {
32 public:
33 EventInjector() : processed_events_(0) {
34 source_ = static_cast<Source*>(g_source_new(&SourceFuncs, sizeof(Source)));
35 source_->injector = this;
Ivan Kotenkove88f3462017-11-08 21:37:33 +090036 g_source_attach(source_, nullptr);
deanm@chromium.orgbec47f42009-06-15 19:30:44 +090037 g_source_set_can_recurse(source_, TRUE);
38 }
39
40 ~EventInjector() {
41 g_source_destroy(source_);
42 g_source_unref(source_);
43 }
44
45 int HandlePrepare() {
46 // If the queue is empty, block.
47 if (events_.empty())
48 return -1;
brettw@chromium.org710ecb92013-06-19 05:27:52 +090049 TimeDelta delta = events_[0].time - Time::NowFromSystemTime();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +090050 return std::max(0, static_cast<int>(ceil(delta.InMillisecondsF())));
51 }
52
53 bool HandleCheck() {
54 if (events_.empty())
55 return false;
brettw@chromium.org710ecb92013-06-19 05:27:52 +090056 return events_[0].time <= Time::NowFromSystemTime();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +090057 }
58
59 void HandleDispatch() {
60 if (events_.empty())
61 return;
tzikc372ed82017-04-18 16:01:15 +090062 Event event = std::move(events_[0]);
deanm@chromium.orgbec47f42009-06-15 19:30:44 +090063 events_.erase(events_.begin());
64 ++processed_events_;
jhawkins@chromium.org598ca1c2012-01-01 11:14:47 +090065 if (!event.callback.is_null())
tzikc372ed82017-04-18 16:01:15 +090066 std::move(event.callback).Run();
jhawkins@chromium.org598ca1c2012-01-01 11:14:47 +090067 else if (!event.task.is_null())
tzikc372ed82017-04-18 16:01:15 +090068 std::move(event.task).Run();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +090069 }
70
dcheng@chromium.org70297102011-12-10 08:06:02 +090071 // Adds an event to the queue. When "handled", executes |callback|.
deanm@chromium.orgbec47f42009-06-15 19:30:44 +090072 // delay_ms is relative to the last event if any, or to Now() otherwise.
tzikc372ed82017-04-18 16:01:15 +090073 void AddEvent(int delay_ms, OnceClosure callback) {
74 AddEventHelper(delay_ms, std::move(callback), OnceClosure());
dcheng@chromium.org70297102011-12-10 08:06:02 +090075 }
76
77 void AddDummyEvent(int delay_ms) {
tzikc372ed82017-04-18 16:01:15 +090078 AddEventHelper(delay_ms, OnceClosure(), OnceClosure());
dcheng@chromium.org70297102011-12-10 08:06:02 +090079 }
80
tzikc372ed82017-04-18 16:01:15 +090081 void AddEventAsTask(int delay_ms, OnceClosure task) {
82 AddEventHelper(delay_ms, OnceClosure(), std::move(task));
deanm@chromium.orgbec47f42009-06-15 19:30:44 +090083 }
84
85 void Reset() {
86 processed_events_ = 0;
87 events_.clear();
88 }
89
90 int processed_events() const { return processed_events_; }
91
92 private:
93 struct Event {
brettw@chromium.org710ecb92013-06-19 05:27:52 +090094 Time time;
tzikc372ed82017-04-18 16:01:15 +090095 OnceClosure callback;
96 OnceClosure task;
deanm@chromium.orgbec47f42009-06-15 19:30:44 +090097 };
98
99 struct Source : public GSource {
100 EventInjector* injector;
101 };
102
tzikc372ed82017-04-18 16:01:15 +0900103 void AddEventHelper(int delay_ms, OnceClosure callback, OnceClosure task) {
brettw@chromium.org710ecb92013-06-19 05:27:52 +0900104 Time last_time;
jhawkins@chromium.org598ca1c2012-01-01 11:14:47 +0900105 if (!events_.empty())
dcheng@chromium.org70297102011-12-10 08:06:02 +0900106 last_time = (events_.end()-1)->time;
jhawkins@chromium.org598ca1c2012-01-01 11:14:47 +0900107 else
brettw@chromium.org710ecb92013-06-19 05:27:52 +0900108 last_time = Time::NowFromSystemTime();
jhawkins@chromium.org598ca1c2012-01-01 11:14:47 +0900109
brettw@chromium.org710ecb92013-06-19 05:27:52 +0900110 Time future = last_time + TimeDelta::FromMilliseconds(delay_ms);
tzikc372ed82017-04-18 16:01:15 +0900111 EventInjector::Event event = {future, std::move(callback), std::move(task)};
112 events_.push_back(std::move(event));
dcheng@chromium.org70297102011-12-10 08:06:02 +0900113 }
114
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900115 static gboolean Prepare(GSource* source, gint* timeout_ms) {
116 *timeout_ms = static_cast<Source*>(source)->injector->HandlePrepare();
117 return FALSE;
118 }
119
120 static gboolean Check(GSource* source) {
121 return static_cast<Source*>(source)->injector->HandleCheck();
122 }
123
124 static gboolean Dispatch(GSource* source,
125 GSourceFunc unused_func,
126 gpointer unused_data) {
127 static_cast<Source*>(source)->injector->HandleDispatch();
128 return TRUE;
129 }
130
131 Source* source_;
132 std::vector<Event> events_;
133 int processed_events_;
134 static GSourceFuncs SourceFuncs;
135 DISALLOW_COPY_AND_ASSIGN(EventInjector);
136};
137
Ivan Kotenkove88f3462017-11-08 21:37:33 +0900138GSourceFuncs EventInjector::SourceFuncs = {EventInjector::Prepare,
139 EventInjector::Check,
140 EventInjector::Dispatch, nullptr};
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900141
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900142void IncrementInt(int *value) {
143 ++*value;
144}
145
146// Checks how many events have been processed by the injector.
147void ExpectProcessedEvents(EventInjector* injector, int count) {
148 EXPECT_EQ(injector->processed_events(), count);
149}
150
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900151// Posts a task on the current message loop.
Brett Wilson89388db2017-09-12 14:22:16 +0900152void PostMessageLoopTask(const Location& from_here, OnceClosure task) {
tzikc372ed82017-04-18 16:01:15 +0900153 ThreadTaskRunnerHandle::Get()->PostTask(from_here, std::move(task));
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900154}
155
156// Test fixture.
157class MessagePumpGLibTest : public testing::Test {
158 public:
Ivan Kotenkove88f3462017-11-08 21:37:33 +0900159 MessagePumpGLibTest() : loop_(nullptr), injector_(nullptr) {}
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900160
tfarina@chromium.org2852dcb2012-07-31 10:38:18 +0900161 // Overridden from testing::Test:
dchengca87abb2014-12-23 11:56:47 +0900162 void SetUp() override {
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900163 loop_ = new MessageLoop(MessageLoop::TYPE_UI);
164 injector_ = new EventInjector();
165 }
dchengca87abb2014-12-23 11:56:47 +0900166 void TearDown() override {
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900167 delete injector_;
Ivan Kotenkove88f3462017-11-08 21:37:33 +0900168 injector_ = nullptr;
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900169 delete loop_;
Ivan Kotenkove88f3462017-11-08 21:37:33 +0900170 loop_ = nullptr;
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900171 }
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
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900184TEST_F(MessagePumpGLibTest, TestQuit) {
185 // Checks that Quit works and that the basic infrastructure is working.
186
187 // Quit from a task
tfarina@chromium.org7d4a0ec2013-02-07 01:56:19 +0900188 RunLoop().RunUntilIdle();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900189 EXPECT_EQ(0, injector()->processed_events());
190
191 injector()->Reset();
192 // Quit from an event
thakis@chromium.orgfc6f9292013-02-05 08:40:00 +0900193 injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
fdoray851719c2016-08-26 00:36:37 +0900194 RunLoop().Run();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900195 EXPECT_EQ(1, injector()->processed_events());
196}
197
198TEST_F(MessagePumpGLibTest, TestEventTaskInterleave) {
199 // Checks that tasks posted by events are executed before the next event if
200 // the posted task queue is empty.
201 // MessageLoop doesn't make strong guarantees that it is the case, but the
202 // current implementation ensures it and the tests below rely on it.
203 // If changes cause this test to fail, it is reasonable to change it, but
204 // TestWorkWhileWaitingForEvents and TestEventsWhileWaitingForWork have to be
205 // changed accordingly, otherwise they can become flaky.
Peter Kasting24efe5e2018-02-24 09:03:01 +0900206 injector()->AddEventAsTask(0, DoNothing());
tzikc372ed82017-04-18 16:01:15 +0900207 OnceClosure check_task =
208 BindOnce(&ExpectProcessedEvents, Unretained(injector()), 2);
209 OnceClosure posted_task =
210 BindOnce(&PostMessageLoopTask, FROM_HERE, std::move(check_task));
211 injector()->AddEventAsTask(0, std::move(posted_task));
Peter Kasting24efe5e2018-02-24 09:03:01 +0900212 injector()->AddEventAsTask(0, DoNothing());
thakis@chromium.orgfc6f9292013-02-05 08:40:00 +0900213 injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
fdoray851719c2016-08-26 00:36:37 +0900214 RunLoop().Run();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900215 EXPECT_EQ(4, injector()->processed_events());
216
217 injector()->Reset();
Peter Kasting24efe5e2018-02-24 09:03:01 +0900218 injector()->AddEventAsTask(0, DoNothing());
tzikc372ed82017-04-18 16:01:15 +0900219 check_task = BindOnce(&ExpectProcessedEvents, Unretained(injector()), 2);
220 posted_task =
221 BindOnce(&PostMessageLoopTask, FROM_HERE, std::move(check_task));
222 injector()->AddEventAsTask(0, std::move(posted_task));
Peter Kasting24efe5e2018-02-24 09:03:01 +0900223 injector()->AddEventAsTask(10, DoNothing());
thakis@chromium.orgfc6f9292013-02-05 08:40:00 +0900224 injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
fdoray851719c2016-08-26 00:36:37 +0900225 RunLoop().Run();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900226 EXPECT_EQ(4, injector()->processed_events());
227}
228
229TEST_F(MessagePumpGLibTest, TestWorkWhileWaitingForEvents) {
230 int task_count = 0;
231 // Tests that we process tasks while waiting for new events.
232 // The event queue is empty at first.
233 for (int i = 0; i < 10; ++i) {
fdoray851719c2016-08-26 00:36:37 +0900234 loop()->task_runner()->PostTask(FROM_HERE,
tzik6bdbeb22017-04-12 00:00:44 +0900235 BindOnce(&IncrementInt, &task_count));
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900236 }
237 // After all the previous tasks have executed, enqueue an event that will
238 // quit.
fdoray851719c2016-08-26 00:36:37 +0900239 loop()->task_runner()->PostTask(
tzik6bdbeb22017-04-12 00:00:44 +0900240 FROM_HERE, BindOnce(&EventInjector::AddEvent, Unretained(injector()), 0,
241 MessageLoop::QuitWhenIdleClosure()));
fdoray851719c2016-08-26 00:36:37 +0900242 RunLoop().Run();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900243 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) {
fdoray851719c2016-08-26 00:36:37 +0900250 loop()->task_runner()->PostDelayedTask(FROM_HERE,
tzik6bdbeb22017-04-12 00:00:44 +0900251 BindOnce(&IncrementInt, &task_count),
fdoray851719c2016-08-26 00:36:37 +0900252 TimeDelta::FromMilliseconds(10 * i));
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900253 }
254 // After all the previous tasks have executed, enqueue an event that will
255 // quit.
256 // This relies on the fact that delayed tasks are executed in delay order.
257 // That is verified in message_loop_unittest.cc.
fdoray851719c2016-08-26 00:36:37 +0900258 loop()->task_runner()->PostDelayedTask(
tzik6bdbeb22017-04-12 00:00:44 +0900259 FROM_HERE,
260 BindOnce(&EventInjector::AddEvent, Unretained(injector()), 10,
261 MessageLoop::QuitWhenIdleClosure()),
brettw@chromium.org710ecb92013-06-19 05:27:52 +0900262 TimeDelta::FromMilliseconds(150));
fdoray851719c2016-08-26 00:36:37 +0900263 RunLoop().Run();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900264 ASSERT_EQ(10, task_count);
265 EXPECT_EQ(1, injector()->processed_events());
266}
267
268TEST_F(MessagePumpGLibTest, TestEventsWhileWaitingForWork) {
269 // Tests that we process events while waiting for work.
270 // The event queue is empty at first.
271 for (int i = 0; i < 10; ++i) {
dcheng@chromium.org70297102011-12-10 08:06:02 +0900272 injector()->AddDummyEvent(0);
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900273 }
274 // After all the events have been processed, post a task that will check that
275 // the events have been processed (note: the task executes after the event
276 // that posted it has been handled, so we expect 11 at that point).
tzikc372ed82017-04-18 16:01:15 +0900277 OnceClosure check_task =
278 BindOnce(&ExpectProcessedEvents, Unretained(injector()), 11);
279 OnceClosure posted_task =
280 BindOnce(&PostMessageLoopTask, FROM_HERE, std::move(check_task));
281 injector()->AddEventAsTask(10, std::move(posted_task));
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900282
283 // And then quit (relies on the condition tested by TestEventTaskInterleave).
thakis@chromium.orgfc6f9292013-02-05 08:40:00 +0900284 injector()->AddEvent(10, MessageLoop::QuitWhenIdleClosure());
fdoray851719c2016-08-26 00:36:37 +0900285 RunLoop().Run();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900286
287 EXPECT_EQ(12, injector()->processed_events());
288}
289
290namespace {
291
292// This class is a helper for the concurrent events / posted tasks test below.
293// It will quit the main loop once enough tasks and events have been processed,
294// while making sure there is always work to do and events in the queue.
brettw@chromium.org710ecb92013-06-19 05:27:52 +0900295class ConcurrentHelper : public RefCounted<ConcurrentHelper> {
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900296 public:
erg@google.combf6ce9f2010-01-27 08:08:02 +0900297 explicit ConcurrentHelper(EventInjector* injector)
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900298 : injector_(injector),
299 event_count_(kStartingEventCount),
300 task_count_(kStartingTaskCount) {
301 }
302
303 void FromTask() {
304 if (task_count_ > 0) {
305 --task_count_;
306 }
307 if (task_count_ == 0 && event_count_ == 0) {
Gabriel Charette274bec82017-07-26 21:36:23 +0900308 RunLoop::QuitCurrentWhenIdleDeprecated();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900309 } else {
fdoray96ab6552016-07-19 08:47:16 +0900310 ThreadTaskRunnerHandle::Get()->PostTask(
tzik6bdbeb22017-04-12 00:00:44 +0900311 FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, this));
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900312 }
313 }
314
315 void FromEvent() {
316 if (event_count_ > 0) {
317 --event_count_;
318 }
319 if (task_count_ == 0 && event_count_ == 0) {
Gabriel Charette274bec82017-07-26 21:36:23 +0900320 RunLoop::QuitCurrentWhenIdleDeprecated();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900321 } else {
tzikc372ed82017-04-18 16:01:15 +0900322 injector_->AddEventAsTask(0,
323 BindOnce(&ConcurrentHelper::FromEvent, this));
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900324 }
325 }
326
327 int event_count() const { return event_count_; }
328 int task_count() const { return task_count_; }
329
330 private:
brettw@chromium.org710ecb92013-06-19 05:27:52 +0900331 friend class RefCounted<ConcurrentHelper>;
jam@chromium.orgb1f47b22009-11-06 06:53:08 +0900332
333 ~ConcurrentHelper() {}
334
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900335 static const int kStartingEventCount = 20;
336 static const int kStartingTaskCount = 20;
337
338 EventInjector* injector_;
339 int event_count_;
340 int task_count_;
341};
342
343} // namespace
344
345TEST_F(MessagePumpGLibTest, TestConcurrentEventPostedTask) {
346 // Tests that posted tasks don't starve events, nor the opposite.
347 // We use the helper class above. We keep both event and posted task queues
348 // full, the helper verifies that both tasks and events get processed.
349 // If that is not the case, either event_count_ or task_count_ will not get
tfarina@chromium.org3b5d1472013-01-10 23:56:17 +0900350 // to 0, and MessageLoop::QuitWhenIdle() will never be called.
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900351 scoped_refptr<ConcurrentHelper> helper = new ConcurrentHelper(injector());
352
353 // Add 2 events to the queue to make sure it is always full (when we remove
354 // the event before processing it).
tzikc372ed82017-04-18 16:01:15 +0900355 injector()->AddEventAsTask(0, BindOnce(&ConcurrentHelper::FromEvent, helper));
356 injector()->AddEventAsTask(0, BindOnce(&ConcurrentHelper::FromEvent, helper));
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900357
358 // Similarly post 2 tasks.
fdoray851719c2016-08-26 00:36:37 +0900359 loop()->task_runner()->PostTask(
tzik6bdbeb22017-04-12 00:00:44 +0900360 FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, helper));
fdoray851719c2016-08-26 00:36:37 +0900361 loop()->task_runner()->PostTask(
tzik6bdbeb22017-04-12 00:00:44 +0900362 FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, helper));
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900363
fdoray851719c2016-08-26 00:36:37 +0900364 RunLoop().Run();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900365 EXPECT_EQ(0, helper->event_count());
366 EXPECT_EQ(0, helper->task_count());
367}
368
369namespace {
370
371void AddEventsAndDrainGLib(EventInjector* injector) {
372 // Add a couple of dummy events
dcheng@chromium.org70297102011-12-10 08:06:02 +0900373 injector->AddDummyEvent(0);
374 injector->AddDummyEvent(0);
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900375 // Then add an event that will quit the main loop.
thakis@chromium.orgfc6f9292013-02-05 08:40:00 +0900376 injector->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900377
378 // Post a couple of dummy tasks
Peter Kasting24efe5e2018-02-24 09:03:01 +0900379 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing());
380 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing());
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900381
382 // Drain the events
Ivan Kotenkove88f3462017-11-08 21:37:33 +0900383 while (g_main_context_pending(nullptr)) {
384 g_main_context_iteration(nullptr, FALSE);
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900385 }
386}
387
388} // namespace
389
390TEST_F(MessagePumpGLibTest, TestDrainingGLib) {
391 // Tests that draining events using GLib works.
fdoray851719c2016-08-26 00:36:37 +0900392 loop()->task_runner()->PostTask(
tzik6bdbeb22017-04-12 00:00:44 +0900393 FROM_HERE, BindOnce(&AddEventsAndDrainGLib, Unretained(injector())));
fdoray851719c2016-08-26 00:36:37 +0900394 RunLoop().Run();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900395
396 EXPECT_EQ(3, injector()->processed_events());
397}
398
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900399namespace {
400
401// Helper class that lets us run the GLib message loop.
brettw@chromium.org710ecb92013-06-19 05:27:52 +0900402class GLibLoopRunner : public RefCounted<GLibLoopRunner> {
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900403 public:
404 GLibLoopRunner() : quit_(false) { }
405
406 void RunGLib() {
407 while (!quit_) {
Ivan Kotenkove88f3462017-11-08 21:37:33 +0900408 g_main_context_iteration(nullptr, TRUE);
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900409 }
410 }
411
sadrul@chromium.org847a5722011-09-21 09:17:19 +0900412 void RunLoop() {
sadrul@chromium.org847a5722011-09-21 09:17:19 +0900413 while (!quit_) {
Ivan Kotenkove88f3462017-11-08 21:37:33 +0900414 g_main_context_iteration(nullptr, TRUE);
sadrul@chromium.org847a5722011-09-21 09:17:19 +0900415 }
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900416 }
417
418 void Quit() {
419 quit_ = true;
420 }
421
422 void Reset() {
423 quit_ = false;
424 }
425
426 private:
brettw@chromium.org710ecb92013-06-19 05:27:52 +0900427 friend class RefCounted<GLibLoopRunner>;
jam@chromium.orgb1f47b22009-11-06 06:53:08 +0900428
429 ~GLibLoopRunner() {}
430
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900431 bool quit_;
432};
433
434void TestGLibLoopInternal(EventInjector* injector) {
435 // Allow tasks to be processed from 'native' event loops.
436 MessageLoop::current()->SetNestableTasksAllowed(true);
437 scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
438
439 int task_count = 0;
440 // Add a couple of dummy events
dcheng@chromium.org70297102011-12-10 08:06:02 +0900441 injector->AddDummyEvent(0);
442 injector->AddDummyEvent(0);
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900443 // Post a couple of dummy tasks
fdoray96ab6552016-07-19 08:47:16 +0900444 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
tzik6bdbeb22017-04-12 00:00:44 +0900445 BindOnce(&IncrementInt, &task_count));
fdoray96ab6552016-07-19 08:47:16 +0900446 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
tzik6bdbeb22017-04-12 00:00:44 +0900447 BindOnce(&IncrementInt, &task_count));
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900448 // Delayed events
dcheng@chromium.org70297102011-12-10 08:06:02 +0900449 injector->AddDummyEvent(10);
450 injector->AddDummyEvent(10);
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900451 // Delayed work
fdoray96ab6552016-07-19 08:47:16 +0900452 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
tzik6bdbeb22017-04-12 00:00:44 +0900453 FROM_HERE, BindOnce(&IncrementInt, &task_count),
brettw@chromium.org710ecb92013-06-19 05:27:52 +0900454 TimeDelta::FromMilliseconds(30));
fdoray96ab6552016-07-19 08:47:16 +0900455 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
tzik6bdbeb22017-04-12 00:00:44 +0900456 FROM_HERE, BindOnce(&GLibLoopRunner::Quit, runner),
brettw@chromium.org710ecb92013-06-19 05:27:52 +0900457 TimeDelta::FromMilliseconds(40));
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900458
459 // Run a nested, straight GLib message loop.
460 runner->RunGLib();
461
462 ASSERT_EQ(3, task_count);
463 EXPECT_EQ(4, injector->processed_events());
Gabriel Charette274bec82017-07-26 21:36:23 +0900464 RunLoop::QuitCurrentWhenIdleDeprecated();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900465}
466
467void TestGtkLoopInternal(EventInjector* injector) {
468 // Allow tasks to be processed from 'native' event loops.
469 MessageLoop::current()->SetNestableTasksAllowed(true);
470 scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
471
472 int task_count = 0;
473 // Add a couple of dummy events
dcheng@chromium.org70297102011-12-10 08:06:02 +0900474 injector->AddDummyEvent(0);
475 injector->AddDummyEvent(0);
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900476 // Post a couple of dummy tasks
fdoray96ab6552016-07-19 08:47:16 +0900477 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
tzik6bdbeb22017-04-12 00:00:44 +0900478 BindOnce(&IncrementInt, &task_count));
fdoray96ab6552016-07-19 08:47:16 +0900479 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
tzik6bdbeb22017-04-12 00:00:44 +0900480 BindOnce(&IncrementInt, &task_count));
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900481 // Delayed events
dcheng@chromium.org70297102011-12-10 08:06:02 +0900482 injector->AddDummyEvent(10);
483 injector->AddDummyEvent(10);
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900484 // Delayed work
fdoray96ab6552016-07-19 08:47:16 +0900485 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
tzik6bdbeb22017-04-12 00:00:44 +0900486 FROM_HERE, BindOnce(&IncrementInt, &task_count),
brettw@chromium.org710ecb92013-06-19 05:27:52 +0900487 TimeDelta::FromMilliseconds(30));
fdoray96ab6552016-07-19 08:47:16 +0900488 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
tzik6bdbeb22017-04-12 00:00:44 +0900489 FROM_HERE, BindOnce(&GLibLoopRunner::Quit, runner),
brettw@chromium.org710ecb92013-06-19 05:27:52 +0900490 TimeDelta::FromMilliseconds(40));
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900491
492 // Run a nested, straight Gtk message loop.
sadrul@chromium.org847a5722011-09-21 09:17:19 +0900493 runner->RunLoop();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900494
495 ASSERT_EQ(3, task_count);
496 EXPECT_EQ(4, injector->processed_events());
Gabriel Charette274bec82017-07-26 21:36:23 +0900497 RunLoop::QuitCurrentWhenIdleDeprecated();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900498}
499
500} // namespace
501
502TEST_F(MessagePumpGLibTest, TestGLibLoop) {
jhawkins@chromium.org598ca1c2012-01-01 11:14:47 +0900503 // Tests that events and posted tasks are correctly executed if the message
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900504 // loop is not run by MessageLoop::Run() but by a straight GLib loop.
505 // Note that in this case we don't make strong guarantees about niceness
506 // between events and posted tasks.
fdoray851719c2016-08-26 00:36:37 +0900507 loop()->task_runner()->PostTask(
tzik6bdbeb22017-04-12 00:00:44 +0900508 FROM_HERE, BindOnce(&TestGLibLoopInternal, Unretained(injector())));
fdoray851719c2016-08-26 00:36:37 +0900509 RunLoop().Run();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900510}
511
512TEST_F(MessagePumpGLibTest, TestGtkLoop) {
jhawkins@chromium.org598ca1c2012-01-01 11:14:47 +0900513 // Tests that events and posted tasks are correctly executed if the message
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900514 // loop is not run by MessageLoop::Run() but by a straight Gtk loop.
515 // Note that in this case we don't make strong guarantees about niceness
516 // between events and posted tasks.
fdoray851719c2016-08-26 00:36:37 +0900517 loop()->task_runner()->PostTask(
tzik6bdbeb22017-04-12 00:00:44 +0900518 FROM_HERE, BindOnce(&TestGtkLoopInternal, Unretained(injector())));
fdoray851719c2016-08-26 00:36:37 +0900519 RunLoop().Run();
deanm@chromium.orgbec47f42009-06-15 19:30:44 +0900520}
tfarina@chromium.org7d4a0ec2013-02-07 01:56:19 +0900521
522} // namespace base