summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
diff options
context:
space:
mode:
authorStephen Kelly <stephen.kelly@kdab.com>2011-10-26 13:29:51 +0200
committerQt by Nokia <qt-info@nokia.com>2012-01-31 23:22:15 +0100
commita512e210ac5b032c5fc2edf1ddf72e5a414485fd (patch)
tree0e9615f317fe5c24bd002155b205a01d5716a1d3 /tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
parent36a590c90ddab6fa438b7b54b05d89e8f9698a52 (diff)
Add the event loop quitlock feature to QtCore.
A feature of a ref-counted quit (managed by a quit-lock class) is added to both QEventLoop and QCoreApplication. This allows, for example, an event loop to quit() when there is no more work for it to do. quitOnLastWindowClosed is implemented in terms of the refcount in QCoreApplication so that jobs can be completed before the application quits. Change-Id: I14c8f4e7ee12bbf81a6e5849290d4c8ff37fa110 Reviewed-by: David Faure <faure@kde.org> Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
Diffstat (limited to 'tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp')
-rw-r--r--tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp516
1 files changed, 515 insertions, 1 deletions
diff --git a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
index ac8f2a34ce..4b8034c7c7 100644
--- a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
+++ b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
@@ -49,6 +49,7 @@
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
#include <QtCore/QProcess>
+#include <QtCore/private/qeventloop_p.h>
#include <QtGui/QFontDatabase>
#include <QtGui/QClipboard>
@@ -138,6 +139,16 @@ private slots:
void qtbug_12673();
+ void testQuitLockRef();
+ void testQuitLock1();
+ void testQuitLock2();
+ void testQuitLock3();
+ void testQuitLock4();
+ void testQuitLock5();
+ void testQuitLock6();
+ void testQuitLock7();
+ void testQuitLock8();
+
void globalStaticObjectDestruction(); // run this last
};
@@ -189,7 +200,6 @@ public:
dialog.show();
dialog.close();
- hide();
event->ignore();
}
};
@@ -516,6 +526,7 @@ void tst_QApplication::lastWindowClosed()
QPointer<CloseWidget>widget = new CloseWidget;
QVERIFY(widget->testAttribute(Qt::WA_QuitOnClose));
+ widget->show();
QObject::connect(&app, SIGNAL(lastWindowClosed()), widget, SLOT(deleteLater()));
app.exec();
QVERIFY(!widget);
@@ -2048,6 +2059,509 @@ void tst_QApplication::qtbug_12673()
QCOMPARE(testProcess.exitStatus(), QProcess::NormalExit);
}
+class JobObject : public QObject
+{
+ Q_OBJECT
+public:
+ JobObject(int milliseconds, QObject *parent = 0)
+ : QObject(parent)
+ {
+ QTimer::singleShot(milliseconds, this, SLOT(timeout()));
+ }
+
+ JobObject(QObject *parent = 0)
+ : QObject(parent)
+ {
+ QTimer::singleShot(1000, this, SLOT(timeout()));
+ }
+
+private slots:
+ void timeout()
+ {
+ emit done();
+ deleteLater();
+ }
+
+signals:
+ void done();
+
+private:
+ QEventLoopLocker locker;
+};
+
+class QuitLockRefTester : public QObject
+{
+ Q_OBJECT
+public:
+ QuitLockRefTester(QObject *parent = 0)
+ : QObject(parent)
+ {
+ QTimer::singleShot(0, this, SLOT(doTest()));
+ }
+
+private slots:
+ void doTest()
+ {
+ QApplicationPrivate *privateClass = static_cast<QApplicationPrivate*>(QObjectPrivate::get(qApp));
+
+ {
+ QDialog *win1 = new QDialog;
+
+ // 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);
+
+ win1->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ QDialog *win2 = new QDialog;
+
+ win2->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 3);
+
+ delete win1;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ delete win2;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ win1 = new QDialog;
+
+ win1->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ JobObject *job1 = new JobObject(this);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 3);
+
+ delete win1;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ delete job1;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ QWidget *w1 = new QWidget;
+
+ w1->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ QWidget *w2 = new QMainWindow;
+
+ w2->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 3);
+
+ QWidget *w3 = new QWidget(0, Qt::Dialog);
+
+ w3->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 4);
+
+ delete w3;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 3);
+
+ delete w2;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ QWidget *subWidget1 = new QWidget(w1, Qt::Window);
+
+ // Even though We create a new widget and show it,
+ // the ref count does not go up because it is a child of
+ // w1, which is the top-level, and what we are actually
+ // refcounting.
+ subWidget1->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ // When we use setParent(0) and re-show, the
+ // ref count does increase:
+ QCOMPARE(subWidget1->isVisible(), true);
+ subWidget1->setParent(0);
+ QCOMPARE(subWidget1->isVisible(), false);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ subWidget1->show();
+ QCOMPARE(subWidget1->isVisible(), true);
+ QCOMPARE(privateClass->quitLockRef.load(), 3);
+
+ subWidget1->setParent(w1);
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ QWidget *subWidget2 = new QWidget(w1);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ subWidget2->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ delete subWidget2;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ QWidget *subWidget3 = new QWidget(w1);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ subWidget3->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ subWidget3->hide();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ delete subWidget3;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ QWidget *subWidget4 = new QWidget(subWidget1);
+ QWidget *subWidget5 = new QWidget(subWidget1);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ QWidget *subWidget6 = new QWidget(subWidget4, Qt::Window);
+
+ subWidget6->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ delete w1;
+
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ w1 = new QWidget;
+ w2 = new QWidget;
+ w3 = new QWidget;
+
+ QHBoxLayout *layout = new QHBoxLayout(w1);
+
+ layout->addWidget(w2);
+ layout->addWidget(w3);
+
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ w1->show();
+
+ QCOMPARE(privateClass->quitLockRef.load(), 2);
+
+ w1->hide();
+ QCOMPARE(privateClass->quitLockRef.load(), 1);
+
+ delete w1;
+
+ }
+ QCOMPARE(privateClass->quitLockRef.load(), 0);
+ }
+};
+
+void tst_QApplication::testQuitLockRef()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qapplication" };
+ QApplication app(argc, argv);
+
+ QuitLockRefTester tester;
+
+ app.exec();
+}
+
+void tst_QApplication::testQuitLock1()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+
+ QWidget *w = new QWidget;
+
+ w->show();
+
+ QMetaObject::invokeMethod(w, "close", Qt::QueuedConnection);
+
+ app.exec();
+
+ // No hang = pass.
+}
+
+void tst_QApplication::testQuitLock2()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+
+ QWidget *w1 = new QWidget;
+
+ w1->show();
+
+ QWidget *w2 = new QWidget;
+
+ w2->show();
+
+ QMetaObject::invokeMethod(w1, "deleteLater", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(w2, "hide", Qt::QueuedConnection);
+
+ app.exec();
+
+ // No hang = pass.
+}
+
+class Result : public QObject
+{
+ Q_OBJECT
+public:
+ Result(QObject *parent = 0)
+ : QObject(parent), m_passes(false)
+ {
+
+ }
+
+ bool result() const
+ {
+ return m_passes;
+ }
+
+public slots:
+
+ void setPasses()
+ {
+ setResult(true);
+ }
+
+ void setFails()
+ {
+ setResult(false);
+ }
+
+ void setResult(bool result)
+ {
+ m_passes = result;
+ }
+
+private:
+ bool m_passes;
+};
+
+void tst_QApplication::testQuitLock3()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+
+ Result *result = new Result(&app);
+
+ JobObject *job = new JobObject(&app);
+
+ QObject::connect(job, SIGNAL(done()), result, SLOT(setPasses()));
+
+ app.exec();
+
+ QVERIFY(result->result());
+}
+
+void tst_QApplication::testQuitLock4()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+
+ QWidget *w = new QWidget;
+
+ w->show();
+
+ Result *result = new Result(&app);
+ JobObject *job = new JobObject(1000, &app);
+
+ QTimer::singleShot(500, w, SLOT(deleteLater()));
+
+ QObject::connect(w, SIGNAL(destroyed()), result, SLOT(setFails()));
+ QObject::connect(job, SIGNAL(done()), result, SLOT(setPasses()));
+
+ app.exec();
+
+ QVERIFY(result->result());
+}
+
+class JobBeforeWindowRunner : public QObject
+{
+ Q_OBJECT
+public:
+ JobBeforeWindowRunner(QObject *parent = 0)
+ : QObject(parent), m_result(new Result(this))
+ {
+
+ }
+
+ void start()
+ {
+ JobObject *job = new JobObject(this);
+ connect(job, SIGNAL(done()), m_result, SLOT(setFails()));
+ connect(job, SIGNAL(destroyed()), SLOT(showWindowDelayed()), Qt::QueuedConnection);
+ }
+
+ bool result() const { return m_result->result(); }
+
+private slots:
+ void showWindowDelayed()
+ {
+ qApp->setQuitLockEnabled(true);
+ QTimer::singleShot(500, this, SLOT(showWindow()));
+ }
+
+ void showWindow()
+ {
+ QWidget *w = new QWidget;
+ w->show();
+ w->deleteLater();
+ connect(w, SIGNAL(destroyed()), m_result, SLOT(setPasses()));
+ }
+
+private:
+ Result * const m_result;
+};
+
+void tst_QApplication::testQuitLock5()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+ app.setQuitLockEnabled(false);
+ // Run a job before showing a window, and only enable the refcounting
+ // after doing so.
+ // Although the job brings the refcount to zero, the app does not exit
+ // until setQuitLockEnabled is called and the feature re-enabled.
+
+ JobBeforeWindowRunner *eventRunner = new JobBeforeWindowRunner(&app);
+
+ eventRunner->start();
+
+ app.exec();
+
+ QVERIFY(eventRunner->result());
+}
+
+class JobDuringWindowRunner : public QObject
+{
+ Q_OBJECT
+public:
+ JobDuringWindowRunner(QObject *parent = 0)
+ : QObject(parent), m_result(new Result(this))
+ {
+
+ }
+
+ void start()
+ {
+ JobObject *job = new JobObject(this);
+
+ QWidget *w = new QWidget;
+ w->show();
+ w->deleteLater();
+
+ QObject::connect(w, SIGNAL(destroyed()), m_result, SLOT(setFails()));
+ QObject::connect(job, SIGNAL(done()), m_result, SLOT(setPasses()));
+ }
+
+ bool result() const { return m_result->result(); }
+
+private:
+ Result * const m_result;
+};
+
+void tst_QApplication::testQuitLock6()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+
+ // A job runs, and while it is running, a window is shown and closed,
+ // then the job ends, which causes the quit.
+
+ JobDuringWindowRunner *eventRunner = new JobDuringWindowRunner(&app);
+
+ eventRunner->start();
+
+ app.exec();
+
+ QVERIFY(eventRunner->result());
+}
+class JobWindowJobWindowRunner : public QObject
+{
+ Q_OBJECT
+public:
+ JobWindowJobWindowRunner(QObject *parent = 0)
+ : QObject(parent), m_result(new Result(this))
+ {
+
+ }
+
+ void start()
+ {
+ JobObject *job = new JobObject(500, this);
+
+ QWidget *w = new QWidget;
+ w->show();
+ QTimer::singleShot(1000, w, SLOT(deleteLater()));
+
+ QObject::connect(w, SIGNAL(destroyed()), m_result, SLOT(setPasses()));
+ QObject::connect(job, SIGNAL(done()), m_result, SLOT(setFails()));
+ }
+
+ bool result() const { return m_result->result(); }
+private:
+ Result * const m_result;
+};
+
+void tst_QApplication::testQuitLock7()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+
+ // A job runs, and while it is running, a window is shown
+ // then the job ends, then the window is closed, which causes the quit.
+
+ JobWindowJobWindowRunner *eventRunner = new JobWindowJobWindowRunner(&app);
+
+ eventRunner->start();
+
+ app.exec();
+
+ QVERIFY(eventRunner->result());
+}
+
+void tst_QApplication::testQuitLock8()
+{
+ int argc = 1;
+ char *argv[] = { "tst_qcoreapplication" };
+ QApplication app(argc, argv);
+
+ QMainWindow *mw1 = new QMainWindow;
+ mw1->show();
+ QMainWindow *mw2 = new QMainWindow;
+ mw2->show();
+
+ QMetaObject::invokeMethod(mw1, "close", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(mw2, "close", Qt::QueuedConnection);
+
+ app.exec();
+
+ // No hang = pass
+}
+
+
/*
This test is meant to ensure that certain objects (public & commonly used)
can safely be used in a Q_GLOBAL_STATIC such that their destructors are