diff options
Diffstat (limited to 'tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp')
-rw-r--r-- | tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp | 302 |
1 files changed, 265 insertions, 37 deletions
diff --git a/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp b/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp index fa374ee076..13f971f5f0 100644 --- a/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp +++ b/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp @@ -1,34 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "../../../shared/highdpi.h" -#include <QtTest/QtTest> +#include <QTest> +#include <QTestEventLoop> #include <qdialog.h> #include <qapplication.h> @@ -36,7 +12,9 @@ #include <qpushbutton.h> #include <qstyle.h> #include <QVBoxLayout> +#include <QSignalSpy> #include <QSizeGrip> +#include <QTimer> #include <QGraphicsProxyWidget> #include <QGraphicsView> #include <QWindow> @@ -44,6 +22,8 @@ #include <qpa/qplatformtheme.h> #include <qpa/qplatformtheme_p.h> +#include <QtWidgets/private/qapplication_p.h> + QT_FORWARD_DECLARE_CLASS(QDialog) // work around function being protected @@ -67,6 +47,8 @@ private slots: void showMinimized(); void showFullScreen(); void showAsTool(); + void showWithoutActivating_data(); + void showWithoutActivating(); void toolDialogPosition(); void deleteMainDefault(); void deleteInExec(); @@ -79,6 +61,11 @@ private slots: void transientParent_data(); void transientParent(); void dialogInGraphicsView(); + void keepPositionOnClose(); + void virtualsOnClose(); + void deleteOnDone(); + void quitOnDone(); + void focusWidgetAfterOpen(); }; // Testing get/set functions @@ -98,18 +85,20 @@ void tst_QDialog::getSetCheck() class ToolDialog : public QDialog { public: - ToolDialog(QWidget *parent = 0) + ToolDialog(QWidget *parent = nullptr) : QDialog(parent, Qt::Tool), mWasActive(false), mWasModalWindow(false), tId(-1) {} bool wasActive() const { return mWasActive; } bool wasModalWindow() const { return mWasModalWindow; } - int exec() { + int exec() override + { tId = startTimer(300); return QDialog::exec(); } protected: - void timerEvent(QTimerEvent *event) { + void timerEvent(QTimerEvent *event) override + { if (tId == event->timerId()) { killTimer(tId); mWasActive = isActiveWindow(); @@ -146,7 +135,7 @@ void tst_QDialog::defaultButtons() pushThree->setAutoDefault(false); testWidget.show(); - QApplication::setActiveWindow(&testWidget); + QApplicationPrivate::setActiveWindow(&testWidget); QVERIFY(QTest::qWaitForWindowExposed(&testWidget)); push->setDefault(true); @@ -295,9 +284,12 @@ void tst_QDialog::showFullScreen() void tst_QDialog::showAsTool() { -#if defined(Q_OS_UNIX) - QSKIP("Qt/X11: Skipped since activeWindow() is not respected by all window managers"); -#endif + if (QStringList{"xcb", "offscreen"}.contains(QGuiApplication::platformName())) + QSKIP("activeWindow() is not respected by all Xcb window managers and the offscreen plugin"); + + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + DummyDialog testWidget; testWidget.resize(200, 200); testWidget.setWindowTitle(QTest::currentTestFunction()); @@ -313,6 +305,33 @@ void tst_QDialog::showAsTool() } } +void tst_QDialog::showWithoutActivating_data() +{ + QTest::addColumn<bool>("showWithoutActivating"); + QTest::addColumn<int>("focusInCount"); + + QTest::addRow("showWithoutActivating") << true << 0; + QTest::addRow("showWithActivating") << false << 1; +} + +void tst_QDialog::showWithoutActivating() +{ + QFETCH(bool, showWithoutActivating); + QFETCH(int, focusInCount); + + struct Dialog : public QDialog + { + int focusInCount = 0; + protected: + void focusInEvent(QFocusEvent *) override { ++focusInCount; } + } dialog; + dialog.setAttribute(Qt::WA_ShowWithoutActivating, showWithoutActivating); + + dialog.show(); + QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + QCOMPARE(dialog.focusInCount, focusInCount); +} + // Verify that pos() returns the same before and after show() // for a dialog with the Tool window type. void tst_QDialog::toolDialogPosition() @@ -321,7 +340,7 @@ void tst_QDialog::toolDialogPosition() dialog.move(QPoint(100,100)); const QPoint beforeShowPosition = dialog.pos(); dialog.show(); - const int fuzz = int(dialog.devicePixelRatioF()); + const int fuzz = int(dialog.devicePixelRatio()); const QPoint afterShowPosition = dialog.pos(); QVERIFY2(HighDpi::fuzzyCompare(afterShowPosition, beforeShowPosition, fuzz), HighDpi::msgPointMismatch(afterShowPosition, beforeShowPosition).constData()); @@ -414,7 +433,7 @@ class TestRejectDialog : public QDialog { public: TestRejectDialog() : cancelReject(false), called(0) {} - void reject() + void reject() override { called++; if (!cancelReject) @@ -465,6 +484,9 @@ void tst_QDialog::snapToDefaultButton() #else if (!QGuiApplication::platformName().compare(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("This platform does not support setting the cursor position."); +#ifdef Q_OS_ANDROID + QSKIP("Android does not support cursor"); +#endif const QRect dialogGeometry(QGuiApplication::primaryScreen()->availableGeometry().topLeft() + QPoint(100, 100), QSize(200, 200)); @@ -540,5 +562,211 @@ void tst_QDialog::dialogInGraphicsView() } } +// QTBUG-79147 (Windows): Closing a dialog by clicking the 'X' in the title +// bar would offset the dialog position when shown next time. +void tst_QDialog::keepPositionOnClose() +{ + QDialog dialog; + dialog.setWindowTitle(QTest::currentTestFunction()); + const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); + dialog.resize(availableGeometry.size() / 4); + QPoint pos = availableGeometry.topLeft() + QPoint(100, 100); + dialog.move(pos); + dialog.show(); + QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + pos = dialog.pos(); + dialog.close(); + dialog.windowHandle()->destroy(); // Emulate a click on close by destroying the window. + QTest::qWait(50); + dialog.show(); + QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + QTest::qWait(50); + 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(); + // we used to only hide the dialog, and we still don't want a + // closeEvent call for application-triggered calls to QDialog::done + QCOMPARE(dialog.closeEventCount, 0); + 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); + 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.size(), 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.size(), 1); +} + +void tst_QDialog::focusWidgetAfterOpen() +{ + QDialog dialog; + dialog.setLayout(new QVBoxLayout); + + QPushButton *pb1 = new QPushButton; + QPushButton *pb2 = new QPushButton; + dialog.layout()->addWidget(pb1); + dialog.layout()->addWidget(pb2); + + pb2->setFocus(); + QCOMPARE(dialog.focusWidget(), static_cast<QWidget *>(pb2)); + + dialog.open(); + QCOMPARE(dialog.focusWidget(), static_cast<QWidget *>(pb2)); +} + QTEST_MAIN(tst_QDialog) #include "tst_qdialog.moc" |