/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include class tst_QCoreApplication: public QObject { Q_OBJECT private slots: void sendEventsOnProcessEvents(); // this must be the first test void getSetCheck(); void qAppName(); void argc(); void postEvent(); void removePostedEvents(); #ifndef QT_NO_THREAD void deliverInDefinedOrder(); #endif void applicationPid(); void globalPostedEventsCount(); void processEventsAlwaysSendsPostedEvents(); void reexec(); void execAfterExit(); void eventLoopExecAfterExit(); void customEventDispatcher(); void testQuitLock(); }; class EventSpy : public QObject { Q_OBJECT public: QList recordedEvents; bool eventFilter(QObject *, QEvent *event) { recordedEvents.append(event->type()); return false; } }; void tst_QCoreApplication::sendEventsOnProcessEvents() { int argc = 1; char *argv[] = { "tst_qcoreapplication" }; QCoreApplication app(argc, argv); EventSpy spy; app.installEventFilter(&spy); QCoreApplication::postEvent(&app, new QEvent(QEvent::Type(QEvent::User + 1))); QCoreApplication::processEvents(); QVERIFY(spy.recordedEvents.contains(QEvent::User + 1)); } void tst_QCoreApplication::getSetCheck() { // do not crash QString v = QCoreApplication::applicationVersion(); v = QLatin1String("3.0.0 prerelease 1"); QCoreApplication::setApplicationVersion(v); QCOMPARE(QCoreApplication::applicationVersion(), v); // Test the property { int argc = 1; char *argv[] = { "tst_qcoreapplication" }; QCoreApplication app(argc, argv); QCOMPARE(app.property("applicationVersion").toString(), v); } v = QString(); QCoreApplication::setApplicationVersion(v); QCOMPARE(QCoreApplication::applicationVersion(), v); } void tst_QCoreApplication::qAppName() { int argc = 1; char *argv[] = { "tst_qcoreapplication" }; QCoreApplication app(argc, argv); QVERIFY(!::qAppName().isEmpty()); } void tst_QCoreApplication::argc() { #ifdef Q_OS_WIN QSKIP("QCoreApplication::arguments() always parses arguments from actual command line in Windows, making this test invalid."); #endif { int argc = 1; char *argv[] = { "tst_qcoreapplication" }; QCoreApplication app(argc, argv); QCOMPARE(argc, 1); QCOMPARE(app.arguments().count(), 1); } { int argc = 4; char *argv[] = { "tst_qcoreapplication", "arg1", "arg2", "arg3" }; QCoreApplication app(argc, argv); QCOMPARE(argc, 4); QCOMPARE(app.arguments().count(), 4); } { int argc = 0; char **argv = 0; QCoreApplication app(argc, argv); QCOMPARE(argc, 0); QCOMPARE(app.arguments().count(), 0); } { int argc = 2; char *argv[] = { "tst_qcoreapplication", "-qmljsdebugger=port:3768,block" }; QCoreApplication app(argc, argv); QCOMPARE(argc, 1); QCOMPARE(app.arguments().count(), 1); } } class EventGenerator : public QObject { Q_OBJECT public: QObject *other; bool event(QEvent *e) { if (e->type() == QEvent::MaxUser) { QCoreApplication::sendPostedEvents(other, 0); } else if (e->type() <= QEvent::User + 999) { // post a new event in response to this posted event int offset = e->type() - QEvent::User; offset = (offset * 10 + offset % 10); QCoreApplication::postEvent(this, new QEvent(QEvent::Type(QEvent::User + offset)), offset); } return QObject::event(e); } }; void tst_QCoreApplication::postEvent() { int argc = 1; char *argv[] = { "tst_qcoreapplication" }; QCoreApplication app(argc, argv); EventSpy spy; EventGenerator odd, even; odd.other = &even; odd.installEventFilter(&spy); even.other = &odd; even.installEventFilter(&spy); QCoreApplication::postEvent(&odd, new QEvent(QEvent::Type(QEvent::User + 1))); QCoreApplication::postEvent(&even, new QEvent(QEvent::Type(QEvent::User + 2))); QCoreApplication::postEvent(&odd, new QEvent(QEvent::Type(QEvent::User + 3)), 1); QCoreApplication::postEvent(&even, new QEvent(QEvent::Type(QEvent::User + 4)), 2); QCoreApplication::postEvent(&odd, new QEvent(QEvent::Type(QEvent::User + 5)), -2); QCoreApplication::postEvent(&even, new QEvent(QEvent::Type(QEvent::User + 6)), -1); QList expected; expected << QEvent::User + 4 << QEvent::User + 3 << QEvent::User + 1 << QEvent::User + 2 << QEvent::User + 6 << QEvent::User + 5; QCoreApplication::sendPostedEvents(); // live lock protection ensures that we only send the initial events QCOMPARE(spy.recordedEvents, expected); expected.clear(); expected << QEvent::User + 66 << QEvent::User + 55 << QEvent::User + 44 << QEvent::User + 33 << QEvent::User + 22 << QEvent::User + 11; spy.recordedEvents.clear(); QCoreApplication::sendPostedEvents(); // expect next sequence events QCOMPARE(spy.recordedEvents, expected); // have the generators call sendPostedEvents() on each other in // response to an event QCoreApplication::postEvent(&odd, new QEvent(QEvent::MaxUser), INT_MAX); QCoreApplication::postEvent(&even, new QEvent(QEvent::MaxUser), INT_MAX); expected.clear(); expected << int(QEvent::MaxUser) << int(QEvent::MaxUser) << QEvent::User + 555 << QEvent::User + 333 << QEvent::User + 111 << QEvent::User + 666 << QEvent::User + 444 << QEvent::User + 222; spy.recordedEvents.clear(); QCoreApplication::sendPostedEvents(); QCOMPARE(spy.recordedEvents, expected); expected.clear(); expected << QEvent::User + 6666 << QEvent::User + 5555 << QEvent::User + 4444 << QEvent::User + 3333 << QEvent::User + 2222 << QEvent::User + 1111; spy.recordedEvents.clear(); QCoreApplication::sendPostedEvents(); QCOMPARE(spy.recordedEvents, expected); // no more events expected.clear(); spy.recordedEvents.clear(); QCoreApplication::sendPostedEvents(); QCOMPARE(spy.recordedEvents, expected); } void tst_QCoreApplication::removePostedEvents() { int argc = 1; char *argv[] = { "tst_qcoreapplication" }; QCoreApplication app(argc, argv); EventSpy spy; QObject one, two; one.installEventFilter(&spy); two.installEventFilter(&spy); QList expected; // remove all events for one object QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 1))); QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 2))); QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 3))); QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 4))); QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 5))); QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 6))); QCoreApplication::removePostedEvents(&one); expected << QEvent::User + 4 << QEvent::User + 5 << QEvent::User + 6; QCoreApplication::sendPostedEvents(); QCOMPARE(spy.recordedEvents, expected); spy.recordedEvents.clear(); expected.clear(); // remove all events for all objects QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 7))); QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 8))); QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 9))); QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 10))); QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 11))); QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 12))); QCoreApplication::removePostedEvents(0); QCoreApplication::sendPostedEvents(); QVERIFY(spy.recordedEvents.isEmpty()); // remove a specific type of event for one object QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 13))); QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 14))); QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 15))); QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 16))); QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 17))); QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 18))); QCoreApplication::removePostedEvents(&one, QEvent::User + 13); QCoreApplication::removePostedEvents(&two, QEvent::User + 18); QCoreApplication::sendPostedEvents(); expected << QEvent::User + 14 << QEvent::User + 15 << QEvent::User + 16 << QEvent::User + 17; QCOMPARE(spy.recordedEvents, expected); spy.recordedEvents.clear(); expected.clear(); // remove a specific type of event for all objects QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 19))); QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 19))); QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 20))); QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 20))); QCoreApplication::postEvent(&one, new QEvent(QEvent::Type(QEvent::User + 21))); QCoreApplication::postEvent(&two, new QEvent(QEvent::Type(QEvent::User + 21))); QCoreApplication::removePostedEvents(0, QEvent::User + 20); QCoreApplication::sendPostedEvents(); expected << QEvent::User + 19 << QEvent::User + 19 << QEvent::User + 21 << QEvent::User + 21; QCOMPARE(spy.recordedEvents, expected); spy.recordedEvents.clear(); expected.clear(); } #ifndef QT_NO_THREAD class DeliverInDefinedOrderThread : public QThread { Q_OBJECT public: DeliverInDefinedOrderThread() : QThread() { } signals: void progress(int); protected: void run() { emit progress(1); emit progress(2); emit progress(3); emit progress(4); emit progress(5); emit progress(6); emit progress(7); } }; class DeliverInDefinedOrderObject : public QObject { Q_OBJECT QPointer thread; int count; int startCount; int loopLevel; public: DeliverInDefinedOrderObject(QObject *parent) : QObject(parent), thread(0), count(0), startCount(0), loopLevel(0) { } signals: void done(); public slots: void startThread() { QVERIFY(!thread); thread = new DeliverInDefinedOrderThread(); connect(thread, SIGNAL(progress(int)), this, SLOT(threadProgress(int))); connect(thread, SIGNAL(finished()), this, SLOT(threadFinished())); connect(thread, SIGNAL(destroyed()), this, SLOT(threadDestroyed())); thread->start(); QCoreApplication::postEvent(this, new QEvent(QEvent::MaxUser), -1); } void threadProgress(int v) { ++count; QVERIFY(v == count); QCoreApplication::postEvent(this, new QEvent(QEvent::MaxUser), -1); } void threadFinished() { QVERIFY(count == 7); count = 0; thread->deleteLater(); QCoreApplication::postEvent(this, new QEvent(QEvent::MaxUser), -1); } void threadDestroyed() { if (++startCount < 20) startThread(); else emit done(); } public: bool event(QEvent *event) { switch (event->type()) { case QEvent::User: { ++loopLevel; if (loopLevel == 2) { // Ready. Starts a thread that emits (queued) signals, which should be handled in order startThread(); } QCoreApplication::postEvent(this, new QEvent(QEvent::MaxUser), -1); (void) QEventLoop().exec(); break; } default: break; } return QObject::event(event); } }; void tst_QCoreApplication::deliverInDefinedOrder() { int argc = 1; char *argv[] = { "tst_qcoreapplication" }; QCoreApplication app(argc, argv); DeliverInDefinedOrderObject obj(&app); // causes sendPostedEvents() to recurse twice QCoreApplication::postEvent(&obj, new QEvent(QEvent::User)); QCoreApplication::postEvent(&obj, new QEvent(QEvent::User)); QObject::connect(&obj, SIGNAL(done()), &app, SLOT(quit())); app.exec(); } #endif // QT_NO_QTHREAD void tst_QCoreApplication::applicationPid() { QVERIFY(QCoreApplication::applicationPid() > 0); } QT_BEGIN_NAMESPACE Q_CORE_EXPORT uint qGlobalPostedEventsCount(); QT_END_NAMESPACE class GlobalPostedEventsCountObject : public QObject { Q_OBJECT public: QList globalPostedEventsCount; bool event(QEvent *event) { if (event->type() == QEvent::User) globalPostedEventsCount.append(qGlobalPostedEventsCount()); return QObject::event(event); } }; void tst_QCoreApplication::globalPostedEventsCount() { int argc = 1; char *argv[] = { "tst_qcoreapplication" }; QCoreApplication app(argc, argv); QCoreApplication::sendPostedEvents(); QCOMPARE(qGlobalPostedEventsCount(), 0u); GlobalPostedEventsCountObject x; QCoreApplication::postEvent(&x, new QEvent(QEvent::User)); QCoreApplication::postEvent(&x, new QEvent(QEvent::User)); QCoreApplication::postEvent(&x, new QEvent(QEvent::User)); QCoreApplication::postEvent(&x, new QEvent(QEvent::User)); QCoreApplication::postEvent(&x, new QEvent(QEvent::User)); QCOMPARE(qGlobalPostedEventsCount(), 5u); QCoreApplication::sendPostedEvents(); QCOMPARE(qGlobalPostedEventsCount(), 0u); QList expected = QList() << 4 << 3 << 2 << 1 << 0; QCOMPARE(x.globalPostedEventsCount, expected); } class ProcessEventsAlwaysSendsPostedEventsObject : public QObject { public: int counter; inline ProcessEventsAlwaysSendsPostedEventsObject() : counter(0) { } bool event(QEvent *event) { if (event->type() == QEvent::User) ++counter; return QObject::event(event); } }; void tst_QCoreApplication::processEventsAlwaysSendsPostedEvents() { int argc = 1; char *argv[] = { "tst_qcoreapplication" }; QCoreApplication app(argc, argv); ProcessEventsAlwaysSendsPostedEventsObject object; QTime t; t.start(); int i = 1; do { QCoreApplication::postEvent(&object, new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(object.counter, i); ++i; } while (t.elapsed() < 1000); } void tst_QCoreApplication::reexec() { int argc = 1; char *argv[] = { "tst_qcoreapplication" }; QCoreApplication app(argc, argv); // exec once QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); QCOMPARE(app.exec(), 0); // and again QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); QCOMPARE(app.exec(), 0); } void tst_QCoreApplication::execAfterExit() { int argc = 1; char *argv[] = { "tst_qcoreapplication" }; QCoreApplication app(argc, argv); app.exit(1); QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); QCOMPARE(app.exec(), 0); } void tst_QCoreApplication::eventLoopExecAfterExit() { int argc = 1; char *argv[] = { "tst_qcoreapplication" }; QCoreApplication app(argc, argv); // exec once and exit QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); QCOMPARE(app.exec(), 0); // and again, but this time using a QEventLoop QEventLoop loop; QMetaObject::invokeMethod(&loop, "quit", Qt::QueuedConnection); QCOMPARE(loop.exec(), 0); } class DummyEventDispatcher : public QAbstractEventDispatcher { public: DummyEventDispatcher() : QAbstractEventDispatcher(), visited(false) {} bool processEvents(QEventLoop::ProcessEventsFlags) { visited = true; emit awake(); QCoreApplication::sendPostedEvents(); return false; } bool hasPendingEvents() { return qGlobalPostedEventsCount(); } void registerSocketNotifier(QSocketNotifier *) {} void unregisterSocketNotifier(QSocketNotifier *) {} void registerTimer(int , int , Qt::TimerType, QObject *) {} bool unregisterTimer(int ) { return false; } bool unregisterTimers(QObject *) { return false; } QList registeredTimers(QObject *) const { return QList(); } void wakeUp() {} void interrupt() {} void flush() {} bool visited; }; void tst_QCoreApplication::customEventDispatcher() { // there should be no ED yet QVERIFY(!QCoreApplication::eventDispatcher()); DummyEventDispatcher *ed = new DummyEventDispatcher; QCoreApplication::setEventDispatcher(ed); // the new ED should be set QCOMPARE(QCoreApplication::eventDispatcher(), ed); // test the alternative API of QAbstractEventDispatcher QCOMPARE(QAbstractEventDispatcher::instance(), ed); QWeakPointer weak_ed(ed); QVERIFY(!weak_ed.isNull()); { int argc = 1; char *arg0 = "tst_qcoreapplication"; char *argv[] = { arg0 }; QCoreApplication app(argc, argv); // instantiating app should not overwrite the ED QCOMPARE(QCoreApplication::eventDispatcher(), ed); QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); app.exec(); // the custom ED has really been used? QVERIFY(ed->visited); } // ED has been deleted? QVERIFY(weak_ed.isNull()); } class JobObject : public QObject { Q_OBJECT public: explicit JobObject(QEventLoop *loop, QObject *parent = 0) : QObject(parent), locker(loop) { QTimer::singleShot(1000, this, SLOT(timeout())); } explicit JobObject(QObject *parent = 0) : QObject(parent) { QTimer::singleShot(1000, this, SLOT(timeout())); } public slots: void startSecondaryJob() { new JobObject(); } private slots: void timeout() { emit done(); deleteLater(); } signals: void done(); private: QEventLoopLocker locker; }; class QuitTester : public QObject { Q_OBJECT public: QuitTester(QObject *parent = 0) : QObject(parent) { QTimer::singleShot(0, this, SLOT(doTest())); } private slots: void doTest() { QCoreApplicationPrivate *privateClass = static_cast(QObjectPrivate::get(qApp)); { QCOMPARE(privateClass->quitLockRef.load(), 0); // Test with a lock active so that the refcount doesn't drop to zero during these tests, causing a quit. // (until we exit the scope) QEventLoopLocker locker; QCOMPARE(privateClass->quitLockRef.load(), 1); JobObject *job1 = new JobObject(this); QCOMPARE(privateClass->quitLockRef.load(), 2); delete job1; QCOMPARE(privateClass->quitLockRef.load(), 1); job1 = new JobObject(this); QCOMPARE(privateClass->quitLockRef.load(), 2); JobObject *job2 = new JobObject(this); QCOMPARE(privateClass->quitLockRef.load(), 3); delete job1; QCOMPARE(privateClass->quitLockRef.load(), 2); JobObject *job3 = new JobObject(job2); QCOMPARE(privateClass->quitLockRef.load(), 3); JobObject *job4 = new JobObject(job2); QCOMPARE(privateClass->quitLockRef.load(), 4); delete job2; QCOMPARE(privateClass->quitLockRef.load(), 1); } QCOMPARE(privateClass->quitLockRef.load(), 0); } }; void tst_QCoreApplication::testQuitLock() { int argc = 1; char *argv[] = { "tst_qcoreapplication" }; QCoreApplication app(argc, argv); QuitTester tester; app.exec(); } QTEST_APPLESS_MAIN(tst_QCoreApplication) #include "tst_qcoreapplication.moc"