1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Copyright (C) 2016 Intel Corporation. |
5 | ** Contact: https://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the test suite of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at https://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
22 | ** included in the packaging of this file. Please review the following |
23 | ** information to ensure the GNU General Public License requirements will |
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
25 | ** |
26 | ** $QT_END_LICENSE$ |
27 | ** |
28 | ****************************************************************************/ |
29 | |
30 | #include "tst_qcoreapplication.h" |
31 | |
32 | #include <QtCore/QtCore> |
33 | #include <QtTest/QtTest> |
34 | |
35 | #include <private/qcoreapplication_p.h> |
36 | #include <private/qeventloop_p.h> |
37 | #include <private/qthread_p.h> |
38 | |
39 | typedef QCoreApplication TestApplication; |
40 | |
41 | class EventSpy : public QObject |
42 | { |
43 | Q_OBJECT |
44 | |
45 | public: |
46 | QList<int> recordedEvents; |
47 | bool eventFilter(QObject *, QEvent *event) |
48 | { |
49 | recordedEvents.append(t: event->type()); |
50 | return false; |
51 | } |
52 | }; |
53 | |
54 | class ThreadedEventReceiver : public QObject |
55 | { |
56 | Q_OBJECT |
57 | public: |
58 | QList<int> recordedEvents; |
59 | bool event(QEvent *event) override |
60 | { |
61 | if (event->type() != QEvent::Type(QEvent::User + 1)) |
62 | return QObject::event(event); |
63 | recordedEvents.append(t: event->type()); |
64 | QThread::currentThread()->quit(); |
65 | QCoreApplication::quit(); |
66 | moveToThread(thread: 0); |
67 | return true; |
68 | } |
69 | }; |
70 | |
71 | class Thread : public QDaemonThread |
72 | { |
73 | void run() override |
74 | { |
75 | QThreadData *data = QThreadData::current(); |
76 | QVERIFY(!data->requiresCoreApplication); // daemon thread |
77 | data->requiresCoreApplication = requiresCoreApplication; |
78 | QThread::run(); |
79 | } |
80 | |
81 | public: |
82 | Thread() : requiresCoreApplication(true) {} |
83 | bool requiresCoreApplication; |
84 | }; |
85 | |
86 | void tst_QCoreApplication::sendEventsOnProcessEvents() |
87 | { |
88 | int argc = 1; |
89 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
90 | TestApplication app(argc, argv); |
91 | |
92 | EventSpy spy; |
93 | app.installEventFilter(filterObj: &spy); |
94 | |
95 | QCoreApplication::postEvent(receiver: &app, event: new QEvent(QEvent::Type(QEvent::User + 1))); |
96 | QCoreApplication::processEvents(); |
97 | QVERIFY(spy.recordedEvents.contains(QEvent::User + 1)); |
98 | } |
99 | |
100 | void tst_QCoreApplication::getSetCheck() |
101 | { |
102 | // do not crash |
103 | QString v = QCoreApplication::applicationVersion(); |
104 | v = QLatin1String("3.0.0 prerelease 1" ); |
105 | QCoreApplication::setApplicationVersion(v); |
106 | QCOMPARE(QCoreApplication::applicationVersion(), v); |
107 | |
108 | // Test the property |
109 | { |
110 | int argc = 1; |
111 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
112 | TestApplication app(argc, argv); |
113 | QCOMPARE(app.property("applicationVersion" ).toString(), v); |
114 | } |
115 | v = QString(); |
116 | QCoreApplication::setApplicationVersion(v); |
117 | QCOMPARE(QCoreApplication::applicationVersion(), v); |
118 | } |
119 | |
120 | void tst_QCoreApplication::qAppName() |
121 | { |
122 | #ifdef QT_GUI_LIB |
123 | const char* appName = "tst_qguiapplication" ; |
124 | #else |
125 | const char* appName = "tst_qcoreapplication" ; |
126 | #endif |
127 | |
128 | { |
129 | int argc = 1; |
130 | char *argv[] = { const_cast<char*>(appName) }; |
131 | TestApplication app(argc, argv); |
132 | QCOMPARE(::qAppName(), QString::fromLatin1(appName)); |
133 | QCOMPARE(QCoreApplication::applicationName(), QString::fromLatin1(appName)); |
134 | } |
135 | // The application name should still be available after destruction; |
136 | // global statics often rely on this. |
137 | QCOMPARE(QCoreApplication::applicationName(), QString::fromLatin1(appName)); |
138 | |
139 | // Setting the appname before creating the application should work (QTBUG-45283) |
140 | const QString wantedAppName("my app name" ); |
141 | { |
142 | int argc = 1; |
143 | char *argv[] = { const_cast<char*>(appName) }; |
144 | QCoreApplication::setApplicationName(wantedAppName); |
145 | TestApplication app(argc, argv); |
146 | QCOMPARE(::qAppName(), QString::fromLatin1(appName)); |
147 | QCOMPARE(QCoreApplication::applicationName(), wantedAppName); |
148 | } |
149 | QCOMPARE(QCoreApplication::applicationName(), wantedAppName); |
150 | |
151 | // Restore to initial value |
152 | QCoreApplication::setApplicationName(QString()); |
153 | QCOMPARE(QCoreApplication::applicationName(), QString()); |
154 | } |
155 | |
156 | void tst_QCoreApplication::qAppVersion() |
157 | { |
158 | #if defined(Q_OS_WINRT) |
159 | const char appVersion[] = "1.0.0.0" ; |
160 | #elif defined(Q_OS_WIN) |
161 | const char appVersion[] = "1.2.3.4" ; |
162 | #elif defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID) |
163 | const char appVersion[] = "1.2.3" ; |
164 | #else |
165 | const char appVersion[] = "" ; |
166 | #endif |
167 | |
168 | { |
169 | int argc = 0; |
170 | char *argv[] = { nullptr }; |
171 | TestApplication app(argc, argv); |
172 | QCOMPARE(QCoreApplication::applicationVersion(), QString::fromLatin1(appVersion)); |
173 | } |
174 | // The application version should still be available after destruction |
175 | QCOMPARE(QCoreApplication::applicationVersion(), QString::fromLatin1(appVersion)); |
176 | |
177 | // Setting the appversion before creating the application should work |
178 | const QString wantedAppVersion("0.0.1" ); |
179 | { |
180 | int argc = 0; |
181 | char *argv[] = { nullptr }; |
182 | QCoreApplication::setApplicationVersion(wantedAppVersion); |
183 | TestApplication app(argc, argv); |
184 | QCOMPARE(QCoreApplication::applicationVersion(), wantedAppVersion); |
185 | } |
186 | QCOMPARE(QCoreApplication::applicationVersion(), wantedAppVersion); |
187 | |
188 | // Restore to initial value |
189 | QCoreApplication::setApplicationVersion(QString()); |
190 | QCOMPARE(QCoreApplication::applicationVersion(), QString()); |
191 | } |
192 | |
193 | void tst_QCoreApplication::argc() |
194 | { |
195 | #if defined(Q_OS_WINRT) |
196 | QSKIP("QCoreApplication::arguments() parses arguments from actual command line on this platform." ); |
197 | #endif |
198 | { |
199 | int argc = 1; |
200 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
201 | TestApplication app(argc, argv); |
202 | QCOMPARE(argc, 1); |
203 | QCOMPARE(app.arguments().count(), 1); |
204 | } |
205 | |
206 | { |
207 | int argc = 4; |
208 | char *argv[] = { const_cast<char*>(QTest::currentAppName()), |
209 | const_cast<char*>("arg1" ), |
210 | const_cast<char*>("arg2" ), |
211 | const_cast<char*>("arg3" ) }; |
212 | TestApplication app(argc, argv); |
213 | QCOMPARE(argc, 4); |
214 | QCOMPARE(app.arguments().count(), 4); |
215 | } |
216 | |
217 | { |
218 | int argc = 0; |
219 | char **argv = 0; |
220 | TestApplication app(argc, argv); |
221 | QCOMPARE(argc, 0); |
222 | QCOMPARE(app.arguments().count(), 0); |
223 | } |
224 | |
225 | { |
226 | int argc = 2; |
227 | char *argv[] = { const_cast<char*>(QTest::currentAppName()), |
228 | const_cast<char*>("-qmljsdebugger=port:3768,block" ) }; |
229 | TestApplication app(argc, argv); |
230 | QCOMPARE(argc, 1); |
231 | QCOMPARE(app.arguments().count(), 1); |
232 | } |
233 | } |
234 | |
235 | class EventGenerator : public QObject |
236 | { |
237 | Q_OBJECT |
238 | |
239 | public: |
240 | QObject *other; |
241 | |
242 | bool event(QEvent *e) |
243 | { |
244 | if (e->type() == QEvent::MaxUser) { |
245 | QCoreApplication::sendPostedEvents(receiver: other, event_type: 0); |
246 | } else if (e->type() <= QEvent::User + 999) { |
247 | // post a new event in response to this posted event |
248 | int offset = e->type() - QEvent::User; |
249 | offset = (offset * 10 + offset % 10); |
250 | QCoreApplication::postEvent(receiver: this, event: new QEvent(QEvent::Type(QEvent::User + offset)), priority: offset); |
251 | } |
252 | |
253 | return QObject::event(event: e); |
254 | } |
255 | }; |
256 | |
257 | void tst_QCoreApplication::postEvent() |
258 | { |
259 | int argc = 1; |
260 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
261 | TestApplication app(argc, argv); |
262 | |
263 | EventSpy spy; |
264 | EventGenerator odd, even; |
265 | odd.other = &even; |
266 | odd.installEventFilter(filterObj: &spy); |
267 | even.other = &odd; |
268 | even.installEventFilter(filterObj: &spy); |
269 | |
270 | QCoreApplication::postEvent(receiver: &odd, event: new QEvent(QEvent::Type(QEvent::User + 1))); |
271 | QCoreApplication::postEvent(receiver: &even, event: new QEvent(QEvent::Type(QEvent::User + 2))); |
272 | |
273 | QCoreApplication::postEvent(receiver: &odd, event: new QEvent(QEvent::Type(QEvent::User + 3)), priority: 1); |
274 | QCoreApplication::postEvent(receiver: &even, event: new QEvent(QEvent::Type(QEvent::User + 4)), priority: 2); |
275 | |
276 | QCoreApplication::postEvent(receiver: &odd, event: new QEvent(QEvent::Type(QEvent::User + 5)), priority: -2); |
277 | QCoreApplication::postEvent(receiver: &even, event: new QEvent(QEvent::Type(QEvent::User + 6)), priority: -1); |
278 | |
279 | QList<int> expected; |
280 | expected << QEvent::User + 4 |
281 | << QEvent::User + 3 |
282 | << QEvent::User + 1 |
283 | << QEvent::User + 2 |
284 | << QEvent::User + 6 |
285 | << QEvent::User + 5; |
286 | |
287 | QCoreApplication::sendPostedEvents(); |
288 | // live lock protection ensures that we only send the initial events |
289 | QCOMPARE(spy.recordedEvents, expected); |
290 | |
291 | expected.clear(); |
292 | expected << QEvent::User + 66 |
293 | << QEvent::User + 55 |
294 | << QEvent::User + 44 |
295 | << QEvent::User + 33 |
296 | << QEvent::User + 22 |
297 | << QEvent::User + 11; |
298 | |
299 | spy.recordedEvents.clear(); |
300 | QCoreApplication::sendPostedEvents(); |
301 | // expect next sequence events |
302 | QCOMPARE(spy.recordedEvents, expected); |
303 | |
304 | // have the generators call sendPostedEvents() on each other in |
305 | // response to an event |
306 | QCoreApplication::postEvent(receiver: &odd, event: new QEvent(QEvent::MaxUser), INT_MAX); |
307 | QCoreApplication::postEvent(receiver: &even, event: new QEvent(QEvent::MaxUser), INT_MAX); |
308 | |
309 | expected.clear(); |
310 | expected << int(QEvent::MaxUser) |
311 | << int(QEvent::MaxUser) |
312 | << QEvent::User + 555 |
313 | << QEvent::User + 333 |
314 | << QEvent::User + 111 |
315 | << QEvent::User + 666 |
316 | << QEvent::User + 444 |
317 | << QEvent::User + 222; |
318 | |
319 | spy.recordedEvents.clear(); |
320 | QCoreApplication::sendPostedEvents(); |
321 | QCOMPARE(spy.recordedEvents, expected); |
322 | |
323 | expected.clear(); |
324 | expected << QEvent::User + 6666 |
325 | << QEvent::User + 5555 |
326 | << QEvent::User + 4444 |
327 | << QEvent::User + 3333 |
328 | << QEvent::User + 2222 |
329 | << QEvent::User + 1111; |
330 | |
331 | spy.recordedEvents.clear(); |
332 | QCoreApplication::sendPostedEvents(); |
333 | QCOMPARE(spy.recordedEvents, expected); |
334 | |
335 | // no more events |
336 | expected.clear(); |
337 | spy.recordedEvents.clear(); |
338 | QCoreApplication::sendPostedEvents(); |
339 | QCOMPARE(spy.recordedEvents, expected); |
340 | } |
341 | |
342 | void tst_QCoreApplication::removePostedEvents() |
343 | { |
344 | int argc = 1; |
345 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
346 | TestApplication app(argc, argv); |
347 | |
348 | EventSpy spy; |
349 | QObject one, two; |
350 | one.installEventFilter(filterObj: &spy); |
351 | two.installEventFilter(filterObj: &spy); |
352 | |
353 | QList<int> expected; |
354 | |
355 | // remove all events for one object |
356 | QCoreApplication::postEvent(receiver: &one, event: new QEvent(QEvent::Type(QEvent::User + 1))); |
357 | QCoreApplication::postEvent(receiver: &one, event: new QEvent(QEvent::Type(QEvent::User + 2))); |
358 | QCoreApplication::postEvent(receiver: &one, event: new QEvent(QEvent::Type(QEvent::User + 3))); |
359 | QCoreApplication::postEvent(receiver: &two, event: new QEvent(QEvent::Type(QEvent::User + 4))); |
360 | QCoreApplication::postEvent(receiver: &two, event: new QEvent(QEvent::Type(QEvent::User + 5))); |
361 | QCoreApplication::postEvent(receiver: &two, event: new QEvent(QEvent::Type(QEvent::User + 6))); |
362 | QCoreApplication::removePostedEvents(receiver: &one); |
363 | expected << QEvent::User + 4 |
364 | << QEvent::User + 5 |
365 | << QEvent::User + 6; |
366 | QCoreApplication::sendPostedEvents(); |
367 | QCOMPARE(spy.recordedEvents, expected); |
368 | spy.recordedEvents.clear(); |
369 | expected.clear(); |
370 | |
371 | // remove all events for all objects |
372 | QCoreApplication::postEvent(receiver: &one, event: new QEvent(QEvent::Type(QEvent::User + 7))); |
373 | QCoreApplication::postEvent(receiver: &two, event: new QEvent(QEvent::Type(QEvent::User + 8))); |
374 | QCoreApplication::postEvent(receiver: &one, event: new QEvent(QEvent::Type(QEvent::User + 9))); |
375 | QCoreApplication::postEvent(receiver: &two, event: new QEvent(QEvent::Type(QEvent::User + 10))); |
376 | QCoreApplication::postEvent(receiver: &one, event: new QEvent(QEvent::Type(QEvent::User + 11))); |
377 | QCoreApplication::postEvent(receiver: &two, event: new QEvent(QEvent::Type(QEvent::User + 12))); |
378 | QCoreApplication::removePostedEvents(receiver: 0); |
379 | QCoreApplication::sendPostedEvents(); |
380 | QVERIFY(spy.recordedEvents.isEmpty()); |
381 | |
382 | // remove a specific type of event for one object |
383 | QCoreApplication::postEvent(receiver: &one, event: new QEvent(QEvent::Type(QEvent::User + 13))); |
384 | QCoreApplication::postEvent(receiver: &two, event: new QEvent(QEvent::Type(QEvent::User + 14))); |
385 | QCoreApplication::postEvent(receiver: &one, event: new QEvent(QEvent::Type(QEvent::User + 15))); |
386 | QCoreApplication::postEvent(receiver: &two, event: new QEvent(QEvent::Type(QEvent::User + 16))); |
387 | QCoreApplication::postEvent(receiver: &one, event: new QEvent(QEvent::Type(QEvent::User + 17))); |
388 | QCoreApplication::postEvent(receiver: &two, event: new QEvent(QEvent::Type(QEvent::User + 18))); |
389 | QCoreApplication::removePostedEvents(receiver: &one, eventType: QEvent::User + 13); |
390 | QCoreApplication::removePostedEvents(receiver: &two, eventType: QEvent::User + 18); |
391 | QCoreApplication::sendPostedEvents(); |
392 | expected << QEvent::User + 14 |
393 | << QEvent::User + 15 |
394 | << QEvent::User + 16 |
395 | << QEvent::User + 17; |
396 | QCOMPARE(spy.recordedEvents, expected); |
397 | spy.recordedEvents.clear(); |
398 | expected.clear(); |
399 | |
400 | // remove a specific type of event for all objects |
401 | QCoreApplication::postEvent(receiver: &one, event: new QEvent(QEvent::Type(QEvent::User + 19))); |
402 | QCoreApplication::postEvent(receiver: &two, event: new QEvent(QEvent::Type(QEvent::User + 19))); |
403 | QCoreApplication::postEvent(receiver: &one, event: new QEvent(QEvent::Type(QEvent::User + 20))); |
404 | QCoreApplication::postEvent(receiver: &two, event: new QEvent(QEvent::Type(QEvent::User + 20))); |
405 | QCoreApplication::postEvent(receiver: &one, event: new QEvent(QEvent::Type(QEvent::User + 21))); |
406 | QCoreApplication::postEvent(receiver: &two, event: new QEvent(QEvent::Type(QEvent::User + 21))); |
407 | QCoreApplication::removePostedEvents(receiver: 0, eventType: QEvent::User + 20); |
408 | QCoreApplication::sendPostedEvents(); |
409 | expected << QEvent::User + 19 |
410 | << QEvent::User + 19 |
411 | << QEvent::User + 21 |
412 | << QEvent::User + 21; |
413 | QCOMPARE(spy.recordedEvents, expected); |
414 | spy.recordedEvents.clear(); |
415 | expected.clear(); |
416 | } |
417 | |
418 | #if QT_CONFIG(thread) |
419 | class DeliverInDefinedOrderThread : public QThread |
420 | { |
421 | Q_OBJECT |
422 | |
423 | public: |
424 | DeliverInDefinedOrderThread() |
425 | : QThread() |
426 | { } |
427 | |
428 | signals: |
429 | void progress(int); |
430 | |
431 | protected: |
432 | void run() |
433 | { |
434 | emit progress(1); |
435 | emit progress(2); |
436 | emit progress(3); |
437 | emit progress(4); |
438 | emit progress(5); |
439 | emit progress(6); |
440 | emit progress(7); |
441 | } |
442 | }; |
443 | |
444 | class DeliverInDefinedOrderObject : public QObject |
445 | { |
446 | Q_OBJECT |
447 | |
448 | QPointer<QThread> thread; |
449 | int count; |
450 | int startCount; |
451 | int loopLevel; |
452 | |
453 | public: |
454 | DeliverInDefinedOrderObject(QObject *parent) |
455 | : QObject(parent), thread(0), count(0), startCount(0), loopLevel(0) |
456 | { } |
457 | |
458 | signals: |
459 | void done(); |
460 | |
461 | public slots: |
462 | void startThread() |
463 | { |
464 | QVERIFY(!thread); |
465 | thread = new DeliverInDefinedOrderThread(); |
466 | connect(sender: thread, SIGNAL(progress(int)), receiver: this, SLOT(threadProgress(int))); |
467 | connect(sender: thread, SIGNAL(finished()), receiver: this, SLOT(threadFinished())); |
468 | connect(sender: thread, SIGNAL(destroyed()), receiver: this, SLOT(threadDestroyed())); |
469 | thread->start(); |
470 | |
471 | QCoreApplication::postEvent(receiver: this, event: new QEvent(QEvent::MaxUser), priority: -1); |
472 | } |
473 | |
474 | void threadProgress(int v) |
475 | { |
476 | ++count; |
477 | QCOMPARE(v, count); |
478 | |
479 | QCoreApplication::postEvent(receiver: this, event: new QEvent(QEvent::MaxUser), priority: -1); |
480 | } |
481 | |
482 | void threadFinished() |
483 | { |
484 | QCOMPARE(count, 7); |
485 | count = 0; |
486 | thread->deleteLater(); |
487 | |
488 | QCoreApplication::postEvent(receiver: this, event: new QEvent(QEvent::MaxUser), priority: -1); |
489 | } |
490 | |
491 | void threadDestroyed() |
492 | { |
493 | if (++startCount < 20) |
494 | startThread(); |
495 | else |
496 | emit done(); |
497 | } |
498 | |
499 | public: |
500 | bool event(QEvent *event) |
501 | { |
502 | switch (event->type()) { |
503 | case QEvent::User: |
504 | { |
505 | ++loopLevel; |
506 | if (loopLevel == 2) { |
507 | // Ready. Starts a thread that emits (queued) signals, which should be handled in order |
508 | startThread(); |
509 | } |
510 | QCoreApplication::postEvent(receiver: this, event: new QEvent(QEvent::MaxUser), priority: -1); |
511 | (void) QEventLoop().exec(); |
512 | break; |
513 | } |
514 | default: |
515 | break; |
516 | } |
517 | return QObject::event(event); |
518 | } |
519 | }; |
520 | |
521 | void tst_QCoreApplication::deliverInDefinedOrder() |
522 | { |
523 | int argc = 1; |
524 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
525 | TestApplication app(argc, argv); |
526 | |
527 | DeliverInDefinedOrderObject obj(&app); |
528 | // causes sendPostedEvents() to recurse twice |
529 | QCoreApplication::postEvent(receiver: &obj, event: new QEvent(QEvent::User)); |
530 | QCoreApplication::postEvent(receiver: &obj, event: new QEvent(QEvent::User)); |
531 | |
532 | QObject::connect(sender: &obj, SIGNAL(done()), receiver: &app, SLOT(quit())); |
533 | app.exec(); |
534 | } |
535 | #endif // QT_CONFIG(thread) |
536 | |
537 | void tst_QCoreApplication::applicationPid() |
538 | { |
539 | QVERIFY(QCoreApplication::applicationPid() > 0); |
540 | } |
541 | |
542 | QT_BEGIN_NAMESPACE |
543 | Q_CORE_EXPORT uint qGlobalPostedEventsCount(); |
544 | QT_END_NAMESPACE |
545 | |
546 | class GlobalPostedEventsCountObject : public QObject |
547 | { |
548 | Q_OBJECT |
549 | |
550 | public: |
551 | QList<int> globalPostedEventsCount; |
552 | |
553 | bool event(QEvent *event) |
554 | { |
555 | if (event->type() == QEvent::User) |
556 | globalPostedEventsCount.append(t: qGlobalPostedEventsCount()); |
557 | return QObject::event(event); |
558 | } |
559 | }; |
560 | |
561 | void tst_QCoreApplication::globalPostedEventsCount() |
562 | { |
563 | int argc = 1; |
564 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
565 | TestApplication app(argc, argv); |
566 | |
567 | QCoreApplication::sendPostedEvents(); |
568 | QCOMPARE(qGlobalPostedEventsCount(), 0u); |
569 | |
570 | GlobalPostedEventsCountObject x; |
571 | QCoreApplication::postEvent(receiver: &x, event: new QEvent(QEvent::User)); |
572 | QCoreApplication::postEvent(receiver: &x, event: new QEvent(QEvent::User)); |
573 | QCoreApplication::postEvent(receiver: &x, event: new QEvent(QEvent::User)); |
574 | QCoreApplication::postEvent(receiver: &x, event: new QEvent(QEvent::User)); |
575 | QCoreApplication::postEvent(receiver: &x, event: new QEvent(QEvent::User)); |
576 | QCOMPARE(qGlobalPostedEventsCount(), 5u); |
577 | |
578 | QCoreApplication::sendPostedEvents(); |
579 | QCOMPARE(qGlobalPostedEventsCount(), 0u); |
580 | |
581 | QList<int> expected = QList<int>() |
582 | << 4 |
583 | << 3 |
584 | << 2 |
585 | << 1 |
586 | << 0; |
587 | QCOMPARE(x.globalPostedEventsCount, expected); |
588 | } |
589 | |
590 | class ProcessEventsAlwaysSendsPostedEventsObject : public QObject |
591 | { |
592 | public: |
593 | int counter; |
594 | |
595 | inline ProcessEventsAlwaysSendsPostedEventsObject() |
596 | : counter(0) |
597 | { } |
598 | |
599 | bool event(QEvent *event) |
600 | { |
601 | if (event->type() == QEvent::User) |
602 | ++counter; |
603 | return QObject::event(event); |
604 | } |
605 | }; |
606 | |
607 | void tst_QCoreApplication::processEventsAlwaysSendsPostedEvents() |
608 | { |
609 | int argc = 1; |
610 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
611 | TestApplication app(argc, argv); |
612 | |
613 | ProcessEventsAlwaysSendsPostedEventsObject object; |
614 | QElapsedTimer t; |
615 | t.start(); |
616 | int i = 1; |
617 | do { |
618 | QCoreApplication::postEvent(receiver: &object, event: new QEvent(QEvent::User)); |
619 | QCoreApplication::processEvents(); |
620 | QCOMPARE(object.counter, i); |
621 | ++i; |
622 | } while (t.elapsed() < 1000); |
623 | } |
624 | |
625 | void tst_QCoreApplication::reexec() |
626 | { |
627 | int argc = 1; |
628 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
629 | TestApplication app(argc, argv); |
630 | |
631 | // exec once |
632 | QMetaObject::invokeMethod(obj: &app, member: "quit" , type: Qt::QueuedConnection); |
633 | QCOMPARE(app.exec(), 0); |
634 | |
635 | // and again |
636 | QMetaObject::invokeMethod(obj: &app, member: "quit" , type: Qt::QueuedConnection); |
637 | QCOMPARE(app.exec(), 0); |
638 | } |
639 | |
640 | void tst_QCoreApplication::execAfterExit() |
641 | { |
642 | int argc = 1; |
643 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
644 | TestApplication app(argc, argv); |
645 | |
646 | app.exit(retcode: 1); |
647 | QMetaObject::invokeMethod(obj: &app, member: "quit" , type: Qt::QueuedConnection); |
648 | QCOMPARE(app.exec(), 0); |
649 | } |
650 | |
651 | void tst_QCoreApplication::eventLoopExecAfterExit() |
652 | { |
653 | int argc = 1; |
654 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
655 | TestApplication app(argc, argv); |
656 | |
657 | // exec once and exit |
658 | QMetaObject::invokeMethod(obj: &app, member: "quit" , type: Qt::QueuedConnection); |
659 | QCOMPARE(app.exec(), 0); |
660 | |
661 | // and again, but this time using a QEventLoop |
662 | QEventLoop loop; |
663 | QMetaObject::invokeMethod(obj: &loop, member: "quit" , type: Qt::QueuedConnection); |
664 | QCOMPARE(loop.exec(), 0); |
665 | } |
666 | |
667 | class DummyEventDispatcher : public QAbstractEventDispatcher { |
668 | public: |
669 | DummyEventDispatcher() : QAbstractEventDispatcher(), visited(false) {} |
670 | bool processEvents(QEventLoop::ProcessEventsFlags) { |
671 | visited = true; |
672 | emit awake(); |
673 | QCoreApplication::sendPostedEvents(); |
674 | return false; |
675 | } |
676 | bool hasPendingEvents() { |
677 | return qGlobalPostedEventsCount(); |
678 | } |
679 | void registerSocketNotifier(QSocketNotifier *) {} |
680 | void unregisterSocketNotifier(QSocketNotifier *) {} |
681 | void registerTimer(int , int , Qt::TimerType, QObject *) {} |
682 | bool unregisterTimer(int ) { return false; } |
683 | bool unregisterTimers(QObject *) { return false; } |
684 | QList<TimerInfo> registeredTimers(QObject *) const { return QList<TimerInfo>(); } |
685 | int remainingTime(int) { return 0; } |
686 | void wakeUp() {} |
687 | void interrupt() {} |
688 | void flush() {} |
689 | |
690 | #ifdef Q_OS_WIN |
691 | bool registerEventNotifier(QWinEventNotifier *) { return false; } |
692 | void unregisterEventNotifier(QWinEventNotifier *) { } |
693 | #endif |
694 | |
695 | bool visited; |
696 | }; |
697 | |
698 | void tst_QCoreApplication::customEventDispatcher() |
699 | { |
700 | // there should be no ED yet |
701 | QVERIFY(!QCoreApplication::eventDispatcher()); |
702 | DummyEventDispatcher *ed = new DummyEventDispatcher; |
703 | QCoreApplication::setEventDispatcher(ed); |
704 | // the new ED should be set |
705 | QCOMPARE(QCoreApplication::eventDispatcher(), ed); |
706 | // test the alternative API of QAbstractEventDispatcher |
707 | QCOMPARE(QAbstractEventDispatcher::instance(), ed); |
708 | QPointer<DummyEventDispatcher> weak_ed(ed); |
709 | QVERIFY(!weak_ed.isNull()); |
710 | { |
711 | int argc = 1; |
712 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
713 | TestApplication app(argc, argv); |
714 | // instantiating app should not overwrite the ED |
715 | QCOMPARE(QCoreApplication::eventDispatcher(), ed); |
716 | QMetaObject::invokeMethod(obj: &app, member: "quit" , type: Qt::QueuedConnection); |
717 | app.exec(); |
718 | // the custom ED has really been used? |
719 | QVERIFY(ed->visited); |
720 | } |
721 | // ED has been deleted? |
722 | QVERIFY(weak_ed.isNull()); |
723 | } |
724 | |
725 | class JobObject : public QObject |
726 | { |
727 | Q_OBJECT |
728 | public: |
729 | |
730 | explicit JobObject(QEventLoop *loop, QObject *parent = 0) |
731 | : QObject(parent), locker(loop) |
732 | { |
733 | QTimer::singleShot(msec: 1000, receiver: this, SLOT(timeout())); |
734 | } |
735 | |
736 | explicit JobObject(QObject *parent = 0) |
737 | : QObject(parent) |
738 | { |
739 | QTimer::singleShot(msec: 1000, receiver: this, SLOT(timeout())); |
740 | } |
741 | |
742 | public slots: |
743 | void startSecondaryJob() |
744 | { |
745 | new JobObject(); |
746 | } |
747 | |
748 | private slots: |
749 | void timeout() |
750 | { |
751 | emit done(); |
752 | deleteLater(); |
753 | } |
754 | |
755 | signals: |
756 | void done(); |
757 | |
758 | private: |
759 | QEventLoopLocker locker; |
760 | }; |
761 | |
762 | class QuitTester : public QObject |
763 | { |
764 | Q_OBJECT |
765 | public: |
766 | QuitTester(QObject *parent = 0) |
767 | : QObject(parent) |
768 | { |
769 | QTimer::singleShot(msec: 0, receiver: this, SLOT(doTest())); |
770 | } |
771 | |
772 | private slots: |
773 | void doTest() |
774 | { |
775 | QCoreApplicationPrivate *privateClass = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(qApp)); |
776 | |
777 | { |
778 | QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 0); |
779 | // Test with a lock active so that the refcount doesn't drop to zero during these tests, causing a quit. |
780 | // (until we exit the scope) |
781 | QEventLoopLocker locker; |
782 | |
783 | QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 1); |
784 | |
785 | JobObject *job1 = new JobObject(this); |
786 | |
787 | QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 2); |
788 | |
789 | delete job1; |
790 | |
791 | QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 1); |
792 | |
793 | job1 = new JobObject(this); |
794 | |
795 | QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 2); |
796 | |
797 | JobObject *job2 = new JobObject(this); |
798 | |
799 | QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 3); |
800 | |
801 | delete job1; |
802 | |
803 | QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 2); |
804 | |
805 | JobObject *job3 = new JobObject(job2); |
806 | Q_UNUSED(job3); |
807 | |
808 | QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 3); |
809 | |
810 | JobObject *job4 = new JobObject(job2); |
811 | Q_UNUSED(job4); |
812 | |
813 | QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 4); |
814 | |
815 | delete job2; |
816 | |
817 | QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 1); |
818 | |
819 | } |
820 | QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 0); |
821 | } |
822 | }; |
823 | |
824 | void tst_QCoreApplication::testQuitLock() |
825 | { |
826 | int argc = 1; |
827 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
828 | TestApplication app(argc, argv); |
829 | |
830 | QuitTester tester; |
831 | app.exec(); |
832 | } |
833 | |
834 | |
835 | void tst_QCoreApplication::QTBUG31606_QEventDestructorDeadLock() |
836 | { |
837 | class MyEvent : public QEvent |
838 | { public: |
839 | MyEvent() : QEvent(QEvent::Type(QEvent::User + 1)) {} |
840 | ~MyEvent() { |
841 | QCoreApplication::postEvent(qApp, event: new QEvent(QEvent::Type(QEvent::User+2))); |
842 | } |
843 | }; |
844 | |
845 | int argc = 1; |
846 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
847 | TestApplication app(argc, argv); |
848 | |
849 | EventSpy spy; |
850 | app.installEventFilter(filterObj: &spy); |
851 | |
852 | QCoreApplication::postEvent(receiver: &app, event: new MyEvent); |
853 | QCoreApplication::processEvents(); |
854 | QVERIFY(spy.recordedEvents.contains(QEvent::User + 1)); |
855 | QVERIFY(!spy.recordedEvents.contains(QEvent::User + 2)); |
856 | QCoreApplication::processEvents(); |
857 | QVERIFY(spy.recordedEvents.contains(QEvent::User + 2)); |
858 | } |
859 | |
860 | // this is almost identical to sendEventsOnProcessEvents |
861 | void tst_QCoreApplication::applicationEventFilters_mainThread() |
862 | { |
863 | int argc = 1; |
864 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
865 | TestApplication app(argc, argv); |
866 | |
867 | EventSpy spy; |
868 | app.installEventFilter(filterObj: &spy); |
869 | |
870 | QCoreApplication::postEvent(receiver: &app, event: new QEvent(QEvent::Type(QEvent::User + 1))); |
871 | QTimer::singleShot(msec: 10, receiver: &app, SLOT(quit())); |
872 | app.exec(); |
873 | QVERIFY(spy.recordedEvents.contains(QEvent::User + 1)); |
874 | } |
875 | |
876 | void tst_QCoreApplication::applicationEventFilters_auxThread() |
877 | { |
878 | int argc = 1; |
879 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
880 | TestApplication app(argc, argv); |
881 | QThread thread; |
882 | ThreadedEventReceiver receiver; |
883 | receiver.moveToThread(thread: &thread); |
884 | |
885 | EventSpy spy; |
886 | app.installEventFilter(filterObj: &spy); |
887 | |
888 | // this is very similar to sendEventsOnProcessEvents |
889 | QCoreApplication::postEvent(receiver: &receiver, event: new QEvent(QEvent::Type(QEvent::User + 1))); |
890 | QTimer::singleShot(msec: 1000, receiver: &app, SLOT(quit())); |
891 | thread.start(); |
892 | app.exec(); |
893 | QVERIFY(thread.wait(1000)); |
894 | QVERIFY(receiver.recordedEvents.contains(QEvent::User + 1)); |
895 | QVERIFY(!spy.recordedEvents.contains(QEvent::User + 1)); |
896 | } |
897 | |
898 | void tst_QCoreApplication::threadedEventDelivery_data() |
899 | { |
900 | QTest::addColumn<bool>(name: "requiresCoreApplication" ); |
901 | QTest::addColumn<bool>(name: "createCoreApplication" ); |
902 | QTest::addColumn<bool>(name: "eventsReceived" ); |
903 | |
904 | // invalid combination: |
905 | //QTest::newRow("default-without-coreapp") << true << false << false; |
906 | QTest::newRow(dataTag: "default" ) << true << true << true; |
907 | QTest::newRow(dataTag: "independent-without-coreapp" ) << false << false << true; |
908 | QTest::newRow(dataTag: "independent-with-coreapp" ) << false << true << true; |
909 | } |
910 | |
911 | // posts the event before the QCoreApplication is destroyed, starts thread after |
912 | void tst_QCoreApplication::threadedEventDelivery() |
913 | { |
914 | QFETCH(bool, requiresCoreApplication); |
915 | QFETCH(bool, createCoreApplication); |
916 | QFETCH(bool, eventsReceived); |
917 | |
918 | int argc = 1; |
919 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
920 | QScopedPointer<TestApplication> app(createCoreApplication ? new TestApplication(argc, argv) : 0); |
921 | |
922 | Thread thread; |
923 | thread.requiresCoreApplication = requiresCoreApplication; |
924 | ThreadedEventReceiver receiver; |
925 | receiver.moveToThread(thread: &thread); |
926 | QCoreApplication::postEvent(receiver: &receiver, event: new QEvent(QEvent::Type(QEvent::User + 1))); |
927 | |
928 | thread.start(); |
929 | QVERIFY(thread.wait(1000)); |
930 | QCOMPARE(receiver.recordedEvents.contains(QEvent::User + 1), eventsReceived); |
931 | |
932 | } |
933 | |
934 | void tst_QCoreApplication::testTrWithPercantegeAtTheEnd() |
935 | { |
936 | QCoreApplication::translate(context: "testcontext" , key: "this will crash%" , disambiguation: "testdisamb" , n: 3); |
937 | } |
938 | |
939 | #if QT_CONFIG(library) |
940 | void tst_QCoreApplication::addRemoveLibPaths() |
941 | { |
942 | QStringList paths = QCoreApplication::libraryPaths(); |
943 | if (paths.isEmpty()) |
944 | QSKIP("Cannot add/remove library paths if there are none." ); |
945 | |
946 | QString currentDir = QDir().absolutePath(); |
947 | QCoreApplication::addLibraryPath(currentDir); |
948 | QVERIFY(QCoreApplication::libraryPaths().contains(currentDir)); |
949 | |
950 | QCoreApplication::removeLibraryPath(paths[0]); |
951 | QVERIFY(!QCoreApplication::libraryPaths().contains(paths[0])); |
952 | |
953 | int argc = 1; |
954 | char *argv[] = { const_cast<char*>(QTest::currentAppName()) }; |
955 | TestApplication app(argc, argv); |
956 | |
957 | // If libraryPaths only contains currentDir, neither will be in libraryPaths now. |
958 | if (paths.length() != 1 && currentDir != paths[0]) { |
959 | // Check that modifications stay alive across the creation of an application. |
960 | QVERIFY(QCoreApplication::libraryPaths().contains(currentDir)); |
961 | QVERIFY(!QCoreApplication::libraryPaths().contains(paths[0])); |
962 | } |
963 | |
964 | QStringList replace; |
965 | replace << currentDir << paths[0]; |
966 | QCoreApplication::setLibraryPaths(replace); |
967 | QCOMPARE(QCoreApplication::libraryPaths(), replace); |
968 | } |
969 | #endif |
970 | |
971 | static void createQObjectOnDestruction() |
972 | { |
973 | // Make sure that we can create a QObject after the last QObject has been |
974 | // destroyed (especially after QCoreApplication has). |
975 | // |
976 | // Before the fixes, this would cause a dangling pointer dereference. If |
977 | // the problem comes back, it's possible that the following causes no |
978 | // effect. |
979 | QObject obj; |
980 | obj.thread()->setProperty(name: "testing" , value: 1); |
981 | } |
982 | Q_DESTRUCTOR_FUNCTION(createQObjectOnDestruction) |
983 | |
984 | #ifndef QT_GUI_LIB |
985 | QTEST_APPLESS_MAIN(tst_QCoreApplication) |
986 | #endif |
987 | |
988 | #include "tst_qcoreapplication.moc" |
989 | |