summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVolker Hilsheimer <volker.hilsheimer@qt.io>2021-09-04 09:51:11 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2021-09-05 23:14:52 +0000
commit406f33e24706e92532efa2afeac40f13421db463 (patch)
tree4555df1f15be3b717c7fae3190a8b3b19da7ed23
parentd46f5dc66157abdf55f2d746829958a9a9121343 (diff)
Add more QDialog unit test related to closing a dialog
QDialog today only hides itself during reject and accept, it doesn't close itself properly. This is problematic, as it doesn't close the QWindow. However, fixing this behavior must not result in duplicate calls to virtual function, or additional calls to virtual functions (such as closeEvent) without explicitly flagging the change in the changelog. Add more tests to document existing behavior so that we can identify such changes and verify the desired side effects. Change-Id: I1f30701cd766eb3c7957751b51e8579d4542dd16 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io> (cherry picked from commit d2cad026c7c354ee94595c3a5f99e046c437d2aa) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp170
1 files changed, 170 insertions, 0 deletions
diff --git a/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp b/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp
index 6a59edca10..1dd053fa3f 100644
--- a/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp
+++ b/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp
@@ -37,7 +37,9 @@
#include <qpushbutton.h>
#include <qstyle.h>
#include <QVBoxLayout>
+#include <QSignalSpy>
#include <QSizeGrip>
+#include <QTimer>
#include <QGraphicsProxyWidget>
#include <QGraphicsView>
#include <QWindow>
@@ -81,6 +83,9 @@ private slots:
void transientParent();
void dialogInGraphicsView();
void keepPositionOnClose();
+ void virtualsOnClose();
+ void deleteOnDone();
+ void quitOnDone();
};
// Testing get/set functions
@@ -566,5 +571,170 @@ void tst_QDialog::keepPositionOnClose()
QCOMPARE(dialog.pos(), pos);
}
+/*!
+ Verify that the virtual functions related to closing a dialog are
+ called exactly once, no matter how the dialog gets closed.
+*/
+void tst_QDialog::virtualsOnClose()
+{
+ class Dialog : public QDialog
+ {
+ public:
+ using QDialog::QDialog;
+ int closeEventCount = 0;
+ int acceptCount = 0;
+ int rejectCount = 0;
+ int doneCount = 0;
+
+ void accept() override
+ {
+ ++acceptCount;
+ QDialog::accept();
+ }
+ void reject() override
+ {
+ ++rejectCount;
+ QDialog::reject();
+ }
+ void done(int result) override
+ {
+ ++doneCount;
+ QDialog::done(result);
+ }
+
+ protected:
+ void closeEvent(QCloseEvent *e) override
+ {
+ ++closeEventCount;
+ QDialog::closeEvent(e);
+ }
+ };
+
+ {
+ Dialog dialog;
+ dialog.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&dialog));
+ dialog.accept();
+ QCOMPARE(dialog.closeEventCount, 0); // we only hide the dialog
+ QCOMPARE(dialog.acceptCount, 1);
+ QCOMPARE(dialog.rejectCount, 0);
+ QCOMPARE(dialog.doneCount, 1);
+ }
+
+ {
+ Dialog dialog;
+ dialog.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&dialog));
+ dialog.reject();
+ QCOMPARE(dialog.closeEventCount, 0); // we only hide the dialog
+ QCOMPARE(dialog.acceptCount, 0);
+ QCOMPARE(dialog.rejectCount, 1);
+ QCOMPARE(dialog.doneCount, 1);
+ }
+
+ {
+ Dialog dialog;
+ dialog.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&dialog));
+ dialog.close();
+ QCOMPARE(dialog.closeEventCount, 1);
+ QCOMPARE(dialog.acceptCount, 0);
+ QCOMPARE(dialog.rejectCount, 1);
+ QCOMPARE(dialog.doneCount, 1);
+ }
+
+ {
+ // user clicks close button in title bar
+ Dialog dialog;
+ dialog.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&dialog));
+ QWindowSystemInterface::handleCloseEvent(dialog.windowHandle());
+ QApplication::processEvents();
+ QCOMPARE(dialog.closeEventCount, 1);
+ QCOMPARE(dialog.acceptCount, 0);
+ QCOMPARE(dialog.rejectCount, 1);
+ QCOMPARE(dialog.doneCount, 1);
+ }
+
+ {
+ struct EventFilter : QObject {
+ EventFilter(Dialog *dialog)
+ { dialog->installEventFilter(this); }
+ int closeEventCount = 0;
+ bool eventFilter(QObject *r, QEvent *e) override
+ {
+ if (e->type() == QEvent::Close) {
+ ++closeEventCount;
+ }
+ return QObject::eventFilter(r, e);
+ }
+ };
+ // dialog gets destroyed while shown
+ Dialog *dialog = new Dialog;
+ QSignalSpy rejectedSpy(dialog, &QDialog::rejected);
+ EventFilter filter(dialog);
+
+ dialog->show();
+ QVERIFY(QTest::qWaitForWindowExposed(dialog));
+ delete dialog;
+ // Qt doesn't deliver events to QWidgets closed during destruction
+ QCOMPARE(filter.closeEventCount, 0);
+ // QDialog doesn't emit signals when closed by destruction
+ QCOMPARE(rejectedSpy.count(), 0);
+ }
+}
+
+/*!
+ QDialog::done is documented to respect Qt::WA_DeleteOnClose.
+*/
+void tst_QDialog::deleteOnDone()
+{
+ {
+ std::unique_ptr<QDialog> dialog(new QDialog);
+ QPointer<QDialog> watcher(dialog.get());
+ dialog->setAttribute(Qt::WA_DeleteOnClose);
+ dialog->show();
+ QVERIFY(QTest::qWaitForWindowExposed(dialog.get()));
+
+ dialog->accept();
+ QTRY_COMPARE(watcher.isNull(), true);
+ dialog.release(); // if we get here, the dialog is destroyed
+ }
+
+ // it is still safe to delete the dialog explicitly as long as events
+ // have not yet been processed
+ {
+ std::unique_ptr<QDialog> dialog(new QDialog);
+ dialog->setAttribute(Qt::WA_DeleteOnClose);
+ dialog->show();
+ QVERIFY(QTest::qWaitForWindowExposed(dialog.get()));
+
+ dialog->accept();
+ dialog.reset();
+ QApplication::processEvents();
+ }
+}
+
+/*!
+ QDialog::done is documented to make QApplication emit lastWindowClosed if
+ the dialog was the last window.
+*/
+void tst_QDialog::quitOnDone()
+{
+ QSignalSpy quitSpy(qApp, &QGuiApplication::lastWindowClosed);
+
+ QDialog dialog;
+ dialog.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&dialog));
+
+ // QGuiApplication::lastWindowClosed is documented to only be emitted
+ // when we are in exec()
+ QTimer::singleShot(0, &dialog, &QDialog::accept);
+ // also quit with a timer in case the test fails
+ QTimer::singleShot(1000, QApplication::instance(), &QApplication::quit);
+ QApplication::exec();
+ QCOMPARE(quitSpy.count(), 1);
+}
+
QTEST_MAIN(tst_QDialog)
#include "tst_qdialog.moc"