[GLib] Move files under Source/WTF/wtf/gobject to Source/WTF/wtf/glib
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WTF / glib / GMainLoopSource.cpp
1 /*
2  * Copyright (C) 2014 Igalia S.L.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21
22 #include <wtf/glib/GThreadSafeMainLoopSource.h>
23 #include <stdio.h>
24
25 namespace TestWebKitAPI {
26
27 template <typename T>
28 class GMainLoopSourceTest {
29 public:
30     GMainLoopSourceTest()
31         : m_mainLoop(g_main_loop_new(nullptr, TRUE))
32     {
33     }
34
35     ~GMainLoopSourceTest()
36     {
37         g_main_loop_unref(m_mainLoop);
38     }
39
40     void runLoop()
41     {
42         g_main_loop_run(m_mainLoop);
43     }
44
45     void delayedFinish()
46     {
47         g_timeout_add(250,
48             [](gpointer data) {
49                 GMainLoopSourceTest& test = *static_cast<GMainLoopSourceTest*>(data);
50                 test.finish();
51                 return G_SOURCE_REMOVE;
52             }, this);
53     }
54
55     void finish()
56     {
57         g_main_loop_quit(m_mainLoop);
58     }
59
60     T& source() { return m_source; }
61
62 private:
63     GMainLoop* m_mainLoop;
64     T m_source;
65 };
66
67 template <typename T>
68 static void basicRescheduling(T& context)
69 {
70     EXPECT_TRUE(!context.test.source().isActive());
71
72     context.test.source().schedule("[Test] FirstTask", [&] {
73         // This should never be called. That's why we assert
74         // that the variable is false a few lines later.
75         context.finishedFirstTask = true;
76     });
77     EXPECT_TRUE(context.test.source().isScheduled());
78
79     context.test.source().schedule("[Test] SecondTask", [&] {
80         EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
81         context.finishedSecondTask = true;
82         context.test.finish();
83     });
84     EXPECT_TRUE(context.test.source().isScheduled());
85
86     context.test.runLoop();
87
88     EXPECT_TRUE(!context.test.source().isActive());
89     EXPECT_FALSE(context.finishedFirstTask);
90     EXPECT_TRUE(context.finishedSecondTask);
91 }
92
93 TEST(WTF_GMainLoopSource, BasicRescheduling)
94 {
95     struct TestingContext {
96         GMainLoopSourceTest<GMainLoopSource> test;
97         bool finishedFirstTask = false;
98         bool finishedSecondTask = false;
99     } context;
100     basicRescheduling<TestingContext>(context);
101
102     struct ThreadSafeTestingContext {
103         GMainLoopSourceTest<GThreadSafeMainLoopSource> test;
104         bool finishedFirstTask = false;
105         bool finishedSecondTask = false;
106     } threadSafeContext;
107     basicRescheduling<ThreadSafeTestingContext>(threadSafeContext);
108 }
109
110 template <typename T>
111 static void reentrantRescheduling(T& context)
112 {
113     EXPECT_TRUE(!context.test.source().isActive());
114
115     context.test.source().schedule("[Test] FirstTask", [&] {
116         EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
117
118         context.test.source().schedule("[Test] SecondTask", [&] {
119             EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
120             EXPECT_TRUE(context.finishedFirstTask);
121
122             context.finishedSecondTask = true;
123             context.test.finish();
124         });
125         EXPECT_TRUE(context.test.source().isScheduled());
126
127         context.finishedFirstTask = true;
128     });
129     EXPECT_TRUE(context.test.source().isScheduled());
130
131     context.test.runLoop();
132
133     EXPECT_TRUE(!context.test.source().isActive());
134     EXPECT_TRUE(context.finishedFirstTask);
135     EXPECT_TRUE(context.finishedSecondTask);
136 }
137
138 TEST(WTF_GMainLoopSource, ReentrantRescheduling)
139 {
140     struct TestingContext {
141         GMainLoopSourceTest<GMainLoopSource> test;
142         bool finishedFirstTask = false;
143         bool finishedSecondTask = false;
144     } context;
145     reentrantRescheduling<TestingContext>(context);
146
147     struct ThreadSafeTestingContext {
148         GMainLoopSourceTest<GThreadSafeMainLoopSource> test;
149         bool finishedFirstTask = false;
150         bool finishedSecondTask = false;
151     } threadSafeContext;
152     reentrantRescheduling<ThreadSafeTestingContext>(threadSafeContext);
153 }
154
155 TEST(WTF_GMainLoopSource, ReschedulingFromDifferentThread)
156 {
157     struct TestingContext {
158         GMainLoopSourceTest<GThreadSafeMainLoopSource> test;
159         bool finishedFirstTask;
160         bool finishedSecondTask;
161     } context;
162
163     EXPECT_TRUE(!context.test.source().isActive());
164
165     context.test.source().schedule("[Test] FirstTask", [&] {
166         EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
167
168         g_usleep(1 * G_USEC_PER_SEC);
169         context.finishedFirstTask = true;
170     });
171     EXPECT_TRUE(context.test.source().isScheduled());
172
173     GThread* helperThread = g_thread_new(nullptr, [](gpointer data) -> gpointer {
174         g_usleep(0.25 * G_USEC_PER_SEC);
175
176         TestingContext& context = *static_cast<TestingContext*>(data);
177         EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
178         EXPECT_FALSE(context.finishedFirstTask);
179
180         context.test.source().schedule("[Test] SecondTask", [&] {
181             EXPECT_TRUE(context.finishedFirstTask);
182
183             context.finishedSecondTask = true;
184             context.test.finish();
185         });
186         EXPECT_TRUE(context.test.source().isScheduled());
187
188         g_thread_exit(nullptr);
189         return nullptr;
190     }, &context);
191
192     context.test.runLoop();
193     g_thread_unref(helperThread);
194
195     EXPECT_TRUE(!context.test.source().isActive());
196     EXPECT_TRUE(context.finishedFirstTask);
197     EXPECT_TRUE(context.finishedSecondTask);
198 }
199
200 TEST(WTF_GMainLoopSource, DestructionDuringDispatch)
201 {
202     // This is just a raw test that ensures deleting the GMainLoopSource object during
203     // dispatch does not cause problems. This test succeeds if it doesn't crash.
204
205     GMainLoopSource* source;
206     GMainLoop* loop = g_main_loop_new(nullptr, TRUE);
207
208     source = new GMainLoopSource;
209     source->schedule("[Test] DestroySourceTask", [&] {
210         delete source;
211         g_main_loop_quit(loop);
212     });
213     g_main_loop_run(loop);
214
215     source = new GMainLoopSource;
216     source->schedule("[Test] DestroySourceTask", std::function<bool ()>([&] {
217         delete source;
218         g_main_loop_quit(loop);
219         return false;
220     }));
221     g_main_loop_run(loop);
222
223     g_main_loop_unref(loop);
224 }
225
226 template <typename T>
227 static void cancelRepeatingSourceDuringDispatch(T& context)
228 {
229     EXPECT_TRUE(!context.test.source().isActive());
230
231     context.test.source().schedule("[Test] RepeatingTask",
232         std::function<bool ()>([&] {
233             EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
234
235             context.callCount++;
236             if (context.callCount == 3)
237                 context.test.source().cancel();
238             return true;
239         }));
240     EXPECT_TRUE(context.test.source().isScheduled());
241
242     context.test.delayedFinish();
243     context.test.runLoop();
244
245     EXPECT_TRUE(!context.test.source().isActive());
246     EXPECT_EQ(3, context.callCount);
247 }
248
249 TEST(WTF_GMainLoopSource, CancelRepeatingSourceDuringDispatch)
250 {
251     struct TestingContext {
252         GMainLoopSourceTest<GMainLoopSource> test;
253         unsigned callCount = 0;
254     } context;
255     cancelRepeatingSourceDuringDispatch<TestingContext>(context);
256
257     struct ThreadSafeTestingContext {
258         GMainLoopSourceTest<GThreadSafeMainLoopSource> test;
259         unsigned callCount = 0;
260     } threadSafeContext;
261     cancelRepeatingSourceDuringDispatch<ThreadSafeTestingContext>(threadSafeContext);
262 }
263
264 template <typename T>
265 static void basicDestroyCallbacks()
266 {
267     {
268         T context;
269         EXPECT_TRUE(!context.test.source().isActive());
270         context.test.source().schedule("[Test] DestroyCallback",
271             [&] {
272                 EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
273                 context.callbackCalled = true;
274             }, G_PRIORITY_DEFAULT,
275             [&] {
276                 EXPECT_TRUE(!context.test.source().isActive());
277                 context.destroyCallbackCalled = true;
278                 context.test.finish();
279             });
280         EXPECT_TRUE(context.test.source().isScheduled());
281
282         context.test.runLoop();
283
284         EXPECT_TRUE(!context.test.source().isActive());
285         EXPECT_TRUE(context.callbackCalled);
286         EXPECT_TRUE(context.destroyCallbackCalled);
287     }
288
289     {
290         T context;
291         EXPECT_TRUE(!context.test.source().isActive());
292         context.test.source().schedule("[Test] DestroyCallback",
293             std::function<bool ()>([&] {
294                 EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
295                 context.callbackCalled = true;
296                 return false;
297             }), G_PRIORITY_DEFAULT,
298             [&] {
299                 EXPECT_TRUE(!context.test.source().isActive());
300                 context.destroyCallbackCalled = true;
301                 context.test.finish();
302             });
303         EXPECT_TRUE(context.test.source().isScheduled());
304
305         context.test.runLoop();
306
307         EXPECT_TRUE(!context.test.source().isActive());
308         EXPECT_TRUE(context.callbackCalled);
309         EXPECT_TRUE(context.destroyCallbackCalled);
310     }
311 }
312
313 TEST(WTF_GMainLoopSource, BasicDestroyCallbacks)
314 {
315     struct TestingContext {
316         GMainLoopSourceTest<GMainLoopSource> test;
317         bool callbackCalled = false;
318         bool destroyCallbackCalled = false;
319     };
320     basicDestroyCallbacks<TestingContext>();
321
322     struct ThreadSafeTestingContext {
323         GMainLoopSourceTest<GThreadSafeMainLoopSource> test;
324         bool callbackCalled = false;
325         bool destroyCallbackCalled = false;
326     };
327     basicDestroyCallbacks<ThreadSafeTestingContext>();
328 }
329
330 template <typename T>
331 static void destroyCallbacksAfterCancellingDuringDispatch()
332 {
333     {
334         T context;
335         EXPECT_TRUE(!context.test.source().isActive());
336         context.test.source().schedule("[Test] DestroyCallback",
337             [&] {
338                 EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
339                 context.callbackCallCount++;
340                 context.test.source().cancel();
341             }, G_PRIORITY_DEFAULT,
342             [&] {
343                 EXPECT_TRUE(!context.test.source().isActive());
344                 context.destroyCallbackCalled = true;
345                 context.test.finish();
346             });
347         EXPECT_TRUE(context.test.source().isScheduled());
348
349         context.test.delayedFinish();
350         context.test.runLoop();
351
352         EXPECT_TRUE(!context.test.source().isActive());
353         EXPECT_EQ(1, context.callbackCallCount);
354         EXPECT_TRUE(context.destroyCallbackCalled);
355     }
356
357     {
358         T context;
359         EXPECT_TRUE(!context.test.source().isActive());
360         context.test.source().schedule("[Test] DestroyCallback",
361             std::function<bool ()>([&] {
362                 EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
363                 context.callbackCallCount++;
364                 if (context.callbackCallCount == 3)
365                     context.test.source().cancel();
366                 return true;
367             }), G_PRIORITY_DEFAULT,
368             [&] {
369                 EXPECT_TRUE(!context.test.source().isActive());
370                 context.destroyCallbackCalled = true;
371             });
372         EXPECT_TRUE(context.test.source().isScheduled());
373
374         context.test.delayedFinish();
375         context.test.runLoop();
376
377         EXPECT_TRUE(!context.test.source().isActive());
378         EXPECT_EQ(3, context.callbackCallCount);
379         EXPECT_TRUE(context.destroyCallbackCalled);
380     }
381 }
382
383 TEST(WTF_GMainLoopSource, DestroyCallbacksAfterCancellingDuringDispatch)
384 {
385     struct TestingContext {
386         GMainLoopSourceTest<GMainLoopSource> test;
387         unsigned callbackCallCount= 0;
388         bool destroyCallbackCalled = false;
389     };
390     destroyCallbacksAfterCancellingDuringDispatch<TestingContext>();
391
392     struct ThreadSafeTestingContext {
393         GMainLoopSourceTest<GThreadSafeMainLoopSource> test;
394         unsigned callbackCallCount= 0;
395         bool destroyCallbackCalled = false;
396     };
397     destroyCallbacksAfterCancellingDuringDispatch<ThreadSafeTestingContext>();
398 }
399
400 template <typename T>
401 static void destroyCallbacksAfterReschedulingDuringDispatch()
402 {
403     {
404         T context;
405         EXPECT_TRUE(!context.test.source().isActive());
406         context.test.source().schedule("[Test] BaseCallback",
407             [&] {
408                 EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
409                 context.firstCallbackCallCount++;
410                 context.test.source().schedule("[Test] ReschedulingCallback",
411                     [&] {
412                         EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
413                         context.secondCallbackCallCount++;
414                     }, G_PRIORITY_DEFAULT,
415                     [&] {
416                         EXPECT_TRUE(!context.test.source().isActive());
417                         context.secondDestroyCallbackCalled = true;
418                     });
419                 EXPECT_TRUE(context.test.source().isScheduled());
420             }, G_PRIORITY_DEFAULT,
421             [&] {
422                 // At this point the GMainLoopSource has been rescheduled, ergo the Scheduled status.
423                 EXPECT_TRUE(context.test.source().isScheduled());
424                 context.firstDestroyCallbackCalled = true;
425             });
426         EXPECT_TRUE(context.test.source().isScheduled());
427
428         context.test.delayedFinish();
429         context.test.runLoop();
430
431         EXPECT_TRUE(!context.test.source().isActive());
432         EXPECT_EQ(1, context.firstCallbackCallCount);
433         EXPECT_TRUE(context.firstDestroyCallbackCalled);
434         EXPECT_EQ(1, context.secondCallbackCallCount);
435         EXPECT_TRUE(context.secondDestroyCallbackCalled);
436     }
437
438     {
439         T context;
440         EXPECT_TRUE(!context.test.source().isActive());
441         context.test.source().schedule("[Test] BaseCallback",
442             std::function<bool ()>([&] {
443                 EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
444                 context.firstCallbackCallCount++;
445                 context.test.source().schedule("[Test] ReschedulingCallback",
446                     std::function<bool ()>([&] {
447                         EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled());
448                         context.secondCallbackCallCount++;
449                         return context.secondCallbackCallCount != 3;
450                     }), G_PRIORITY_DEFAULT,
451                     [&] {
452                         EXPECT_TRUE(!context.test.source().isActive());
453                         context.secondDestroyCallbackCalled = true;
454                     });
455                 EXPECT_TRUE(context.test.source().isScheduled());
456                 return true;
457             }), G_PRIORITY_DEFAULT,
458             [&] {
459                 // At this point the GMainLoopSource has been rescheduled, ergo the Scheduled status.
460                 EXPECT_TRUE(context.test.source().isScheduled());
461                 context.firstDestroyCallbackCalled = true;
462             });
463         EXPECT_TRUE(context.test.source().isScheduled());
464
465         context.test.delayedFinish();
466         context.test.runLoop();
467
468         EXPECT_TRUE(!context.test.source().isActive());
469         EXPECT_EQ(1, context.firstCallbackCallCount);
470         EXPECT_TRUE(context.firstDestroyCallbackCalled);
471         EXPECT_EQ(3, context.secondCallbackCallCount);
472         EXPECT_TRUE(context.secondDestroyCallbackCalled);
473     }
474 }
475
476 TEST(WTF_GMainLoopSource, DestroyCallbacksAfterReschedulingDuringDispatch)
477 {
478     struct TestingContext {
479         GMainLoopSourceTest<GMainLoopSource> test;
480         unsigned firstCallbackCallCount = 0;
481         bool firstDestroyCallbackCalled = false;
482         unsigned secondCallbackCallCount = 0;
483         bool secondDestroyCallbackCalled = false;
484     };
485     destroyCallbacksAfterReschedulingDuringDispatch<TestingContext>();
486
487     struct ThreadSafeTestingContext {
488         GMainLoopSourceTest<GThreadSafeMainLoopSource> test;
489         unsigned firstCallbackCallCount = 0;
490         bool firstDestroyCallbackCalled = false;
491         unsigned secondCallbackCallCount = 0;
492         bool secondDestroyCallbackCalled = false;
493     };
494     destroyCallbacksAfterReschedulingDuringDispatch<ThreadSafeTestingContext>();
495 }
496
497 TEST(WTF_GMainLoopSource, DeleteOnDestroySources)
498 {
499     // Testing the delete-on-destroy sources is very limited. There's no good way
500     // of testing that the GMainLoopSource objects are deleted when their GSource
501     // is destroyed.
502
503     struct TestingContext {
504         GMainLoopSourceTest<GMainLoopSource> test;
505         unsigned callbackCallCount = 0;
506         bool destroyCallbackCalled = false;
507     } context;
508
509     {
510         TestingContext context;
511
512         GMainLoopSource::scheduleAndDeleteOnDestroy("[Test] DeleteOnDestroy",
513             [&] {
514                 context.callbackCallCount++;
515             }, G_PRIORITY_DEFAULT,
516             [&] {
517                 EXPECT_FALSE(context.destroyCallbackCalled);
518                 context.destroyCallbackCalled = true;
519             });
520
521         context.test.delayedFinish();
522         context.test.runLoop();
523         EXPECT_EQ(1, context.callbackCallCount);
524         EXPECT_TRUE(context.destroyCallbackCalled);
525     }
526
527     {
528         TestingContext context;
529
530         GMainLoopSource::scheduleAndDeleteOnDestroy("[Test] DeleteOnDestroy",
531             std::function<bool ()>([&] {
532                 context.callbackCallCount++;
533                 return context.callbackCallCount != 3;
534             }), G_PRIORITY_DEFAULT,
535             [&] {
536                 EXPECT_FALSE(context.destroyCallbackCalled);
537                 context.destroyCallbackCalled = true;
538             });
539
540         context.test.delayedFinish();
541         context.test.runLoop();
542         EXPECT_EQ(3, context.callbackCallCount);
543         EXPECT_TRUE(context.destroyCallbackCalled);
544     }
545 }
546
547 } // namespace TestWebKitAPI