diff options
Diffstat (limited to 'tests/auto/widgets/widgets')
113 files changed, 3081 insertions, 618 deletions
diff --git a/tests/auto/widgets/widgets/CMakeLists.txt b/tests/auto/widgets/widgets/CMakeLists.txt index 8fe11d09ac..eb44f3d103 100644 --- a/tests/auto/widgets/widgets/CMakeLists.txt +++ b/tests/auto/widgets/widgets/CMakeLists.txt @@ -29,6 +29,7 @@ add_subdirectory(qscrollbar) add_subdirectory(qsizegrip) add_subdirectory(qslider) add_subdirectory(qspinbox) +add_subdirectory(qsplashscreen) add_subdirectory(qsplitter) add_subdirectory(qstackedwidget) add_subdirectory(qstatusbar) @@ -58,3 +59,4 @@ endif() if(QT_FEATURE_opengl) add_subdirectory(qopenglwidget) endif() +add_subdirectory(qrhiwidget) diff --git a/tests/auto/widgets/widgets/qabstractbutton/CMakeLists.txt b/tests/auto/widgets/widgets/qabstractbutton/CMakeLists.txt index 327edc6868..883614fc04 100644 --- a/tests/auto/widgets/widgets/qabstractbutton/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qabstractbutton/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qabstractbutton Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qabstractbutton LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qabstractbutton SOURCES tst_qabstractbutton.cpp diff --git a/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp b/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp index c2bcbe73f5..cb91f0c6e6 100644 --- a/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp +++ b/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -480,7 +480,6 @@ void tst_QAbstractButton::setShortcut() QKeySequence seq( Qt::Key_A ); testWidget->setShortcut( seq ); - QApplicationPrivate::setActiveWindow(testWidget); testWidget->activateWindow(); // must be active to get shortcuts QVERIFY(QTest::qWaitForWindowActive(testWidget)); diff --git a/tests/auto/widgets/widgets/qabstractscrollarea/CMakeLists.txt b/tests/auto/widgets/widgets/qabstractscrollarea/CMakeLists.txt index 34272b3375..ac1d8ad54a 100644 --- a/tests/auto/widgets/widgets/qabstractscrollarea/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qabstractscrollarea/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qabstractscrollarea Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qabstractscrollarea LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qabstractscrollarea SOURCES tst_qabstractscrollarea.cpp diff --git a/tests/auto/widgets/widgets/qabstractscrollarea/tst_qabstractscrollarea.cpp b/tests/auto/widgets/widgets/qabstractscrollarea/tst_qabstractscrollarea.cpp index 86c42a6522..fa1f799855 100644 --- a/tests/auto/widgets/widgets/qabstractscrollarea/tst_qabstractscrollarea.cpp +++ b/tests/auto/widgets/widgets/qabstractscrollarea/tst_qabstractscrollarea.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -13,6 +13,7 @@ #include <qwidget.h> #include <qdialog.h> #include <qscroller.h> +#include <qstyle.h> class tst_QAbstractScrollArea : public QObject { @@ -34,6 +35,7 @@ private slots: void margins(); void resizeWithOvershoot(); + void sizeHint(); }; tst_QAbstractScrollArea::tst_QAbstractScrollArea() @@ -408,5 +410,56 @@ void tst_QAbstractScrollArea::resizeWithOvershoot() QTRY_COMPARE(scrollArea.viewport()->pos(), originAtRest); } +void tst_QAbstractScrollArea::sizeHint() +{ + class ScrollArea : public QAbstractScrollArea + { + public: + QSize viewportSizeHint() const override { return {200, 200}; } + } scrollArea; + // We cannot reliable test the impact of the scrollbars on the size hint + // if the style uses transient scrollbars, so use the class Windows style. + const QString defaultStyle = QApplication::style()->name(); + QApplication::setStyle("Windows"); + auto resetStyle = qScopeGuard([defaultStyle]{ + QApplication::setStyle(defaultStyle); + }); + scrollArea.setFrameShape(QFrame::NoFrame); + scrollArea.setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); + scrollArea.show(); + + QSize sizeHint = scrollArea.sizeHint(); + QCOMPARE(sizeHint, scrollArea.viewportSizeHint()); + + scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + const QSize sizeHintWithScrollBars = scrollArea.sizeHint(); + QTRY_COMPARE_GT(sizeHintWithScrollBars.width(), sizeHint.width()); + QTRY_COMPARE_GT(sizeHintWithScrollBars.height(), sizeHint.height()); + + sizeHint = scrollArea.sizeHint(); + + // whether the scroll area itself is visible or not should not influence + // the size hint + scrollArea.hide(); + QCOMPARE(scrollArea.sizeHint(), sizeHint); + scrollArea.show(); + QCOMPARE(scrollArea.sizeHint(), sizeHint); + + scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + QCOMPARE(scrollArea.sizeHint(), scrollArea.viewportSizeHint()); + + scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + + scrollArea.verticalScrollBar()->setRange(0, 1); + scrollArea.horizontalScrollBar()->setRange(0, 1); + scrollArea.resize(sizeHint / 2); + QApplication::processEvents(); // trigger lazy layout process + QCOMPARE(scrollArea.sizeHint(), sizeHintWithScrollBars); +} + QTEST_MAIN(tst_QAbstractScrollArea) #include "tst_qabstractscrollarea.moc" diff --git a/tests/auto/widgets/widgets/qabstractslider/CMakeLists.txt b/tests/auto/widgets/widgets/qabstractslider/CMakeLists.txt index 8d1c7d5afe..711c73931d 100644 --- a/tests/auto/widgets/widgets/qabstractslider/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qabstractslider/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qabstractslider Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qabstractslider LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qabstractslider SOURCES tst_qabstractslider.cpp diff --git a/tests/auto/widgets/widgets/qabstractslider/tst_qabstractslider.cpp b/tests/auto/widgets/widgets/qabstractslider/tst_qabstractslider.cpp index 170c29922a..9be41ad799 100644 --- a/tests/auto/widgets/widgets/qabstractslider/tst_qabstractslider.cpp +++ b/tests/auto/widgets/widgets/qabstractslider/tst_qabstractslider.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qabstractspinbox/CMakeLists.txt b/tests/auto/widgets/widgets/qabstractspinbox/CMakeLists.txt index f692c6b162..adaef01601 100644 --- a/tests/auto/widgets/widgets/qabstractspinbox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qabstractspinbox/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qabstractspinbox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qabstractspinbox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qabstractspinbox SOURCES tst_qabstractspinbox.cpp diff --git a/tests/auto/widgets/widgets/qabstractspinbox/tst_qabstractspinbox.cpp b/tests/auto/widgets/widgets/qabstractspinbox/tst_qabstractspinbox.cpp index 6c3eaa5e9c..00d0fdaf94 100644 --- a/tests/auto/widgets/widgets/qabstractspinbox/tst_qabstractspinbox.cpp +++ b/tests/auto/widgets/widgets/qabstractspinbox/tst_qabstractspinbox.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qbuttongroup/CMakeLists.txt b/tests/auto/widgets/widgets/qbuttongroup/CMakeLists.txt index 6b3492930d..b58775d2ff 100644 --- a/tests/auto/widgets/widgets/qbuttongroup/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qbuttongroup/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qbuttongroup Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qbuttongroup LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qbuttongroup SOURCES tst_qbuttongroup.cpp diff --git a/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp b/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp index 7ab2868e9a..3ff748ef27 100644 --- a/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp +++ b/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -122,7 +122,6 @@ void tst_QButtonGroup::arrowKeyNavigation() layout.addWidget(&g2); dlg.show(); - QApplicationPrivate::setActiveWindow(&dlg); QVERIFY(QTest::qWaitForWindowActive(&dlg)); bt1.setFocus(); @@ -204,7 +203,6 @@ void tst_QButtonGroup::keyNavigationPushButtons() buttonGroup->addButton(pb3); dlg.show(); - QApplicationPrivate::setActiveWindow(&dlg); if (!QTest::qWaitForWindowActive(&dlg)) QSKIP("Window activation failed, skipping test"); @@ -435,7 +433,6 @@ void tst_QButtonGroup::task106609() qRegisterMetaType<QAbstractButton*>("QAbstractButton*"); QSignalSpy spy1(buttons, SIGNAL(buttonClicked(QAbstractButton*))); - QApplicationPrivate::setActiveWindow(&dlg); QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&dlg)); radio1->setFocus(); diff --git a/tests/auto/widgets/widgets/qcalendarwidget/CMakeLists.txt b/tests/auto/widgets/widgets/qcalendarwidget/CMakeLists.txt index a1608d6bbc..5140c37e94 100644 --- a/tests/auto/widgets/widgets/qcalendarwidget/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qcalendarwidget/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qcalendarwidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcalendarwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qcalendarwidget SOURCES tst_qcalendarwidget.cpp diff --git a/tests/auto/widgets/widgets/qcalendarwidget/tst_qcalendarwidget.cpp b/tests/auto/widgets/widgets/qcalendarwidget/tst_qcalendarwidget.cpp index 2fd9d1b48d..ca5cc06e99 100644 --- a/tests/auto/widgets/widgets/qcalendarwidget/tst_qcalendarwidget.cpp +++ b/tests/auto/widgets/widgets/qcalendarwidget/tst_qcalendarwidget.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qcheckbox/CMakeLists.txt b/tests/auto/widgets/widgets/qcheckbox/CMakeLists.txt index b0cb425de8..9e3f0e9874 100644 --- a/tests/auto/widgets/widgets/qcheckbox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qcheckbox/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qcheckbox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcheckbox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qcheckbox SOURCES tst_qcheckbox.cpp diff --git a/tests/auto/widgets/widgets/qcheckbox/tst_qcheckbox.cpp b/tests/auto/widgets/widgets/qcheckbox/tst_qcheckbox.cpp index 9055b13901..42eb81d3c7 100644 --- a/tests/auto/widgets/widgets/qcheckbox/tst_qcheckbox.cpp +++ b/tests/auto/widgets/widgets/qcheckbox/tst_qcheckbox.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -16,6 +16,7 @@ private slots: void initTestCase(); void setChecked(); + void setCheckedSignal(); void setTriState(); void setText_data(); void setText(); @@ -25,7 +26,7 @@ private slots: void toggle(); void pressed(); void toggled(); - void stateChanged(); + void checkStateChanged(); void foregroundRole(); void minimumSizeHint(); }; @@ -59,6 +60,25 @@ void tst_QCheckBox::setChecked() QVERIFY(!testWidget.isChecked()); } +void tst_QCheckBox::setCheckedSignal() +{ + QCheckBox testWidget; + testWidget.setCheckState(Qt::Unchecked); + QSignalSpy checkStateChangedSpy(&testWidget, &QCheckBox::checkStateChanged); + testWidget.setCheckState(Qt::Checked); + testWidget.setCheckState(Qt::Checked); + QTRY_COMPARE(checkStateChangedSpy.size(), 1); // get signal only once + QCOMPARE(testWidget.checkState(), Qt::Checked); + testWidget.setCheckState(Qt::Unchecked); + testWidget.setCheckState(Qt::Unchecked); + QTRY_COMPARE(checkStateChangedSpy.size(), 2); // get signal only once + QCOMPARE(testWidget.checkState(), Qt::Unchecked); + testWidget.setCheckState(Qt::PartiallyChecked); + testWidget.setCheckState(Qt::PartiallyChecked); + QTRY_COMPARE(checkStateChangedSpy.size(), 3); // get signal only once + QCOMPARE(testWidget.checkState(), Qt::PartiallyChecked); +} + void tst_QCheckBox::setTriState() { QCheckBox testWidget; @@ -188,35 +208,49 @@ void tst_QCheckBox::toggled() QCOMPARE(click_count, 0); } -void tst_QCheckBox::stateChanged() +void tst_QCheckBox::checkStateChanged() { QCheckBox testWidget; QCOMPARE(testWidget.checkState(), Qt::Unchecked); Qt::CheckState cur_state = Qt::Unchecked; + QSignalSpy checkStateChangedSpy(&testWidget, &QCheckBox::checkStateChanged); +#if QT_DEPRECATED_SINCE(6, 9) + QT_IGNORE_DEPRECATIONS( QSignalSpy stateChangedSpy(&testWidget, &QCheckBox::stateChanged); - connect(&testWidget, &QCheckBox::stateChanged, this, [&](auto state) { cur_state = Qt::CheckState(state); }); + ) +#endif + connect(&testWidget, &QCheckBox::checkStateChanged, this, [&](auto state) { cur_state = state; }); testWidget.setChecked(true); - QVERIFY(QTest::qWaitFor([&]() { return stateChangedSpy.size() == 1; })); + QTRY_COMPARE(checkStateChangedSpy.size(), 1); +#if QT_DEPRECATED_SINCE(6, 9) QCOMPARE(stateChangedSpy.size(), 1); +#endif QCOMPARE(cur_state, Qt::Checked); QCOMPARE(testWidget.checkState(), Qt::Checked); testWidget.setChecked(false); - QVERIFY(QTest::qWaitFor([&]() { return stateChangedSpy.size() == 2; })); + QTRY_COMPARE(checkStateChangedSpy.size(), 2); +#if QT_DEPRECATED_SINCE(6, 9) QCOMPARE(stateChangedSpy.size(), 2); +#endif QCOMPARE(cur_state, Qt::Unchecked); QCOMPARE(testWidget.checkState(), Qt::Unchecked); testWidget.setCheckState(Qt::PartiallyChecked); - QVERIFY(QTest::qWaitFor([&]() { return stateChangedSpy.size() == 3; })); + QTRY_COMPARE(checkStateChangedSpy.size(), 3); +#if QT_DEPRECATED_SINCE(6, 9) QCOMPARE(stateChangedSpy.size(), 3); +#endif QCOMPARE(cur_state, Qt::PartiallyChecked); QCOMPARE(testWidget.checkState(), Qt::PartiallyChecked); testWidget.setCheckState(Qt::PartiallyChecked); QCoreApplication::processEvents(); + QCOMPARE(checkStateChangedSpy.size(), 3); +#if QT_DEPRECATED_SINCE(6, 9) QCOMPARE(stateChangedSpy.size(), 3); +#endif } void tst_QCheckBox::isToggleButton() diff --git a/tests/auto/widgets/widgets/qcombobox/CMakeLists.txt b/tests/auto/widgets/widgets/qcombobox/CMakeLists.txt index c52beca6ae..87ac247b24 100644 --- a/tests/auto/widgets/widgets/qcombobox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qcombobox/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qcombobox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcombobox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data list(APPEND test_data "qtlogo.png") list(APPEND test_data "qtlogoinverted.png") diff --git a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp index 245c4c8ca4..66c1359bd8 100644 --- a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp +++ b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> @@ -42,11 +42,13 @@ #include <qstandarditemmodel.h> #include <qproxystyle.h> #include <qfont.h> +#include <qstylehints.h> #include "../../../shared/platforminputcontext.h" #include <private/qinputmethod_p.h> #include <QtTest/private/qtesthelpers_p.h> +#include <QtTest/private/qemulationdetector_p.h> #include <QtWidgets/private/qapplication_p.h> @@ -109,6 +111,7 @@ private slots: #ifndef QT_NO_STYLE_FUSION void task190351_layout(); void task191329_size(); + void popupPositionAfterStyleChange(); #endif void task166349_setEditableOnReturn(); void task190205_setModelAdjustToContents(); @@ -152,6 +155,7 @@ private slots: void buttonPressKeys(); void clearModel(); void cancelClosesPopupNotDialog(); + void closePopupWithCheckableItems(); private: PlatformInputContext m_platformInputContext; @@ -845,7 +849,6 @@ void tst_QComboBox::autoCompletionCaseSensitivity() TestWidget topLevel; topLevel.show(); QComboBox *testWidget = topLevel.comboBox(); - QApplicationPrivate::setActiveWindow(&topLevel); testWidget->setFocus(); QVERIFY(QTest::qWaitForWindowActive(&topLevel)); QCOMPARE(qApp->focusWidget(), (QWidget *)testWidget); @@ -1283,11 +1286,12 @@ void tst_QComboBox::insertItem_data() initialItems << "foo" << "bar"; for(int e = 0 ; e<2 ; e++) { bool editable = (e==0); - QTest::newRow("Insert less then 0") << initialItems << -1 << "inserted" << 0 << editable; - QTest::newRow("Insert at 0") << initialItems << 0 << "inserted" << 0 << editable; - QTest::newRow("Insert beyond count") << initialItems << 3 << "inserted" << 2 << editable; - QTest::newRow("Insert at count") << initialItems << 2 << "inserted" << 2 << editable; - QTest::newRow("Insert in the middle") << initialItems << 1 << "inserted" << 1 << editable; + const auto txt = editable ? QByteArray("editable: ") : QByteArray("non-editable: "); + QTest::newRow(txt + "Insert less then 0") << initialItems << -1 << "inserted" << 0 << editable; + QTest::newRow(txt + "Insert at 0") << initialItems << 0 << "inserted" << 0 << editable; + QTest::newRow(txt + "Insert beyond count") << initialItems << 3 << "inserted" << 2 << editable; + QTest::newRow(txt + "Insert at count") << initialItems << 2 << "inserted" << 2 << editable; + QTest::newRow(txt + "Insert in the middle") << initialItems << 1 << "inserted" << 1 << editable; } } @@ -1502,42 +1506,70 @@ void tst_QComboBox::currentTextChanged() testWidget->addItems(QStringList() << "foo" << "bar"); QCOMPARE(testWidget->count(), 2); - QSignalSpy spy(testWidget, SIGNAL(currentTextChanged(QString))); + QSignalSpy textChangedSpy(testWidget, &QComboBox::currentTextChanged); testWidget->setEditable(editable); // set text in list testWidget->setCurrentIndex(0); QCOMPARE(testWidget->currentIndex(), 0); - spy.clear(); + textChangedSpy.clear(); testWidget->setCurrentText(QString("bar")); - QCOMPARE(spy.size(), 1); - QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("bar")); + QCOMPARE(textChangedSpy.size(), 1); + QCOMPARE(qvariant_cast<QString>(textChangedSpy.at(0).at(0)), QString("bar")); // set text not in list testWidget->setCurrentIndex(0); QCOMPARE(testWidget->currentIndex(), 0); - spy.clear(); + textChangedSpy.clear(); testWidget->setCurrentText(QString("qt")); if (editable) { - QCOMPARE(spy.size(), 1); - QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("qt")); + QCOMPARE(textChangedSpy.size(), 1); + QCOMPARE(qvariant_cast<QString>(textChangedSpy.at(0).at(0)), QString("qt")); } else { - QCOMPARE(spy.size(), 0); + QCOMPARE(textChangedSpy.size(), 0); } // item changed testWidget->setCurrentIndex(0); QCOMPARE(testWidget->currentIndex(), 0); - spy.clear(); + textChangedSpy.clear(); testWidget->setItemText(0, QString("ape")); - QCOMPARE(spy.size(), 1); - QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("ape")); + QCOMPARE(textChangedSpy.size(), 1); + QCOMPARE(qvariant_cast<QString>(textChangedSpy.at(0).at(0)), QString("ape")); + // change it back - spy.clear(); + textChangedSpy.clear(); testWidget->setItemText(0, QString("foo")); - QCOMPARE(spy.size(), 1); - QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("foo")); + QCOMPARE(textChangedSpy.size(), 1); + QCOMPARE(qvariant_cast<QString>(textChangedSpy.at(0).at(0)), QString("foo")); + + // currentIndexChanged vs. currentTextChanged + testWidget->clear(); + testWidget->addItems(QStringList() << "first" << "second" << "third" << "fourth" << "fourth"); + testWidget->setCurrentIndex(4); + textChangedSpy.clear(); + QSignalSpy indexChangedSpy(testWidget, &QComboBox::currentIndexChanged); + + // Index change w/o text change + testWidget->removeItem(3); + QCOMPARE(textChangedSpy.count(), 0); + QCOMPARE(indexChangedSpy.count(), 1); + + // Index and text change + testWidget->setCurrentIndex(0); + QCOMPARE(textChangedSpy.count(), 1); + QCOMPARE(indexChangedSpy.count(), 2); + + // remove item above current index + testWidget->removeItem(2); + QCOMPARE(textChangedSpy.count(), 1); + QCOMPARE(indexChangedSpy.count(), 2); + + // Text change w/o index change + testWidget->setItemText(0, "first class"); + QCOMPARE(textChangedSpy.count(), 2); + QCOMPARE(indexChangedSpy.count(), 2); } void tst_QComboBox::editTextChanged() @@ -1989,7 +2021,7 @@ void tst_QComboBox::flaggedItems_data() disableFlagList << 1; keyMovementList.clear(); keyMovementList << Qt::Key_T << Qt::Key_Enter; - QTest::newRow(testCase.toLatin1() + "disabled") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 2; + QTest::newRow(testCase.toLatin1() + "disabled with key") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 2; QTest::newRow(testCase.toLatin1() + "broken autocompletion") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 2; } } @@ -2001,8 +2033,8 @@ void tst_QComboBox::flaggedItems() QSKIP("Wayland: This fails. Figure out why."); QFETCH(QStringList, itemList); - QFETCH(IntList, deselectFlagList); - QFETCH(IntList, disableFlagList); + QFETCH(const IntList, deselectFlagList); + QFETCH(const IntList, disableFlagList); QFETCH(KeyList, keyMovementList); QFETCH(bool, editable); QFETCH(int, expectedIndex); @@ -2013,17 +2045,16 @@ void tst_QComboBox::flaggedItems() listWidget.addItems(itemList); comboBox.setEditable(editable); - foreach (int index, deselectFlagList) + for (int index : deselectFlagList) listWidget.item(index)->setFlags(listWidget.item(index)->flags() & ~Qt::ItemIsSelectable); - foreach (int index, disableFlagList) + for (int index : disableFlagList) listWidget.item(index)->setFlags(listWidget.item(index)->flags() & ~Qt::ItemIsEnabled); comboBox.setModel(listWidget.model()); comboBox.setView(&listWidget); comboBox.move(200, 200); comboBox.show(); - QApplicationPrivate::setActiveWindow(&comboBox); comboBox.activateWindow(); comboBox.setFocus(); QVERIFY(QTest::qWaitForWindowActive(&comboBox)); @@ -2099,7 +2130,7 @@ void tst_QComboBox::mouseWheel_data() void tst_QComboBox::mouseWheel() { - QFETCH(IntList, disabledItems); + QFETCH(const IntList, disabledItems); QFETCH(int, startIndex); QFETCH(int, wheelDirection); QFETCH(int, expectedIndex); @@ -2114,7 +2145,7 @@ void tst_QComboBox::mouseWheel() QListWidget listWidget; listWidget.addItems(list); - foreach (int index, disabledItems) + for (int index : disabledItems) listWidget.item(index)->setFlags(listWidget.item(index)->flags() & ~Qt::ItemIsEnabled); box.setModel(listWidget.model()); @@ -2248,11 +2279,11 @@ void tst_QComboBox::separatorItem_data() void tst_QComboBox::separatorItem() { QFETCH(QStringList, items); - QFETCH(IntList, separators); + QFETCH(const IntList, separators); QComboBox box; box.addItems(items); - foreach(int index, separators) + for (int index : separators) box.insertSeparator(index); QCOMPARE(box.count(), (items.size() + separators.size())); for (int i = 0, s = 0; i < box.count(); ++i) { @@ -2373,7 +2404,8 @@ void tst_QComboBox::task191329_size() QFrame *container = tableCombo.findChild<QComboBoxPrivateContainer *>(); QVERIFY(container); QCOMPARE(static_cast<QAbstractItemView *>(table), container->findChild<QAbstractItemView *>()); - foreach (QWidget *button, container->findChildren<QComboBoxPrivateScroller *>()) { + const auto buttons = container->findChildren<QComboBoxPrivateScroller *>(); + for (QWidget *button : buttons) { //the popup should be large enough to contains everithing so the top and left button are hidden QVERIFY(!button->isVisible()); } @@ -2452,7 +2484,6 @@ void tst_QComboBox::task247863_keyBoardSelection() combo.addItem( QLatin1String("111")); combo.addItem( QLatin1String("222")); combo.show(); - QApplicationPrivate::setActiveWindow(&combo); QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&combo)); QSignalSpy spy(&combo, &QComboBox::activated); @@ -2478,7 +2509,6 @@ void tst_QComboBox::task220195_keyBoardSelection2() combo.addItem( QLatin1String("foo2")); combo.addItem( QLatin1String("foo3")); combo.show(); - QApplicationPrivate::setActiveWindow(&combo); QVERIFY(QTest::qWaitForWindowActive(&combo)); combo.setCurrentIndex(-1); @@ -2764,7 +2794,6 @@ void tst_QComboBox::keyBoardNavigationWithMouse() combo.move(200, 200); combo.showNormal(); - QApplicationPrivate::setActiveWindow(&combo); QVERIFY(QTest::qWaitForWindowActive(&combo)); QCOMPARE(combo.currentText(), QLatin1String("0")); @@ -2820,7 +2849,6 @@ void tst_QComboBox::task_QTBUG_1071_changingFocusEmitsActivated() layout.addWidget(&edit); w.show(); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); cb.clearEditText(); cb.setFocus(); @@ -3172,31 +3200,55 @@ void tst_QComboBox::task_QTBUG_54191_slotOnEditTextChangedSetsComboBoxToReadOnly QCOMPARE(cb.currentIndex(), 1); } +class ComboBox : public QComboBox { +public: + using QComboBox::QComboBox; + + void keyPressEvent(QKeyEvent *e) override + { + QComboBox::keyPressEvent(e); + accepted = e->isAccepted(); + } + bool accepted = false; +}; + void tst_QComboBox::keyboardSelection() { - QComboBox comboBox; + ComboBox comboBox; const int keyboardInterval = QApplication::keyboardInputInterval(); - QStringList list; - list << "OA" << "OB" << "OC" << "OO" << "OP" << "PP"; + const QStringList list = {"OA", "OB", "OC", "OO", "OP", "PP"}; comboBox.addItems(list); // Clear any remaining keyboard input from previous tests. QTest::qWait(keyboardInterval); QTest::keyClicks(&comboBox, "oo", Qt::NoModifier, 50); QCOMPARE(comboBox.currentText(), list.at(3)); + QCOMPARE(comboBox.accepted, true); QTest::qWait(keyboardInterval); QTest::keyClicks(&comboBox, "op", Qt::NoModifier, 50); QCOMPARE(comboBox.currentText(), list.at(4)); + QCOMPARE(comboBox.accepted, true); QTest::keyClick(&comboBox, Qt::Key_P, Qt::NoModifier, keyboardInterval); QCOMPARE(comboBox.currentText(), list.at(5)); + QCOMPARE(comboBox.accepted, true); QTest::keyClick(&comboBox, Qt::Key_O, Qt::NoModifier, keyboardInterval); QCOMPARE(comboBox.currentText(), list.at(0)); + QCOMPARE(comboBox.accepted, true); QTest::keyClick(&comboBox, Qt::Key_O, Qt::NoModifier, keyboardInterval); QCOMPARE(comboBox.currentText(), list.at(1)); + QCOMPARE(comboBox.accepted, true); + + QTest::keyClick(&comboBox, Qt::Key_Tab, Qt::NoModifier, keyboardInterval); + QCOMPARE(comboBox.currentText(), list.at(1)); + QCOMPARE(comboBox.accepted, false); + + QTest::keyClick(&comboBox, Qt::Key_Tab, Qt::ControlModifier, keyboardInterval); + QCOMPARE(comboBox.currentText(), list.at(1)); + QCOMPARE(comboBox.accepted, false); } void tst_QComboBox::updateDelegateOnEditableChange() @@ -3273,10 +3325,10 @@ void tst_QComboBox::task_QTBUG_49831_scrollerNotActivated() QVERIFY(container); QVERIFY(QTest::qWaitForWindowExposed(container)); - QList<QComboBoxPrivateScroller *> scrollers = container->findChildren<QComboBoxPrivateScroller *>(); + const QList<QComboBoxPrivateScroller *> scrollers = container->findChildren<QComboBoxPrivateScroller *>(); // Not all styles support scrollers. We rely only on those platforms that do to catch any regression. if (!scrollers.isEmpty()) { - Q_FOREACH (QComboBoxPrivateScroller *scroller, scrollers) { + for (QComboBoxPrivateScroller *scroller : scrollers) { if (scroller->isVisible()) { QSignalSpy doScrollSpy(scroller, SIGNAL(doScroll(int))); QTest::mouseMove(scroller, QPoint(5, 5), 500); @@ -3358,6 +3410,65 @@ void tst_QComboBox::task_QTBUG_56693_itemFontFromModel() box.hidePopup(); } +#ifndef QT_NO_STYLE_FUSION +void tst_QComboBox::popupPositionAfterStyleChange() +{ +#ifdef Q_OS_QNX + QSKIP("Fails on QNX, QTBUG-123798"); +#endif + // Check that the popup opens up centered on top of the current + // index if the style has changed since the last time it was + // opened (QTBUG-113765). + QComboBox box; + QStyleOptionComboBox opt; + const bool usePopup = qApp->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, &box); + if (!usePopup) + QSKIP("This test is only relevant for styles that centers the popup on top of the combo!"); + if (QTestPrivate::isRunningArmOnX86()) + QSKIP("Flaky on QEMU, QTBUG-114760"); + + box.addItems({"first", "middle", "last"}); + box.show(); + QVERIFY(QTest::qWaitForWindowExposed(&box)); + box.showPopup(); + + QFrame *container = box.findChild<QComboBoxPrivateContainer *>(); + QVERIFY(container); + QVERIFY(QTest::qWaitForWindowExposed(container)); + + // Select the last menu item, which will close the popup. This item is then expected + // to be centered on top of the combobox the next time the popup opens. + const QRect lastItemRect = box.view()->visualRect(box.view()->model()->index(2, 0)); + QTest::mouseClick(box.view(), Qt::LeftButton, Qt::NoModifier, lastItemRect.center()); + + // Change style. This can make the popup smaller, which will result in up-and-down + // scroll widgets showing in the menu, directly underneath the mouse before the popup + // ends up hidden. This again will trigger the item view to scroll, which seems to be + // the root cause of QTBUG-113765. + qApp->setStyle(QStringLiteral("Fusion")); + + // Click on the combobox again to reopen it. But since both QComboBox + // (QComboBoxPrivateScroller) is using its own internal timer to do scrolling, we + // need to wait a bit until the scrolling is done before we can reopen it (since + // the scrolling is the sore spot that we want to test). + // But note, we expect, but don't require, the popup to scroll. And for that + // reason, we don't see it as a failure if the scrolling doesn't happen. + (void) QTest::qWaitFor([&box]{ return box.view()->verticalScrollBar()->value() > 0; }, 1000); + + // Verify that the popup is hidden before we click the button + QTRY_VERIFY(!container->isVisible()); + QTest::mouseClick(&box, Qt::LeftButton); + + // Click on item under mouse. But wait a bit, to avoid a double click + QTest::qWait(2 * QGuiApplication::styleHints()->mouseDoubleClickInterval()); + QTest::mouseClick(&box, Qt::LeftButton); + + // Ensure that the item that was centered on top of the combobox, and which + // we therefore clicked, was the same item we clicked on the first time. + QTRY_COMPARE(box.currentText(), QStringLiteral("last")); +} +#endif // QT_NO_STYLE_FUSION + void tst_QComboBox::inputMethodUpdate() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) @@ -3440,7 +3551,6 @@ void tst_QComboBox::task_QTBUG_52027_mapCompleterIndex() QCOMPARE(spy.size(), 0); cbox.move(200, 200); cbox.show(); - QApplicationPrivate::setActiveWindow(&cbox); QVERIFY(QTest::qWaitForWindowActive(&cbox)); QTest::keyClicks(&cbox, "foobar2"); @@ -3466,7 +3576,6 @@ void tst_QComboBox::task_QTBUG_52027_mapCompleterIndex() cbox.activateWindow(); } - QApplicationPrivate::setActiveWindow(&cbox); QVERIFY(QTest::qWaitForWindowActive(&cbox)); QTest::keyClicks(&cbox, "foobar1"); @@ -3534,7 +3643,6 @@ void tst_QComboBox::checkEmbeddedLineEditWhenStyleSheetIsSet() layout->addWidget(comboBox); topLevel.show(); comboBox->setEditable(true); - QApplicationPrivate::setActiveWindow(&topLevel); QVERIFY(QTest::qWaitForWindowActive(&topLevel)); QImage grab = comboBox->grab().toImage(); @@ -3696,5 +3804,40 @@ void tst_QComboBox::cancelClosesPopupNotDialog() QVERIFY(!dialog.isVisible()); } +void tst_QComboBox::closePopupWithCheckableItems() +{ + QWidget widget; + + QVBoxLayout *vb = new QVBoxLayout(&widget); + + QLabel *dlgLabel = new QLabel("Click when combo expanded."); + vb->addWidget(dlgLabel); + + QComboBox *combo = new QComboBox(); + vb->addWidget(combo); + + QStandardItemModel model; + const int rowCount = 10; + for (int r = 0; r < rowCount; ++r) { + QString str = "Item: " + QString::number(r); + QStandardItem *item = new QStandardItem(str); + const bool isChecked = (r % 2); + + item->setData(isChecked ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); + item->setFlags(Qt::ItemIsUserCheckable | (item->flags() & ~(Qt::ItemIsSelectable)) ); + model.appendRow(item); + } + + combo->setModel(&model); + + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + + QTest::mouseClick(widget.windowHandle(), Qt::LeftButton, {}, combo->geometry().center()); + QVERIFY(QTest::qWaitForWindowExposed(combo->view())); + QTest::mouseClick(widget.windowHandle(), Qt::LeftButton, {}, dlgLabel->geometry().center()); + QTRY_VERIFY(!combo->view()->isVisible()); +} + QTEST_MAIN(tst_QComboBox) #include "tst_qcombobox.moc" diff --git a/tests/auto/widgets/widgets/qcommandlinkbutton/CMakeLists.txt b/tests/auto/widgets/widgets/qcommandlinkbutton/CMakeLists.txt index 8cf7859fef..b89c5aa1dd 100644 --- a/tests/auto/widgets/widgets/qcommandlinkbutton/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qcommandlinkbutton/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qcommandlinkbutton Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcommandlinkbutton LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qcommandlinkbutton SOURCES tst_qcommandlinkbutton.cpp diff --git a/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp b/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp index 6b02775786..e0c63e5ced 100644 --- a/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp +++ b/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qdatetimeedit/CMakeLists.txt b/tests/auto/widgets/widgets/qdatetimeedit/CMakeLists.txt index 018070c5a4..9c2e777380 100644 --- a/tests/auto/widgets/widgets/qdatetimeedit/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qdatetimeedit/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qdatetimeedit Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdatetimeedit LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qdatetimeedit SOURCES tst_qdatetimeedit.cpp diff --git a/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp b/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp index a5e9594ada..2d9689db41 100644 --- a/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp +++ b/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qapplication.h> #include <qgroupbox.h> @@ -233,10 +233,12 @@ private slots: void nextPrevSection(); void dateEditTimeEditFormats(); +#if QT_DEPRECATED_SINCE(6, 10) void timeSpec_data(); void timeSpec(); - void timeSpecBug(); - void timeSpecInit(); +#endif + void timeZoneBug(); + void timeZoneInit(); void setDateTime_data(); void setDateTime(); @@ -252,7 +254,7 @@ private slots: void task196924(); void focusNextPrevChild(); - void taskQTBUG_12384_timeSpecShowTimeOnly(); + void taskQTBUG_12384_timeZoneShowTimeOnly(); void deleteCalendarWidget(); @@ -406,7 +408,7 @@ void tst_QDateTimeEdit::initTestCase() qWarning("Running under locale %s/%s -- this test may generate failures due to language differences", qPrintable(QLocale::languageToString(system.language())), qPrintable(QLocale::territoryToString(system.territory()))); - testWidget = new EditorDateEdit(nullptr); + testWidget = new EditorDateEdit; testFocusWidget = new QWidget(nullptr); testFocusWidget->resize(200, 100); testFocusWidget->show(); @@ -436,7 +438,7 @@ void tst_QDateTimeEdit::cleanup() { testWidget->clearMinimumDateTime(); testWidget->clearMaximumDateTime(); - testWidget->setTimeSpec(Qt::LocalTime); + testWidget->setTimeZone(QTimeZone::LocalTime); testWidget->setSpecialValueText(QString()); testWidget->setWrapping(false); // Restore the default. @@ -825,7 +827,6 @@ void tst_QDateTimeEdit::selectAndScrollWithKeys() return; #endif - QApplicationPrivate::setActiveWindow(testWidget); testWidget->setDate(QDate(2004, 05, 11)); testWidget->setDisplayFormat("dd/MM/yyyy"); testWidget->show(); @@ -927,7 +928,6 @@ void tst_QDateTimeEdit::selectAndScrollWithKeys() void tst_QDateTimeEdit::backspaceKey() { - QApplicationPrivate::setActiveWindow(testWidget); testWidget->setDate(QDate(2004, 05, 11)); testWidget->setDisplayFormat("d/MM/yyyy"); testWidget->show(); @@ -993,7 +993,6 @@ void tst_QDateTimeEdit::backspaceKey() void tst_QDateTimeEdit::deleteKey() { - QApplicationPrivate::setActiveWindow(testWidget); testWidget->setDate(QDate(2004, 05, 11)); testWidget->setDisplayFormat("d/MM/yyyy"); #ifdef Q_OS_MAC @@ -1012,7 +1011,6 @@ void tst_QDateTimeEdit::deleteKey() void tst_QDateTimeEdit::tabKeyNavigation() { - QApplicationPrivate::setActiveWindow(testWidget); testWidget->setDate(QDate(2004, 05, 11)); testWidget->setDisplayFormat("dd/MM/yyyy"); testWidget->show(); @@ -1030,7 +1028,6 @@ void tst_QDateTimeEdit::tabKeyNavigation() void tst_QDateTimeEdit::tabKeyNavigationWithPrefix() { - QApplicationPrivate::setActiveWindow(testWidget); testWidget->setDate(QDate(2004, 05, 11)); testWidget->setDisplayFormat("prefix dd/MM/yyyy"); @@ -1048,7 +1045,6 @@ void tst_QDateTimeEdit::tabKeyNavigationWithPrefix() void tst_QDateTimeEdit::tabKeyNavigationWithSuffix() { - QApplicationPrivate::setActiveWindow(testWidget); testWidget->setDate(QDate(2004, 05, 11)); testWidget->setDisplayFormat("dd/MM/yyyy 'suffix'"); @@ -1064,7 +1060,6 @@ void tst_QDateTimeEdit::tabKeyNavigationWithSuffix() void tst_QDateTimeEdit::enterKey() { - QApplicationPrivate::setActiveWindow(testWidget); testWidget->setDate(QDate(2004, 5, 11)); testWidget->setDisplayFormat("prefix d/MM/yyyy 'suffix'"); testWidget->lineEdit()->setFocus(); @@ -1352,7 +1347,6 @@ void tst_QDateTimeEdit::editingRanged() }); edit->show(); - QApplicationPrivate::setActiveWindow(edit.get()); if (!QTest::qWaitForWindowActive(edit.get())) QSKIP("Failed to make window active, aborting"); edit->setFocus(); @@ -3365,7 +3359,7 @@ void tst_QDateTimeEdit::wheelEvent() QFETCH(QDate, startDate); QFETCH(DateList, expectedDates); - EditorDateEdit edit(0); + EditorDateEdit edit; edit.setDate(startDate); edit.setCurrentSection(section); @@ -3491,6 +3485,7 @@ void tst_QDateTimeEdit::dateEditTimeEditFormats() QCOMPARE(d.displayedSections(), QDateTimeEdit::YearSection); } +#if QT_DEPRECATED_SINCE(6, 10) void tst_QDateTimeEdit::timeSpec_data() { QTest::addColumn<bool>("useSetProperty"); @@ -3498,6 +3493,8 @@ void tst_QDateTimeEdit::timeSpec_data() QTest::newRow("setTimeSpec") << false; } +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED void tst_QDateTimeEdit::timeSpec() { QFETCH(bool, useSetProperty); @@ -3538,10 +3535,12 @@ void tst_QDateTimeEdit::timeSpec() QSKIP("Not tested in the GMT timezone"); } } +QT_WARNING_POP +#endif // test deprecated timeSpec property -void tst_QDateTimeEdit::timeSpecBug() +void tst_QDateTimeEdit::timeZoneBug() { - testWidget->setTimeSpec(Qt::UTC); + testWidget->setTimeZone(QTimeZone::UTC); testWidget->setDisplayFormat("hh:mm"); testWidget->setTime(QTime(2, 2)); const QString oldText = testWidget->text(); @@ -3551,7 +3550,7 @@ void tst_QDateTimeEdit::timeSpecBug() QCOMPARE(oldText, testWidget->text()); } -void tst_QDateTimeEdit::timeSpecInit() +void tst_QDateTimeEdit::timeZoneInit() { QDateTime utc(QDate(2000, 1, 1), QTime(12, 0), QTimeZone::UTC); QDateTimeEdit widget(utc); @@ -3560,28 +3559,24 @@ void tst_QDateTimeEdit::timeSpecInit() void tst_QDateTimeEdit::setDateTime_data() { - QDateTime localNoon(QDate(2019, 12, 24), QTime(12, 0)); - // TODO QTBUG-80417: port away from spec, to use QTimeZone instead. - QTest::addColumn<Qt::TimeSpec>("spec"); + const QDateTime localNoon(QDate(2019, 12, 24), QTime(12, 0)); + const QTimeZone UTC(QTimeZone::UTC), local(QTimeZone::LocalTime); + QTest::addColumn<QTimeZone>("zone"); QTest::addColumn<QDateTime>("store"); QTest::addColumn<QDateTime>("expect"); - QTest::newRow("LocalTime/LocalTime") - << Qt::LocalTime << localNoon << localNoon; - QTest::newRow("LocalTime/UTC") - << Qt::LocalTime << localNoon.toUTC() << localNoon; - QTest::newRow("UTC/LocalTime") - << Qt::UTC << localNoon << localNoon.toUTC(); - QTest::newRow("UTC/UTC") - << Qt::UTC << localNoon.toUTC() << localNoon.toUTC(); + QTest::newRow("LocalTime/LocalTime") << local << localNoon << localNoon; + QTest::newRow("LocalTime/UTC") << local << localNoon.toUTC() << localNoon; + QTest::newRow("UTC/LocalTime") << UTC << localNoon << localNoon.toUTC(); + QTest::newRow("UTC/UTC") << UTC << localNoon.toUTC() << localNoon.toUTC(); } void tst_QDateTimeEdit::setDateTime() { - QFETCH(const Qt::TimeSpec, spec); + QFETCH(const QTimeZone, zone); QFETCH(const QDateTime, store); QFETCH(const QDateTime, expect); QDateTimeEdit editor; - editor.setTimeSpec(spec); + editor.setTimeZone(zone); editor.setDateTime(store); QCOMPARE(editor.dateTime(), expect); } @@ -3775,14 +3770,14 @@ void tst_QDateTimeEdit::focusNextPrevChild() QCOMPARE(edit.currentSection(), QDateTimeEdit::MonthSection); } -void tst_QDateTimeEdit::taskQTBUG_12384_timeSpecShowTimeOnly() +void tst_QDateTimeEdit::taskQTBUG_12384_timeZoneShowTimeOnly() { QDateTime time = QDateTime::fromString("20100723 04:02:40", "yyyyMMdd hh:mm:ss"); time.setTimeZone(QTimeZone::UTC); EditorDateEdit edit; edit.setDisplayFormat("hh:mm:ss"); - edit.setTimeSpec(Qt::UTC); + edit.setTimeZone(QTimeZone::UTC); edit.setDateTime(time); QCOMPARE(edit.minimumTime(), QTime(0, 0, 0, 0)); @@ -3798,7 +3793,7 @@ void tst_QDateTimeEdit::deleteCalendarWidget() QVERIFY(!edit.calendarWidget()); edit.setCalendarPopup(true); QVERIFY(edit.calendarWidget()); - edit.calendarWidget()->setObjectName("cw1");; + edit.calendarWidget()->setObjectName("cw1"); // delete delete edit.calendarWidget(); @@ -4424,7 +4419,7 @@ void tst_QDateTimeEdit::stepModifierButtons() testWidget->hide(); - EditorDateEdit edit(0); + EditorDateEdit edit; edit.setTime(startTime); edit.show(); QVERIFY(QTest::qWaitForWindowActive(&edit)); @@ -4518,7 +4513,7 @@ void tst_QDateTimeEdit::stepModifierPressAndHold() testWidget->hide(); - EditorDateEdit edit(0); + EditorDateEdit edit; edit.setDate(startDate); QScopedPointer<StepModifierStyle, QScopedPointerDeleteLater> stepModifierStyle( @@ -4568,13 +4563,20 @@ static QDateTime findSpring(int year, const QTimeZone &timeZone) const QTimeZone::OffsetData transition = midSummer.isDaylightTime() ? timeZone.previousTransition(midSummer) : timeZone.nextTransition(midSummer); - const QDateTime spring = transition.atUtc.toLocalTime(); + const QDateTime spring = transition.atUtc.toTimeZone(timeZone); // there might have been DST at some point, but not in the year we care about if (spring.date().year() != year || !spring.isDaylightTime()) return QDateTime(); return spring; }; + +// Number of missing seconds between a day before and a day after when. +// If when is the time of a spring-forward transition, this is the width of its gap. +static int missingSecondsNear(const QDateTime &when) +{ + return 2 * 24 * 60 * 60 - when.addDays(-1).secsTo(when.addDays(1)); +} #endif /*! @@ -4598,36 +4600,40 @@ void tst_QDateTimeEdit::springForward_data() QSKIP("Failed to obtain valid spring forward datetime for 2019!"); const QDate springDate = springTransition.date(); - const int gapWidth = timeZone.daylightTimeOffset(springTransition.addDays(1)); + const int gapWidth = missingSecondsNear(springTransition); + if (gapWidth <= 0) + QSKIP("Spring forward transition did not actually skip any time!"); + const QTime springGap = springTransition.time().addSecs(-gapWidth); - const QByteArray springTime = springGap.toString("hh:mm").toLocal8Bit(); - const QTime springGapMiddle = springTransition.time().addSecs(-gapWidth/2); + const QTime springGapMiddle = springTransition.time().addSecs(-gapWidth / 2); + const QByteArray startGapTime = springGap.toString("hh:mm").toLocal8Bit(); + const QByteArray midGapTime = springGapMiddle.toString("hh:mm").toLocal8Bit(); - QTest::addRow("forward to %s, correct to previous", springTime.data()) + QTest::addRow("forward to %s, correct to previous", startGapTime.data()) << QDateTime(springDate, springGap.addSecs(-gapWidth)) << QAbstractSpinBox::CorrectToPreviousValue << springGap << QDateTime(springDate, springGap.addSecs(-gapWidth)); - QTest::addRow("back to %s, correct to previous", springTime.data()) + QTest::addRow("back to %s, correct to previous", startGapTime.data()) << springTransition << QAbstractSpinBox::CorrectToPreviousValue << springGap << springTransition; - QTest::addRow("forward to %s, correct to nearest", springTime.data()) + QTest::addRow("forward to %s, correct to nearest", midGapTime.data()) << QDateTime(springDate, springGap.addSecs(-gapWidth)) << QAbstractSpinBox::CorrectToNearestValue << springGapMiddle << springTransition; - QTest::addRow("back to %s, correct to nearest", springTime.data()) + QTest::addRow("back to %s, correct to nearest", midGapTime.data()) << springTransition << QAbstractSpinBox::CorrectToNearestValue << springGapMiddle << springTransition; - QTest::addRow("jump to %s, correct to nearest", qPrintable(springGapMiddle.toString("hh:mm"))) + QTest::addRow("jump to %s, correct to nearest", midGapTime.data()) << QDateTime(QDate(1980, 5, 10), springGap) << QAbstractSpinBox::CorrectToNearestValue << springGapMiddle @@ -4655,11 +4661,11 @@ void tst_QDateTimeEdit::springForward() edit.setSelectedSection(QDateTimeEdit::DaySection); const QDate date = expected.date(); - const QString day = QString::number(date.day()).rightJustified(2, QLatin1Char('0')); - const QString month = QString::number(date.month()).rightJustified(2, QLatin1Char('0')); + const QString day = QString::number(date.day()).rightJustified(2, u'0'); + const QString month = QString::number(date.month()).rightJustified(2, u'0'); const QString year = QString::number(date.year()); - const QString hour = QString::number(inputTime.hour()).rightJustified(2, QLatin1Char('0')); - const QString minute = QString::number(inputTime.minute()).rightJustified(2, QLatin1Char('0')); + const QString hour = QString::number(inputTime.hour()).rightJustified(2, u'0'); + const QString minute = QString::number(inputTime.minute()).rightJustified(2, u'0'); QTest::keyClicks(&edit, day); QTest::keyClicks(&edit, month); QTest::keyClicks(&edit, year); @@ -4686,7 +4692,7 @@ void tst_QDateTimeEdit::stepIntoDSTGap_data() QTest::addColumn<int>("steps"); QTest::addColumn<QDateTime>("end"); - const QTimeZone timeZone = QTimeZone("Europe/Oslo"); + const QTimeZone timeZone = QTimeZone::systemTimeZone(); if (!timeZone.hasDaylightTime()) QSKIP("This test needs to run in a timezone that observes DST!"); @@ -4695,11 +4701,14 @@ void tst_QDateTimeEdit::stepIntoDSTGap_data() QSKIP("Failed to obtain valid spring forward datetime for 2007!"); const QDate spring = springTransition.date(); - const int gapWidth = timeZone.daylightTimeOffset(springTransition.addDays(1)); + const int gapWidth = missingSecondsNear(springTransition); + if (gapWidth <= 0) + QSKIP("Spring forward transition did not actually skip any time!"); + const QTime springGap = springTransition.time().addSecs(-gapWidth); const QByteArray springTime = springGap.toString("hh:mm").toLocal8Bit(); - // change hour + // change hour (can't change day): if (springGap.hour() != 0) { QTest::addRow("hour up into %s gap", springTime.data()) << QDateTime(spring, springGap.addSecs(-3600)) @@ -4709,7 +4718,7 @@ void tst_QDateTimeEdit::stepIntoDSTGap_data() // 3:00:10 into 2:00:10 should get us to 1:00:10 QTest::addRow("hour down into %s gap", springTime.data()) - << QDateTime(spring, springGap.addSecs(3610)) + << QDateTime(spring, springGap.addSecs(gapWidth + 10)) << QDateTimeEdit::HourSection << -1 << QDateTime(spring, springGap.addSecs(-3590)); @@ -4734,28 +4743,31 @@ void tst_QDateTimeEdit::stepIntoDSTGap_data() } // change month - QTest::addRow("month up into %s gap", springTime.data()) - << QDateTime(spring.addMonths(-1), springGap) - << QDateTimeEdit::MonthSection - << +1 - << springTransition; - QTest::addRow("month down into %s gap", springTime.data()) - << QDateTime(spring.addMonths(1), springGap) - << QDateTimeEdit::MonthSection - << -1 - << springTransition; + // Previous month may well be February, so lack the day-of-month that + // matches spring (e.g. Asia/Jerusalem, March 30). + if (QDate prior = spring.addMonths(-1); prior.day() == spring.day()) { + QTest::addRow("month up into %s gap", springTime.data()) + << QDateTime(prior, springGap) << QDateTimeEdit::MonthSection << +1 << springTransition; + } + // America/{Jujuy,Cordoba,Catamarca} did a 2007 Dec 30th 00:00 spring + // forward; and QDTE month steps won't change the year. + if (QDate prior = spring.addMonths(1); + prior.year() == spring.year() && prior.day() == spring.day()) { + QTest::addRow("month down into %s gap", springTime.data()) + << QDateTime(prior, springGap) << QDateTimeEdit::MonthSection << -1 << springTransition; + } // change year - QTest::addRow("year up into %s gap", springTime.data()) - << QDateTime(spring.addYears(-1), springGap) - << QDateTimeEdit::YearSection - << +1 - << springTransition; - QTest::addRow("year down into %s gap", springTime.data()) - << QDateTime(spring.addYears(1), springGap) - << QDateTimeEdit::YearSection - << -1 - << springTransition; + // Some zones (e.g. Asia/Baghdad) do transitions on a fixed date; for these, + // the springGap moment is invalid every year, so skip this test. + if (QDateTime prior = QDateTime(spring.addYears(-1), springGap); prior.isValid()) { + QTest::addRow("year up into %s gap", springTime.data()) + << prior << QDateTimeEdit::YearSection << +1 << springTransition; + } + if (QDateTime later(spring.addYears(1), springGap); later.isValid()) { + QTest::addRow("year down into %s gap", springTime.data()) + << later << QDateTimeEdit::YearSection << -1 << springTransition; + } #else QSKIP("Needs timezone feature enabled"); #endif diff --git a/tests/auto/widgets/widgets/qdial/CMakeLists.txt b/tests/auto/widgets/widgets/qdial/CMakeLists.txt index dc8b011293..5e300eec94 100644 --- a/tests/auto/widgets/widgets/qdial/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qdial/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qdial Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdial LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qdial SOURCES tst_qdial.cpp diff --git a/tests/auto/widgets/widgets/qdial/tst_qdial.cpp b/tests/auto/widgets/widgets/qdial/tst_qdial.cpp index 1d8c970ef4..d0274ace2a 100644 --- a/tests/auto/widgets/widgets/qdial/tst_qdial.cpp +++ b/tests/auto/widgets/widgets/qdial/tst_qdial.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -17,6 +17,7 @@ private slots: void valueChanged(); void sliderMoved(); void wrappingCheck(); + void minEqualMaxValueOutsideRange(); void notchSize_data(); void notchSize(); @@ -172,6 +173,15 @@ void tst_QDial::wrappingCheck() } } +// QTBUG-104641 +void tst_QDial::minEqualMaxValueOutsideRange() +{ + QDial dial; + dial.setRange(30, 30); + dial.setWrapping(true); + dial.setValue(45); +} + /* Verify that the notchSizes calculated don't change compared to Qt 5.15 results for dial sizes at the edge values of the diff --git a/tests/auto/widgets/widgets/qdialogbuttonbox/CMakeLists.txt b/tests/auto/widgets/widgets/qdialogbuttonbox/CMakeLists.txt index a7eb85b140..098b129cbc 100644 --- a/tests/auto/widgets/widgets/qdialogbuttonbox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qdialogbuttonbox/CMakeLists.txt @@ -5,10 +5,17 @@ ## tst_qdialogbuttonbox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdialogbuttonbox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qdialogbuttonbox SOURCES tst_qdialogbuttonbox.cpp LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp b/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp index df26b4c5da..3e7dc92da1 100644 --- a/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp +++ b/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp @@ -1,13 +1,17 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> #include <QtWidgets/QPushButton> #include <QtWidgets/QStyle> #include <QtWidgets/QLayout> #include <QtWidgets/QDialog> +#include <QtWidgets/QLineEdit> #include <QtGui/QAction> +#include <QtGui/QStyleHints> #include <qdialogbuttonbox.h> +#include <QtWidgets/private/qdialogbuttonbox_p.h> +#include <QtWidgets/private/qabstractbutton_p.h> #include <limits.h> Q_DECLARE_METATYPE(QDialogButtonBox::ButtonRole) @@ -49,6 +53,10 @@ private slots: void clear(); void removeButton_data(); void removeButton(); +#ifdef QT_BUILD_INTERNAL + void hideAndShowButton(); +#endif + void hideAndShowStandardButton(); void buttonRole_data(); void buttonRole(); void setStandardButtons_data(); @@ -74,6 +82,9 @@ private slots: void task191642_default(); void testDeletedStandardButton(); + void automaticDefaultButton(); + void initialFocus_data(); + void initialFocus(); private: qint64 timeStamp; @@ -372,6 +383,70 @@ void tst_QDialogButtonBox::removeButton() delete button; } +#ifdef QT_BUILD_INTERNAL +void tst_QDialogButtonBox::hideAndShowButton() +{ + if (QGuiApplication::styleHints()->tabFocusBehavior() == Qt::NoTabFocus) + QSKIP("Test skipped with Qt::NoTabFocus"); + + QDialogButtonBox buttonBox; + QDialogButtonBoxPrivate *d = static_cast<QDialogButtonBoxPrivate *>(QObjectPrivate::get(&buttonBox)); + auto *apply = buttonBox.addButton( "Apply", QDialogButtonBox::ApplyRole ); + auto *accept = buttonBox.addButton( "Accept", QDialogButtonBox::AcceptRole ); + buttonBox.addButton( "Reject", QDialogButtonBox::RejectRole ); + auto *widget = new QWidget(); + auto *layout = new QHBoxLayout(widget); + layout->addWidget(&buttonBox); + + // apply button shows first on macOS. accept button on all other OSes. + QAbstractButton *hideButton; +#ifdef Q_OS_MACOS + hideButton = apply; + Q_UNUSED(accept); +#else + hideButton = accept; + Q_UNUSED(apply); +#endif + + hideButton->hide(); + widget->show(); + QVERIFY(QTest::qWaitForWindowExposed(widget)); + QTRY_VERIFY(buttonBox.focusWidget()); // QTBUG-114377: Without fixing, focusWidget() == nullptr + QCOMPARE(d->visibleButtons().count(), 2); + QCOMPARE(d->hiddenButtons.count(), 1); + QVERIFY(d->hiddenButtons.contains(hideButton)); + QCOMPARE(buttonBox.buttons().count(), 3); + QSignalSpy spy(qApp, &QApplication::focusChanged); + QTest::sendKeyEvent(QTest::KeyAction::Click, &buttonBox, Qt::Key_Tab, QString(), Qt::KeyboardModifiers()); + QCOMPARE(spy.count(), 1); // QTBUG-114377: Without fixing, tabbing wouldn't work. + hideButton->show(); + QCOMPARE_GE(spy.count(), 1); + QTRY_COMPARE(QApplication::focusWidget(), hideButton); + QCOMPARE(d->visibleButtons().count(), 3); + QCOMPARE(d->hiddenButtons.count(), 0); + QCOMPARE(buttonBox.buttons().count(), 3); + spy.clear(); + QTest::sendKeyEvent(QTest::KeyAction::Click, &buttonBox, Qt::Key_Backtab, QString(), Qt::KeyboardModifiers()); + QCOMPARE(spy.count(), 1); +} +#endif + +void tst_QDialogButtonBox::hideAndShowStandardButton() +{ + QDialogButtonBox buttonBox; + buttonBox.setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + buttonBox.show(); + QVERIFY(QTest::qWaitForWindowExposed(&buttonBox)); + auto *button = buttonBox.button(QDialogButtonBox::Cancel); + QVERIFY(button); + button->hide(); + QVERIFY(QTest::qWaitFor([button](){ return !button->isVisible(); })); + QCOMPARE(button, buttonBox.button(QDialogButtonBox::Cancel)); + button->show(); + QVERIFY(QTest::qWaitForWindowExposed(button)); + QCOMPARE(button, buttonBox.button(QDialogButtonBox::Cancel)); +} + void tst_QDialogButtonBox::testDelete() { QDialogButtonBox buttonBox; @@ -838,5 +913,87 @@ void tst_QDialogButtonBox::testDeletedStandardButton() QVERIFY(!buttonC); } +void tst_QDialogButtonBox::automaticDefaultButton() +{ + // Having a QDialogButtonBox inside a QDialog triggers Qt to + // enable autoDefault for QPushButtons inside the button box. + // Check that the logic for resolving a default button based + // on the Accept role is not overridden by the first button + // in the dialog (the focus proxy) taking focus, and hence + // stealing the default button state. + + { + QDialog dialog; + QDialogButtonBox *bb = new QDialogButtonBox(&dialog); + // Force horizontal orientation, where we know the order between + // Reset and Accept roles are always the same for all layouts. + bb->setOrientation(Qt::Horizontal); + auto *okButton = bb->addButton(QDialogButtonBox::Ok); + auto *resetButton = bb->addButton(QDialogButtonBox::Reset); + // Double check our assumption about Reset being first + QCOMPARE(bb->layout()->itemAt(0)->widget(), resetButton); + + dialog.show(); + QVERIFY(QTest::qWaitForWindowActive(&dialog)); + + QVERIFY(okButton->isDefault()); + QSignalSpy buttonClicked(okButton, &QPushButton::clicked); + QTest::keyPress(&dialog, Qt::Key_Enter); + QCOMPARE(buttonClicked.count(), 1); + } + + // However, if an explicit button has been focused, we respect that. + + { + QDialog dialog; + QDialogButtonBox *bb = new QDialogButtonBox(&dialog); + bb->setOrientation(Qt::Horizontal); + bb->addButton(QDialogButtonBox::Ok); + auto *resetButton = bb->addButton(QDialogButtonBox::Reset); + resetButton->setFocus(); + dialog.show(); + QVERIFY(QTest::qWaitForWindowActive(&dialog)); + + QVERIFY(resetButton->isDefault()); + QSignalSpy buttonClicked(resetButton, &QPushButton::clicked); + QTest::keyPress(&dialog, Qt::Key_Enter); + QCOMPARE(buttonClicked.count(), 1); + } +} + +void tst_QDialogButtonBox::initialFocus_data() +{ + QTest::addColumn<Qt::FocusPolicy>("focusPolicy"); + QTest::addColumn<bool>("lineEditHasFocus"); + + QTest::addRow("TabFocus") << Qt::FocusPolicy::TabFocus << false; + QTest::addRow("StrongFocus") << Qt::FocusPolicy::StrongFocus << true; + QTest::addRow("NoFocus") << Qt::FocusPolicy::NoFocus << false; + QTest::addRow("ClickFocus") << Qt::FocusPolicy::ClickFocus << false; + QTest::addRow("WheelFocus") << Qt::FocusPolicy::WheelFocus << false; +} + +void tst_QDialogButtonBox::initialFocus() +{ + QFETCH(const Qt::FocusPolicy, focusPolicy); + QFETCH(const bool, lineEditHasFocus); + QDialog dialog; + QVBoxLayout *layout = new QVBoxLayout(&dialog); + QLineEdit *lineEdit = new QLineEdit(&dialog); + lineEdit->setFocusPolicy(focusPolicy); + layout->addWidget(lineEdit); + QDialogButtonBox *dialogButtonBox = new QDialogButtonBox(&dialog); + layout->addWidget(dialogButtonBox); + dialogButtonBox->addButton(QDialogButtonBox::Reset); + const auto *firstAcceptButton = dialogButtonBox->addButton(QDialogButtonBox::Ok); + dialogButtonBox->addButton(QDialogButtonBox::Cancel); + dialog.show(); + dialog.activateWindow(); + if (lineEditHasFocus) + QTRY_VERIFY(lineEdit->hasFocus()); + else + QTRY_VERIFY(firstAcceptButton->hasFocus()); +} + QTEST_MAIN(tst_QDialogButtonBox) #include "tst_qdialogbuttonbox.moc" diff --git a/tests/auto/widgets/widgets/qdockwidget/BLACKLIST b/tests/auto/widgets/widgets/qdockwidget/BLACKLIST index 8b7a126b4d..8873589ff4 100644 --- a/tests/auto/widgets/widgets/qdockwidget/BLACKLIST +++ b/tests/auto/widgets/widgets/qdockwidget/BLACKLIST @@ -5,23 +5,32 @@ android # QDockWidget::isFloating() is flaky after state change on these OS [closeAndDelete] macos +b2qt +arm +android + +# QTBUG-103091 [floatingTabs] +arm +android +qnx +macos +b2qt + # QTBUG-103091 +[hoverWithoutDrop] +arm +android qnx macos -[closeAndDelete] b2qt -[floatingTabs] -macos b2qt arm android -[closeAndDelete] + +# OSes are flaky because of unplugging and plugging requires +# precise calculation of the title bar area for mouse emulation +# That's not possible for floating dock widgets. +[deleteFloatingTabWithSingleDockWidget] +qnx b2qt -[floatingTabs] arm -[closeAndDelete] -macos b2qt arm android -[floatingTabs] -arm -[closeAndDelete] -android -[floatingTabs] android +macos diff --git a/tests/auto/widgets/widgets/qdockwidget/CMakeLists.txt b/tests/auto/widgets/widgets/qdockwidget/CMakeLists.txt index 2f8a7a266d..21d8fb60cc 100644 --- a/tests/auto/widgets/widgets/qdockwidget/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qdockwidget/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qdockwidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdockwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qdockwidget SOURCES tst_qdockwidget.cpp diff --git a/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp b/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp index 35a05f768a..88a7057d2e 100644 --- a/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp +++ b/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> @@ -10,6 +10,7 @@ #include "private/qmainwindowlayout_p.h" #include <QAbstractButton> #include <qlineedit.h> +#include <QtGui/qpa/qplatformwindow.h> #include <qtabbar.h> #include <QScreen> #include <QTimer> @@ -65,14 +66,20 @@ private slots: // Dock area permissions for DockWidgets and DockWidgetGroupWindows void dockPermissions(); - // test floating tabs and item_tree consistency + // test floating tabs, item_tree and window title consistency void floatingTabs(); + void hoverWithoutDrop(); + + // floating tab gets removed, when last child goes away + void deleteFloatingTabWithSingleDockWidget_data(); + void deleteFloatingTabWithSingleDockWidget(); // test hide & show void hideAndShow(); // test closing and deleting consistency void closeAndDelete(); + void closeUnclosable(); // test save and restore consistency void saveAndRestore(); @@ -80,9 +87,29 @@ private slots: private: // helpers and consts for dockPermissions, hideAndShow, closeAndDelete #ifdef QT_BUILD_INTERNAL - void createTestWidgets(QMainWindow* &MainWindow, QPointer<QWidget> ¢, QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2) const; + void createTestWidgets(QMainWindow* &MainWindow, QPointer<QWidget> ¢, + QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2) const; + void unplugAndResize(QMainWindow* MainWindow, QDockWidget* dw, QPoint home, QSize size) const; + void createFloatingTabs(QMainWindow* &MainWindow, QPointer<QWidget> ¢, + QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2, + QList<int> &path1, QList<int> &path2) const; + +#if defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID) || defined(Q_OS_QNX) +#define qCreateFloatingTabs(mainWindow, centralWidget, d1, d2, path1, path2)\ + mainWindow = nullptr;\ + Q_UNUSED(path1);\ + Q_UNUSED(path2);\ + QSKIP("Platform not supported"); +#else +#define qCreateFloatingTabs(mainWindow, centralWidget, d1, d2, path1, path2)\ + createFloatingTabs(mainWindow, centralWidget, d1, d2, path1, path2);\ + std::unique_ptr<QMainWindow> up_mainWindow(mainWindow);\ + if (!platformSupportingRaise)\ + QSKIP("Platform not supporting raise(). Floating tab based tests will fail.") +#endif + static inline QPoint dragPoint(QDockWidget* dockWidget); static inline QPoint home1(QMainWindow* MainWindow) { return MainWindow->mapToGlobal(MainWindow->rect().topLeft() + QPoint(0.1 * MainWindow->width(), 0.1 * MainWindow->height())); } @@ -102,12 +129,27 @@ private: bool checkFloatingTabs(QMainWindow* MainWindow, QPointer<QDockWidgetGroupWindow> &ftabs, const QList<QDockWidget*> &dwList = {}) const; // move a dock widget - void moveDockWidget(QDockWidget* dw, QPoint to, QPoint from = QPoint()) const; + enum class MoveDockWidgetRule { + Drop, + Abort + }; + + void moveDockWidget(QDockWidget* dw, QPoint to, QPoint from, MoveDockWidgetRule rule) const; +#ifdef QT_BUILD_INTERNAL // Message handling for xcb error QTBUG 82059 static void xcbMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); + + enum class ChildRemovalReason { + Destroyed, + Closed, + Reparented + }; + public: bool xcbError = false; + bool platformSupportingRaise = true; +#endif private: #ifdef QT_DEBUG @@ -128,11 +170,6 @@ private: }; -// Statics for xcb error / msg handler -static tst_QDockWidget *qThis = nullptr; -static void (*oldMessageHandler)(QtMsgType, const QMessageLogContext&, const QString&); -#define QXCBVERIFY(cond) do { if (xcbError) QSKIP("Test skipped due to XCB error"); QVERIFY(cond); } while (0) - // Testing get/set functions void tst_QDockWidget::getSetCheck() { @@ -424,6 +461,23 @@ void tst_QDockWidget::setFloating() dw.setFloating(dw.isFloating()); QCOMPARE(spy.size(), 0); spy.clear(); + +#if defined(QT_BUILD_INTERNAL) && !defined(Q_OS_WIN) + // Check that setFloating() reparents the dock widget to the main window, + // in case it has a QDockWidgetGroupWindow parent + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + QList<int> path1; + QList<int> path2; + qCreateFloatingTabs(mainWindow, cent, d1, d2, path1, path2); + QVERIFY(qobject_cast<QDockWidgetGroupWindow *>(d1->parentWidget())); + QVERIFY(qobject_cast<QDockWidgetGroupWindow *>(d2->parentWidget())); + d1->setFloating(true); + QTRY_COMPARE(mainWindow, d1->parentWidget()); + QTRY_COMPARE(mainWindow, d2->parentWidget()); +#endif // defined(QT_BUILD_INTERNAL) && !defined(Q_OS_WIN) } void tst_QDockWidget::allowedAreas() @@ -675,6 +729,9 @@ void tst_QDockWidget::updateTabBarOnVisibilityChanged() mw.tabifyDockWidget(dw1, dw2); mw.tabifyDockWidget(dw2, dw3); + const auto list1 = QList<QDockWidget *>{dw1, dw2, dw3}; + QCOMPARE(mw.tabifiedDockWidgets(dw0), list1); + QTabBar *tabBar = mw.findChild<QTabBar *>(); QVERIFY(tabBar); tabBar->setCurrentIndex(2); @@ -688,6 +745,11 @@ void tst_QDockWidget::updateTabBarOnVisibilityChanged() dw1->hide(); QTRY_COMPARE(tabBar->count(), 2); QCOMPARE(tabBar->currentIndex(), 0); + + QCOMPARE(mw.tabifiedDockWidgets(dw2), {dw3}); + + mw.removeDockWidget(dw3); + QCOMPARE(mw.tabifiedDockWidgets(dw2).count(), 0); } Q_DECLARE_METATYPE(Qt::DockWidgetArea) @@ -1074,9 +1136,10 @@ void tst_QDockWidget::setWindowTitle() QMainWindow window; QDockWidget dock1(&window); QDockWidget dock2(&window); - const QString dock1Title = QStringLiteral("&Window"); - const QString dock2Title = QStringLiteral("&Modifiable Window [*]"); + constexpr QLatin1StringView dock1Title("&Window"); + constexpr QLatin1StringView dock2Title("&Modifiable Window [*]"); + // Set title on docked dock widgets, before main window is shown dock1.setWindowTitle(dock1Title); dock2.setWindowTitle(dock2Title); window.addDockWidget(Qt::RightDockWidgetArea, &dock1); @@ -1087,6 +1150,7 @@ void tst_QDockWidget::setWindowTitle() QCOMPARE(dock1.windowTitle(), dock1Title); QCOMPARE(dock2.windowTitle(), dock2Title); + // Check if title remains unchanged when docking / undocking dock1.setFloating(true); dock1.show(); QVERIFY(QTest::qWaitForWindowExposed(&dock1)); @@ -1096,12 +1160,16 @@ void tst_QDockWidget::setWindowTitle() dock1.setFloating(true); dock1.show(); QVERIFY(QTest::qWaitForWindowExposed(&dock1)); - const QString changed = QStringLiteral("Changed "); + + // Change a floating dock widget's title and check remains unchanged when docking + constexpr QLatin1StringView changed("Changed "); dock1.setWindowTitle(QString(changed + dock1Title)); QCOMPARE(dock1.windowTitle(), QString(changed + dock1Title)); dock1.setFloating(false); + QVERIFY(QTest::qWaitFor([&dock1](){ return !dock1.windowHandle(); })); QCOMPARE(dock1.windowTitle(), QString(changed + dock1Title)); + // Test consistency after toggling modified and floating dock2.setWindowModified(true); QCOMPARE(dock2.windowTitle(), dock2Title); dock2.setFloating(true); @@ -1116,6 +1184,12 @@ void tst_QDockWidget::setWindowTitle() dock2.show(); QVERIFY(QTest::qWaitForWindowExposed(&dock2)); QCOMPARE(dock2.windowTitle(), dock2Title); + + // Test title change of a closed dock widget + static constexpr QLatin1StringView closedDock2("Closed D2"); + dock2.close(); + dock2.setWindowTitle(closedDock2); + QCOMPARE(dock2.windowTitle(), closedDock2); } // helpers for dockPermissions, hideAndShow, closeAndDelete @@ -1175,7 +1249,7 @@ QPoint tst_QDockWidget::dragPoint(QDockWidget* dockWidget) return dockWidget->mapToGlobal(dwlayout->titleArea().center()); } -void tst_QDockWidget::moveDockWidget(QDockWidget* dw, QPoint to, QPoint from) const +void tst_QDockWidget::moveDockWidget(QDockWidget* dw, QPoint to, QPoint from, MoveDockWidgetRule rule) const { Q_ASSERT(dw); @@ -1192,12 +1266,22 @@ void tst_QDockWidget::moveDockWidget(QDockWidget* dw, QPoint to, QPoint from) co QTest::mouseMove(dw, target); qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "to" << target; qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "to" << to; - QTest::mouseRelease(dw, Qt::LeftButton, Qt::KeyboardModifiers(), target); - QTest::qWait(waitingTime); + if (rule == MoveDockWidgetRule::Drop) { + QTest::mouseRelease(dw, Qt::LeftButton, Qt::KeyboardModifiers(), target); + QTest::qWait(waitingTime); - // Verify WindowActive only for floating dock widgets - if (dw->isFloating()) - QTRY_VERIFY(QTest::qWaitForWindowActive(dw)); + // Verify WindowActive only for floating dock widgets + if (dw->isFloating()) + QTRY_VERIFY(QTest::qWaitForWindowActive(dw)); + return; + } + qCDebug(lcTestDockWidget) << "Aborting move and dropping at origin"; + + // Give animations some time + QTest::qWait(waitingTime); + QTest::mouseMove(dw, from); + QTest::mouseRelease(dw, Qt::LeftButton, Qt::KeyboardModifiers(), from); + QTest::qWait(waitingTime); } void tst_QDockWidget::unplugAndResize(QMainWindow* mainWindow, QDockWidget* dw, QPoint home, QSize size) const @@ -1245,7 +1329,7 @@ void tst_QDockWidget::unplugAndResize(QMainWindow* mainWindow, QDockWidget* dw, QPoint pos1 = dw->mapToGlobal(dw->rect().center()); pos1.rx() += mx; pos1.ry() += my; - moveDockWidget(dw, pos1, dw->mapToGlobal(dw->rect().center())); + moveDockWidget(dw, pos1, dw->mapToGlobal(dw->rect().center()), MoveDockWidgetRule::Drop); QTRY_VERIFY(dw->isFloating()); // Unplugged object's size may differ max. by 2x frame size @@ -1307,20 +1391,71 @@ bool tst_QDockWidget::checkFloatingTabs(QMainWindow* mainWindow, QPointer<QDockW return true; } -// detect xcb error +#ifdef QT_BUILD_INTERNAL +// Statics for xcb error, raise() suppert / msg handler +static tst_QDockWidget *qThis = nullptr; +static void (*oldMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &); +#define QXCBVERIFY(cond) do { if (xcbError) QSKIP("Test skipped due to XCB error"); QVERIFY(cond); } while (0) + +// detect xcb error and missing raise() support // qt.qpa.xcb: internal error: void QXcbWindow::setNetWmStateOnUnmappedWindow() called on mapped window void tst_QDockWidget::xcbMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { Q_ASSERT(oldMessageHandler); - if (type == QtWarningMsg && QString(context.category) == "qt.qpa.xcb" && msg.contains("internal error")) { + if (type == QtWarningMsg) { Q_ASSERT(qThis); - qThis->xcbError = true; + if (QString(context.category) == "qt.qpa.xcb" && msg.contains("internal error")) + qThis->xcbError = true; + if (msg.contains("does not support raise")) + qThis->platformSupportingRaise = false; } return oldMessageHandler(type, context, msg); } +#endif +void tst_QDockWidget::createFloatingTabs(QMainWindow* &mainWindow, QPointer<QWidget> ¢, + QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2, + QList<int> &path1, QList<int> &path2) const +{ + createTestWidgets(mainWindow, cent, d1, d2); + +#ifdef QT_BUILD_INTERNAL + qThis = const_cast<tst_QDockWidget *>(this); + oldMessageHandler = qInstallMessageHandler(xcbMessageHandler); + auto resetMessageHandler = qScopeGuard([] { qInstallMessageHandler(oldMessageHandler); }); +#endif + + // Test will fail if platform doesn't support raise. + mainWindow->windowHandle()->handle()->raise(); + if (!platformSupportingRaise) + return; + + // remember paths to d1 and d2 + QMainWindowLayout* layout = qobject_cast<QMainWindowLayout *>(mainWindow->layout()); + path1 = layout->layoutState.indexOf(d1); + path2 = layout->layoutState.indexOf(d2); + + // unplug and resize both dock widgets + unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow)); + unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow)); + + // docks must be parented to the main window, no group window must exist + QCOMPARE(d1->parentWidget(), mainWindow); + QCOMPARE(d2->parentWidget(), mainWindow); + QVERIFY(mainWindow->findChildren<QDockWidgetGroupWindow *>().isEmpty()); + + // Test plugging + qCDebug(lcTestDockWidget) << "*** move d1 dock over d2 dock ***"; + qCDebug(lcTestDockWidget) << "**********(test plugging)*************"; + qCDebug(lcTestDockWidget) << "Move d1 over d2"; + moveDockWidget(d1, d2->mapToGlobal(d2->rect().center()), QPoint(), MoveDockWidgetRule::Drop); + + // Now MainWindow has to have a floatingTab child + QPointer<QDockWidgetGroupWindow> ftabs; + QTRY_VERIFY(checkFloatingTabs(mainWindow, ftabs, QList<QDockWidget *>() << d1 << d2)); +} #endif // QT_BUILD_INTERNAL // test floating tabs and item_tree consistency @@ -1337,33 +1472,19 @@ void tst_QDockWidget::floatingTabs() QPointer<QDockWidget> d2; QPointer<QWidget> cent; QMainWindow* mainWindow; - createTestWidgets(mainWindow, cent, d1, d2); - std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + QList<int> path1; + QList<int> path2; + qCreateFloatingTabs(mainWindow, cent, d1, d2, path1, path2); + + QCOMPARE(mainWindow->tabifiedDockWidgets(d1), {d2}); + QCOMPARE(mainWindow->tabifiedDockWidgets(d2), {d1}); /* * unplug both dockwidgets, resize them and plug them into a joint floating tab * expected behavior: QDOckWidgetGroupWindow with both widgets is created */ - // remember paths to d1 and d2 - QMainWindowLayout* layout = qobject_cast<QMainWindowLayout*>(mainWindow->layout()); - const QList<int> path1 = layout->layoutState.indexOf(d1); - const QList<int> path2 = layout->layoutState.indexOf(d2); - - // unplug and resize both dock widgets - unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow)); - unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow)); - - // Test plugging - qCDebug(lcTestDockWidget) << "*** move d1 dock over d2 dock ***"; - qCDebug(lcTestDockWidget) << "**********(test plugging)*************"; - qCDebug(lcTestDockWidget) << "Move d1 over d2"; - moveDockWidget(d1, d2->mapToGlobal(d2->rect().center())); - - // Both dock widgets must no longer be floating // disabled due to flakiness on macOS and Windows - //QTRY_VERIFY(!d1->isFloating()); - //QTRY_VERIFY(!d2->isFloating()); if (d1->isFloating()) qWarning("OS flakiness: D1 is docked and reports being floating"); if (d2->isFloating()) @@ -1371,11 +1492,25 @@ void tst_QDockWidget::floatingTabs() // Now MainWindow has to have a floatingTab child QPointer<QDockWidgetGroupWindow> ftabs; - QTRY_VERIFY(checkFloatingTabs(mainWindow, ftabs, QList<QDockWidget*>() << d1 << d2)); + QTRY_VERIFY(checkFloatingTabs(mainWindow, ftabs, QList<QDockWidget *>() << d1 << d2)); + + // Hide both dock widgets. Verify that the group window is also hidden. + qCDebug(lcTestDockWidget) << "*** Hide and show tabbed dock widgets ***"; + d1->hide(); + d2->hide(); + QTRY_VERIFY(ftabs->isHidden()); + + // Show both dockwidgets again. Verify that the group window is visible. + d1->show(); + d2->show(); + QTRY_VERIFY(ftabs->isVisible()); /* * replug both dock widgets into their initial position - * expected behavior: both docks are plugged and no longer floating + * expected behavior: + - both docks are plugged + - both docks are no longer floating + - title changes have been propagated */ @@ -1392,17 +1527,20 @@ void tst_QDockWidget::floatingTabs() QTest::mouseClick(floatButton, Qt::LeftButton, Qt::KeyboardModifiers(), pos1); QTest::qWait(waitingTime); - // d1 must be floating again, while d2 is still in its GroupWindow + // d1 and d2 must be floating again QTRY_VERIFY(d1->isFloating()); - QTRY_VERIFY(!d2->isFloating()); + QTRY_VERIFY(d2->isFloating()); + + // d2 was the active tab, so d1 was not visible + QTRY_VERIFY(d1->isVisible()); + QTRY_VERIFY(d2->isVisible()); // Plug back into dock areas qCDebug(lcTestDockWidget) << "*** test plugging back to dock areas ***"; qCDebug(lcTestDockWidget) << "Move d1 to left dock"; - //moveDockWidget(d1, d1->mapFrom(MainWindow, dockPoint(MainWindow, Qt::LeftDockWidgetArea))); - moveDockWidget(d1, dockPoint(mainWindow, Qt::LeftDockWidgetArea)); + moveDockWidget(d1, dockPoint(mainWindow, Qt::LeftDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); qCDebug(lcTestDockWidget) << "Move d2 to right dock"; - moveDockWidget(d2, dockPoint(mainWindow, Qt::RightDockWidgetArea)); + moveDockWidget(d2, dockPoint(mainWindow, Qt::RightDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); qCDebug(lcTestDockWidget) << "Waiting" << waitBeforeClose << "ms before plugging back."; QTest::qWait(waitBeforeClose); @@ -1416,13 +1554,97 @@ void tst_QDockWidget::floatingTabs() QTRY_VERIFY(ftabs.isNull()); // Check if paths are consistent + QMainWindowLayout* layout = qobject_cast<QMainWindowLayout *>(mainWindow->layout()); qCDebug(lcTestDockWidget) << "Checking path consistency" << layout->layoutState.indexOf(d1) << layout->layoutState.indexOf(d2); - // Path1 must be identical - QTRY_VERIFY(path1 == layout->layoutState.indexOf(d1)); + // Paths must be identical + QTRY_COMPARE(layout->layoutState.indexOf(d1), path1); + QTRY_COMPARE(layout->layoutState.indexOf(d2), path2); + + QCOMPARE(mainWindow->tabifiedDockWidgets(d1), {}); + QCOMPARE(mainWindow->tabifiedDockWidgets(d2), {}); +#else + QSKIP("test requires -developer-build option"); +#endif // QT_BUILD_INTERNAL +} + +void tst_QDockWidget::deleteFloatingTabWithSingleDockWidget_data() +{ +#ifdef QT_BUILD_INTERNAL + QTest::addColumn<int>("reason"); + QTest::addRow("Delete child") << static_cast<int>(ChildRemovalReason::Destroyed); + QTest::addRow("Close child") << static_cast<int>(ChildRemovalReason::Closed); + QTest::addRow("Reparent child") << static_cast<int>(ChildRemovalReason::Reparented); +#endif +} + +void tst_QDockWidget::deleteFloatingTabWithSingleDockWidget() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); +#ifdef Q_OS_WIN + QSKIP("Test skipped on Windows platforms"); +#endif // Q_OS_WIN +#ifdef QT_BUILD_INTERNAL + + QFETCH(int, reason); + const ChildRemovalReason removalReason = static_cast<ChildRemovalReason>(reason); + + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + QList<int> path1; + QList<int> path2; + qCreateFloatingTabs(mainWindow, cent, d1, d2, path1, path2); + + switch (removalReason) { + case ChildRemovalReason::Destroyed: + delete d1; + break; + case ChildRemovalReason::Closed: + d1->close(); + break; + case ChildRemovalReason::Reparented: + // This will create an invalid state, because setParent() doesn't fix the item_list. + // Testing this case anyway, because setParent() includig item_list fixup is executed, + // when the 2nd last dock widget is dragged out of a floating tab. + // => despite of the broken state, the group window has to be gone. + d1->setParent(mainWindow); + break; + } + + QTRY_VERIFY(!qobject_cast<QDockWidgetGroupWindow *>(d2->parentWidget())); + QTRY_VERIFY(mainWindow->findChildren<QDockWidgetGroupWindow *>().isEmpty()); +#endif // QT_BUILD_INTERNAL +} + +void tst_QDockWidget::hoverWithoutDrop() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); +#ifdef QT_BUILD_INTERNAL + + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + createTestWidgets(mainWindow, cent, d1, d2); + std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); - // d1 must have a gap item due to size change - QTRY_VERIFY(layout->layoutState.indexOf(d2) == QList<int>() << path2 << 0); + // unplug and resize both dock widgets + unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow)); + unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow)); + + // Test plugging + qCDebug(lcTestDockWidget) << "*** move d1 dock over d2 dock ***"; + qCDebug(lcTestDockWidget) << "*******(test hovering)***********"; + qCDebug(lcTestDockWidget) << "Move d1 over d2, wait and return to origin"; + const QPoint source = d1->mapToGlobal(d1->rect().center()); + const QPoint target = d2->mapToGlobal(d2->rect().center()); + moveDockWidget(d1, target, source, MoveDockWidgetRule::Abort); + auto *groupWindow = mainWindow->findChild<QDockWidgetGroupWindow *>(); + QCOMPARE(groupWindow, nullptr); #else QSKIP("test requires -developer-build option"); #endif // QT_BUILD_INTERNAL @@ -1431,6 +1653,8 @@ void tst_QDockWidget::floatingTabs() // test hide & show void tst_QDockWidget::hideAndShow() { + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); #ifdef QT_BUILD_INTERNAL // Skip test if xcb error is launched qThis = this; @@ -1473,7 +1697,7 @@ void tst_QDockWidget::hideAndShow() unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow)); unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow)); - // Check hiding of undocked widgets + // Check hiding of undocked widgets qCDebug(lcTestDockWidget) << "Hiding mainWindow with unplugged dock widgets" << mainWindow; mainWindow->hide(); QTRY_VERIFY(!mainWindow->isVisible()); @@ -1484,6 +1708,16 @@ void tst_QDockWidget::hideAndShow() QTRY_VERIFY(!d1->isVisible()); QTRY_VERIFY(!d2->isVisible()); + + // Check floating, hidden dock widgets remain hidden, when their state is restored + qCDebug(lcTestDockWidget) << "Restoring state of unplugged, hidden dock widgets" << mainWindow; + const QByteArray state = mainWindow->saveState(); + mainWindow->restoreState(state); + mainWindow->show(); + QVERIFY(QTest::qWaitForWindowExposed(mainWindow)); + QTRY_VERIFY(!d1->isVisible()); + QTRY_VERIFY(!d2->isVisible()); + qCDebug(lcTestDockWidget) << "Waiting" << waitBeforeClose << "ms before closing."; QTest::qWait(waitBeforeClose); #else @@ -1494,7 +1728,12 @@ void tst_QDockWidget::hideAndShow() // test closing and deleting consistency void tst_QDockWidget::closeAndDelete() { + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); #ifdef QT_BUILD_INTERNAL + if (QSysInfo::productType() == "rhel") + QSKIP("Memory leak on RHEL 9.2 QTBUG-124559", TestFailMode::Abort); + // Create a mainwindow with a central widget and two dock widgets QPointer<QDockWidget> d1; QPointer<QDockWidget> d2; @@ -1509,7 +1748,7 @@ void tst_QDockWidget::closeAndDelete() // Create a floating tab and unplug it again qCDebug(lcTestDockWidget) << "Move d1 over d2"; - moveDockWidget(d1, d2->mapToGlobal(d2->rect().center())); + moveDockWidget(d1, d2->mapToGlobal(d2->rect().center()), QPoint(), MoveDockWidgetRule::Drop); // Both dock widgets must no longer be floating // disabled due to flakiness on macOS and Windows @@ -1521,8 +1760,10 @@ void tst_QDockWidget::closeAndDelete() qWarning("OS flakiness: D2 is docked and reports being floating"); // Close everything with a single shot. Expected behavior: Event loop stops - bool eventLoopStopped = true; - QTimer::singleShot(0, this, [mainWindow, d1, d2] { + QSignalSpy closeSpy(qApp, &QApplication::lastWindowClosed); + QObject localContext; + + QTimer::singleShot(0, &localContext, [&](){ mainWindow->close(); QTRY_VERIFY(!mainWindow->isVisible()); QTRY_VERIFY(d1->isVisible()); @@ -1531,19 +1772,12 @@ void tst_QDockWidget::closeAndDelete() d2->close(); QTRY_VERIFY(!d1->isVisible()); QTRY_VERIFY(!d2->isVisible()); - }); - - // Fallback timer to report event loop still running - QTimer::singleShot(100, this, [&eventLoopStopped] { - qCDebug(lcTestDockWidget) << "Last dock widget hasn't shout down event loop!"; - eventLoopStopped = false; + QTRY_COMPARE(closeSpy.count(), 1); QApplication::quit(); }); QApplication::exec(); - QTRY_VERIFY(eventLoopStopped); - // Check heap cleanup qCDebug(lcTestDockWidget) << "Deleting mainWindow"; up_mainWindow.reset(); @@ -1555,9 +1789,29 @@ void tst_QDockWidget::closeAndDelete() #endif // QT_BUILD_INTERNAL } +void tst_QDockWidget::closeUnclosable() +{ + QDockWidget *dockWidget = new QDockWidget("dock"); + dockWidget->setWidget(new QScrollArea); + dockWidget->setFeatures(QDockWidget::DockWidgetFloatable); + + QMainWindow mw; + mw.addDockWidget(Qt::TopDockWidgetArea, dockWidget); + mw.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&mw)); + dockWidget->setFloating(true); + + QCOMPARE(dockWidget->close(), false); + mw.close(); + QCOMPARE(dockWidget->close(), true); +} + // Test dock area permissions void tst_QDockWidget::dockPermissions() { + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); #ifdef Q_OS_WIN QSKIP("Test skipped on Windows platforms"); #endif // Q_OS_WIN @@ -1599,16 +1853,16 @@ void tst_QDockWidget::dockPermissions() // Move d2 to non allowed dock areas and verify it remains floating qCDebug(lcTestDockWidget) << "Move d2 to top dock"; - moveDockWidget(d2, dockPoint(mainWindow, Qt::TopDockWidgetArea)); + moveDockWidget(d2, dockPoint(mainWindow, Qt::TopDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); QTRY_VERIFY(d2->isFloating()); qCDebug(lcTestDockWidget) << "Move d2 to left dock"; //moveDockWidget(d2, d2->mapFrom(MainWindow, dockPoint(MainWindow, Qt::LeftDockWidgetArea))); - moveDockWidget(d2, dockPoint(mainWindow, Qt::LeftDockWidgetArea)); + moveDockWidget(d2, dockPoint(mainWindow, Qt::LeftDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); QTRY_VERIFY(d2->isFloating()); qCDebug(lcTestDockWidget) << "Move d2 to bottom dock"; - moveDockWidget(d2, dockPoint(mainWindow, Qt::BottomDockWidgetArea)); + moveDockWidget(d2, dockPoint(mainWindow, Qt::BottomDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); QTRY_VERIFY(d2->isFloating()); qCDebug(lcTestDockWidget) << "Waiting" << waitBeforeClose << "ms before closing."; @@ -1774,6 +2028,7 @@ void tst_QDockWidget::saveAndRestore() QCOMPARE(d1->isFloating(), isFloating1); QCOMPARE(d2->isFloating(), isFloating2); +#undef qCreateFloatingTabs #endif // QT_BUILD_INTERNAL } diff --git a/tests/auto/widgets/widgets/qdoublespinbox/CMakeLists.txt b/tests/auto/widgets/widgets/qdoublespinbox/CMakeLists.txt index 88e19e218d..b023174dc9 100644 --- a/tests/auto/widgets/widgets/qdoublespinbox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qdoublespinbox/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qdoublespinbox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdoublespinbox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qdoublespinbox SOURCES tst_qdoublespinbox.cpp diff --git a/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp b/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp index c365e09277..999b5cac07 100644 --- a/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp +++ b/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -1154,7 +1154,6 @@ void tst_QDoubleSpinBox::taskQTBUG_5008_textFromValueAndValidate() spinbox.show(); spinbox.activateWindow(); spinbox.setFocus(); - QApplicationPrivate::setActiveWindow(&spinbox); QVERIFY(QTest::qWaitForWindowActive(&spinbox)); QCOMPARE(static_cast<QWidget *>(&spinbox), QApplication::activeWindow()); QTRY_VERIFY(spinbox.hasFocus()); diff --git a/tests/auto/widgets/widgets/qfocusframe/CMakeLists.txt b/tests/auto/widgets/widgets/qfocusframe/CMakeLists.txt index 4f02720fc7..5e2f3bd955 100644 --- a/tests/auto/widgets/widgets/qfocusframe/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qfocusframe/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qfocusframe Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfocusframe LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qfocusframe SOURCES tst_qfocusframe.cpp diff --git a/tests/auto/widgets/widgets/qfocusframe/tst_qfocusframe.cpp b/tests/auto/widgets/widgets/qfocusframe/tst_qfocusframe.cpp index ce711b7ed7..560ba686cc 100644 --- a/tests/auto/widgets/widgets/qfocusframe/tst_qfocusframe.cpp +++ b/tests/auto/widgets/widgets/qfocusframe/tst_qfocusframe.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qfontcombobox/CMakeLists.txt b/tests/auto/widgets/widgets/qfontcombobox/CMakeLists.txt index 319aba0723..15dad99b9c 100644 --- a/tests/auto/widgets/widgets/qfontcombobox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qfontcombobox/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qfontcombobox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfontcombobox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qfontcombobox SOURCES tst_qfontcombobox.cpp diff --git a/tests/auto/widgets/widgets/qfontcombobox/tst_qfontcombobox.cpp b/tests/auto/widgets/widgets/qfontcombobox/tst_qfontcombobox.cpp index 149b6586ae..abb9262288 100644 --- a/tests/auto/widgets/widgets/qfontcombobox/tst_qfontcombobox.cpp +++ b/tests/auto/widgets/widgets/qfontcombobox/tst_qfontcombobox.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qframe/CMakeLists.txt b/tests/auto/widgets/widgets/qframe/CMakeLists.txt index 104fb07bca..2213f4a7d9 100644 --- a/tests/auto/widgets/widgets/qframe/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qframe/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qframe Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qframe LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/widgets/widgets/qframe/tst_qframe.cpp b/tests/auto/widgets/widgets/qframe/tst_qframe.cpp index 40cb897ed1..324c512219 100644 --- a/tests/auto/widgets/widgets/qframe/tst_qframe.cpp +++ b/tests/auto/widgets/widgets/qframe/tst_qframe.cpp @@ -1,6 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QFrame> diff --git a/tests/auto/widgets/widgets/qgroupbox/CMakeLists.txt b/tests/auto/widgets/widgets/qgroupbox/CMakeLists.txt index c440f902bd..0414ce0cf5 100644 --- a/tests/auto/widgets/widgets/qgroupbox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qgroupbox/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qgroupbox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgroupbox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgroupbox SOURCES tst_qgroupbox.cpp diff --git a/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp b/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp index 065310398c..b178707e7a 100644 --- a/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp +++ b/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -430,7 +430,7 @@ void tst_QGroupBox::childrenAreDisabled() layout->addWidget(new QRadioButton); box.setLayout(layout); - foreach (QObject *object, box.children()) { + for (QObject *object : box.children()) { if (QWidget *widget = qobject_cast<QWidget *>(object)) { QVERIFY(!widget->isEnabled()); QVERIFY(!widget->testAttribute(Qt::WA_ForceDisabled)); @@ -438,7 +438,7 @@ void tst_QGroupBox::childrenAreDisabled() } box.setChecked(true); - foreach (QObject *object, box.children()) { + for (QObject *object : box.children()) { if (QWidget *widget = qobject_cast<QWidget *>(object)) { QVERIFY(widget->isEnabled()); QVERIFY(!widget->testAttribute(Qt::WA_ForceDisabled)); @@ -446,7 +446,7 @@ void tst_QGroupBox::childrenAreDisabled() } box.setChecked(false); - foreach (QObject *object, box.children()) { + for (QObject *object : box.children()) { if (QWidget *widget = qobject_cast<QWidget *>(object)) { QVERIFY(!widget->isEnabled()); QVERIFY(!widget->testAttribute(Qt::WA_ForceDisabled)); @@ -462,7 +462,6 @@ void tst_QGroupBox::propagateFocus() QGroupBox box; QLineEdit lineEdit(&box); box.show(); - QApplicationPrivate::setActiveWindow(&box); QVERIFY(QTest::qWaitForWindowActive(&box)); box.setFocus(); QTRY_COMPARE(qApp->focusWidget(), static_cast<QWidget*>(&lineEdit)); diff --git a/tests/auto/widgets/widgets/qkeysequenceedit/CMakeLists.txt b/tests/auto/widgets/widgets/qkeysequenceedit/CMakeLists.txt index 8d445b5090..0cf0b1bdd3 100644 --- a/tests/auto/widgets/widgets/qkeysequenceedit/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qkeysequenceedit/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qkeysequenceedit Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qkeysequenceedit LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qkeysequenceedit SOURCES tst_qkeysequenceedit.cpp diff --git a/tests/auto/widgets/widgets/qkeysequenceedit/tst_qkeysequenceedit.cpp b/tests/auto/widgets/widgets/qkeysequenceedit/tst_qkeysequenceedit.cpp index 2bd0c6ed85..301be319bf 100644 --- a/tests/auto/widgets/widgets/qkeysequenceedit/tst_qkeysequenceedit.cpp +++ b/tests/auto/widgets/widgets/qkeysequenceedit/tst_qkeysequenceedit.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qlabel/CMakeLists.txt b/tests/auto/widgets/widgets/qlabel/CMakeLists.txt index 08cf667ded..e29000a6ca 100644 --- a/tests/auto/widgets/widgets/qlabel/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qlabel/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qlabel Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qlabel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp b/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp index 2775e2c683..325e188091 100644 --- a/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp +++ b/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -77,6 +77,8 @@ private Q_SLOTS: #ifndef QT_NO_CONTEXTMENU void taskQTBUG_7902_contextMenuCrash(); + void contextMenu_data(); + void contextMenu(); #endif void taskQTBUG_48157_dprPixmap(); @@ -561,6 +563,43 @@ void tst_QLabel::taskQTBUG_7902_contextMenuCrash() QTest::qWait(350); // No crash, it's allright. } + +void tst_QLabel::contextMenu_data() +{ + QTest::addColumn<QString>("text"); + QTest::addColumn<Qt::TextInteractionFlag>("interactionFlags"); + QTest::addColumn<bool>("showsContextMenu"); + + QTest::addRow("Read-only") << "Plain Text" + << Qt::NoTextInteraction + << false; + QTest::addRow("Selectable") << "Plain Text" + << Qt::TextEditorInteraction + << true; + QTest::addRow("Link") << "<a href=\"nowhere\">Rich text with link</a>" + << Qt::TextBrowserInteraction + << true; + QTest::addRow("Rich text") << "<b>Rich text without link</b>" + << Qt::TextBrowserInteraction + << true; +} + +void tst_QLabel::contextMenu() +{ + QFETCH(QString, text); + QFETCH(Qt::TextInteractionFlag, interactionFlags); + QFETCH(bool, showsContextMenu); + + QLabel label(text); + label.setTextInteractionFlags(interactionFlags); + label.show(); + QVERIFY(QTest::qWaitForWindowExposed(&label)); + + const QPoint menuPosition = label.rect().center(); + QContextMenuEvent cme(QContextMenuEvent::Mouse, menuPosition, label.mapToGlobal(menuPosition)); + QApplication::sendEvent(&label, &cme); + QCOMPARE(cme.isAccepted(), showsContextMenu); +} #endif void tst_QLabel::taskQTBUG_48157_dprPixmap() diff --git a/tests/auto/widgets/widgets/qlcdnumber/CMakeLists.txt b/tests/auto/widgets/widgets/qlcdnumber/CMakeLists.txt index 06f701892e..954ec095e5 100644 --- a/tests/auto/widgets/widgets/qlcdnumber/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qlcdnumber/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qlcdnumber Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qlcdnumber LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qlcdnumber SOURCES tst_qlcdnumber.cpp diff --git a/tests/auto/widgets/widgets/qlcdnumber/tst_qlcdnumber.cpp b/tests/auto/widgets/widgets/qlcdnumber/tst_qlcdnumber.cpp index c339268e8e..8fcf9c49fe 100644 --- a/tests/auto/widgets/widgets/qlcdnumber/tst_qlcdnumber.cpp +++ b/tests/auto/widgets/widgets/qlcdnumber/tst_qlcdnumber.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qlineedit/CMakeLists.txt b/tests/auto/widgets/widgets/qlineedit/CMakeLists.txt index 50ef23f1bc..22ecf40aed 100644 --- a/tests/auto/widgets/widgets/qlineedit/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qlineedit/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qlineedit Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qlineedit LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qlineedit SOURCES tst_qlineedit.cpp diff --git a/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp b/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp index efd59059d0..7a63c156f3 100644 --- a/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp +++ b/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -292,6 +292,13 @@ private slots: void QTBUG_60319_setInputMaskCheckImSurroundingText(); void testQuickSelectionWithMouse(); void inputRejected(); + void keyReleasePropagates(); + +#if QT_CONFIG(shortcut) + void deleteWordByKeySequence_data(); + void deleteWordByKeySequence(); +#endif + protected slots: void editingFinished(); @@ -3517,11 +3524,12 @@ void tst_QLineEdit::textMargin_data() QLineEdit testWidget; QFontMetrics metrics(testWidget.font()); const QString s = QLatin1String("MMM MMM MMM"); + const int windows11StyleHorizontalOffset = qApp->style()->inherits("QWindows11Style") ? 8 : 0; // Different styles generate different offsets, so // calculate the width rather than hardcode it. - const int pixelWidthOfM = metrics.horizontalAdvance(s, 1); - const int pixelWidthOfMMM_MM = metrics.horizontalAdvance(s, 6); + const int pixelWidthOfM = windows11StyleHorizontalOffset + metrics.horizontalAdvance(s, 1); + const int pixelWidthOfMMM_MM = windows11StyleHorizontalOffset + metrics.horizontalAdvance(s, 6); QTest::newRow("default-0") << 0 << 0 << 0 << 0 << QPoint(pixelWidthOfMMM_MM, 0) << 6; QTest::newRow("default-1") << 0 << 0 << 0 << 0 << QPoint(1, 1) << 0; @@ -3714,7 +3722,6 @@ void tst_QLineEdit::task174640_editingFinished() layout->addWidget(le2); mw.show(); - QApplicationPrivate::setActiveWindow(&mw); mw.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&mw)); QCOMPARE(&mw, QApplication::activeWindow()); @@ -3822,7 +3829,6 @@ void tst_QLineEdit::task210502_caseInsensitiveInlineCompletion() completer.setCompletionMode(QCompleter::InlineCompletion); lineEdit.setCompleter(&completer); lineEdit.show(); - QApplicationPrivate::setActiveWindow(&lineEdit); QVERIFY(QTest::qWaitForWindowActive(&lineEdit)); lineEdit.setFocus(); QTRY_VERIFY(lineEdit.hasFocus()); @@ -3923,7 +3929,6 @@ void tst_QLineEdit::task241436_passwordEchoOnEditRestoreEchoMode() testWidget->setFocus(); centerOnScreen(testWidget); testWidget->show(); - QApplicationPrivate::setActiveWindow(testWidget); QVERIFY(QTest::qWaitForWindowActive(testWidget)); QVERIFY(testWidget->hasFocus()); @@ -3974,7 +3979,6 @@ void tst_QLineEdit::taskQTBUG_4401_enterKeyClearsPassword() testWidget->selectAll(); centerOnScreen(testWidget); testWidget->show(); - QApplicationPrivate::setActiveWindow(testWidget); QVERIFY(QTest::qWaitForWindowActive(testWidget)); QTest::keyPress(testWidget, Qt::Key_Enter); @@ -4057,7 +4061,6 @@ void tst_QLineEdit::taskQTBUG_7395_readOnlyShortcut() le.show(); QVERIFY(QTest::qWaitForWindowExposed(&le)); - QApplicationPrivate::setActiveWindow(&le); QVERIFY(QTest::qWaitForWindowActive(&le)); le.setFocus(); QTRY_VERIFY(le.hasFocus()); @@ -4079,7 +4082,6 @@ void tst_QLineEdit::QTBUG697_paletteCurrentColorGroup() le.setPalette(p); le.show(); - QApplicationPrivate::setActiveWindow(&le); QVERIFY(QTest::qWaitForWindowActive(&le)); le.setFocus(); QTRY_VERIFY(le.hasFocus()); @@ -4574,7 +4576,6 @@ void tst_QLineEdit::clearButton() l->addWidget(listView); testWidget.move(300, 300); testWidget.show(); - QApplicationPrivate::setActiveWindow(&testWidget); QVERIFY(QTest::qWaitForWindowActive(&testWidget)); // Flip the clear button on,off, trying to detect crashes. filterLineEdit->setClearButtonEnabled(true); @@ -4685,7 +4686,8 @@ void tst_QLineEdit::sideWidgets() testWidget.move(300, 300); testWidget.show(); QVERIFY(QTest::qWaitForWindowExposed(&testWidget)); - foreach (QToolButton *button, lineEdit->findChildren<QToolButton *>()) + const auto buttons = lineEdit->findChildren<QToolButton *>(); + for (QToolButton *button : buttons) QCOMPARE(button->cursor().shape(), Qt::ArrowCursor); // Arbitrarily add/remove actions, trying to detect crashes. Add QTRY_VERIFY(false) to view the result. delete label3Action; @@ -4700,7 +4702,8 @@ void tst_QLineEdit::sideWidgets() template <class T> T *findAssociatedWidget(const QAction *a) { - foreach (QObject *w, a->associatedObjects()) { + const auto associatedObjects = a->associatedObjects(); + for (QObject *w : associatedObjects) { if (T *result = qobject_cast<T *>(w)) return result; } @@ -4974,7 +4977,7 @@ void tst_QLineEdit::QTBUG59957_clearButtonLeftmostAction() bool tst_QLineEdit::unselectingWithLeftOrRightChangesCursorPosition() { -#if defined Q_OS_WIN || defined Q_OS_QNX //Windows and QNX do not jump to the beginning of the selection +#if defined Q_OS_WIN || defined Q_OS_QNX || defined Q_OS_VXWORKS //Windows, QNX and VxWorks do not jump to the beginning of the selection return true; #endif // Platforms minimal/offscreen also need left after unselecting with right @@ -5143,5 +5146,149 @@ void tst_QLineEdit::inputRejected() QCOMPARE(spyInputRejected.size(), 2); } +void tst_QLineEdit::keyReleasePropagates() +{ + struct Dialog : QWidget + { + QLineEdit *lineEdit; + int releasedKey = {}; + + Dialog() + { + lineEdit = new QLineEdit; + QHBoxLayout *hbox = new QHBoxLayout; + + hbox->addWidget(lineEdit); + setLayout(hbox); + } + + protected: + void keyReleaseEvent(QKeyEvent *e) override + { + releasedKey = e->key(); + } + } dialog; + + dialog.show(); + QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + + QTest::keyPress(dialog.lineEdit, Qt::Key_A); + QTest::keyRelease(dialog.lineEdit, Qt::Key_A); + + QCOMPARE(dialog.releasedKey, Qt::Key_A); + + QTest::keyPress(dialog.lineEdit, Qt::Key_Alt); + QTest::keyRelease(dialog.lineEdit, Qt::Key_Alt); + + QCOMPARE(dialog.releasedKey, Qt::Key_Alt); +} + +#if QT_CONFIG(shortcut) + +void tst_QLineEdit::deleteWordByKeySequence_data() +{ + QTest::addColumn<QString>("startText"); + QTest::addColumn<int>("selectionStart"); + QTest::addColumn<int>("selectionEnd"); + QTest::addColumn<int>("cursorPosition"); + QTest::addColumn<QKeySequence::StandardKey>("key"); + QTest::addColumn<QString>("expectedText"); + QTest::addColumn<int>("expectedCursorPosition"); + + QTest::newRow("Delete start, no selection") + << QStringLiteral("Some Text") << 0 << 0 << 9 << QKeySequence::DeleteStartOfWord + << QStringLiteral("Some ") << 5; + QTest::newRow("Delete end, no selection") + << QStringLiteral("Some Text") << 0 << 0 << 5 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Some ") << 5; + QTest::newRow("Delete start from middle, no selection") + << QStringLiteral("Some Text") << 0 << 0 << 7 << QKeySequence::DeleteStartOfWord + << QStringLiteral("Some xt") << 5; + QTest::newRow("Delete end from middle, no selection") + << QStringLiteral("Some Text") << 0 << 0 << 7 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Some Te") << 7; + QTest::newRow("Delete end from first, no selection") + << QStringLiteral("Some Text") << 0 << 0 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Text") << 0; + + QTest::newRow("Delete start, full selection") + << QStringLiteral("Some Text") << 0 << 9 << 0 << QKeySequence::DeleteStartOfWord + << QStringLiteral("") << 0; + QTest::newRow("Delete end, full selection") + << QStringLiteral("Some Text") << 0 << 9 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("") << 0; + QTest::newRow("Delete start, full selection, single word") + << QStringLiteral("Some") << 0 << 4 << 0 << QKeySequence::DeleteStartOfWord + << QStringLiteral("") << 0; + QTest::newRow("Delete end, full selection, single word") + << QStringLiteral("Some") << 0 << 4 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("") << 0; + + QTest::newRow("Delete start, word selection") + << QStringLiteral("Some Text") << 5 << 9 << 0 << QKeySequence::DeleteStartOfWord + << QStringLiteral("Some ") << 5; + QTest::newRow("Delete end, word selection") + << QStringLiteral("Some Text") << 5 << 9 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Some ") << 5; + QTest::newRow("Delete start, partial word selection") + << QStringLiteral("Some Text") << 5 << 7 << 0 << QKeySequence::DeleteStartOfWord + << QStringLiteral("Some xt") << 5; + QTest::newRow("Delete end, partial word selection") + << QStringLiteral("Some Text") << 5 << 7 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Some xt") << 5; + QTest::newRow("Delete start, partial inner word selection") + << QStringLiteral("Some Text") << 6 << 8 << 0 << QKeySequence::DeleteStartOfWord + << QStringLiteral("Some Tt") << 6; + QTest::newRow("Delete end, partial inner word selection") + << QStringLiteral("Some Text") << 6 << 8 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Some Tt") << 6; + QTest::newRow("Delete start, selection with space") + << QStringLiteral("Some Text") << 3 << 9 << 0 << QKeySequence::DeleteStartOfWord + << QStringLiteral("Som") << 3; + QTest::newRow("Delete end, selection with space") + << QStringLiteral("Some Text") << 3 << 9 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Som") << 3; + QTest::newRow("Delete start, partial word selection with space") + << QStringLiteral("Some Text") << 3 << 7 << 0 << QKeySequence::DeleteStartOfWord + << QStringLiteral("Somxt") << 3; + QTest::newRow("Delete end, partial selection with space") + << QStringLiteral("Some Text") << 3 << 7 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Somxt") << 3; +} + +void tst_QLineEdit::deleteWordByKeySequence() +{ + QFETCH(QString, startText); + QFETCH(int, selectionStart); + QFETCH(int, selectionEnd); + QFETCH(int, cursorPosition); + QFETCH(QKeySequence::StandardKey, key); + QFETCH(QString, expectedText); + QFETCH(int, expectedCursorPosition); + + QWidget widget; + + QLineEdit *lineEdit = new QLineEdit(startText, &widget); + lineEdit->setFocus(); + lineEdit->setCursorPosition(cursorPosition); + if (selectionStart != selectionEnd) + lineEdit->setSelection(selectionStart, selectionEnd - selectionStart); + + widget.show(); + + QVERIFY(QTest::qWaitForWindowActive(&widget)); + + QTestEventList keys; + addKeySequenceStandardKey(keys, key); + keys.simulate(lineEdit); + + QCOMPARE(lineEdit->text(), expectedText); + QCOMPARE(lineEdit->selectionStart(), -1); + QCOMPARE(lineEdit->selectionEnd(), -1); + QCOMPARE(lineEdit->cursorPosition(), expectedCursorPosition); +} + +#endif // QT_CONFIG(shortcut) + QTEST_MAIN(tst_QLineEdit) #include "tst_qlineedit.moc" diff --git a/tests/auto/widgets/widgets/qmainwindow/BLACKLIST b/tests/auto/widgets/widgets/qmainwindow/BLACKLIST new file mode 100644 index 0000000000..28b08026d8 --- /dev/null +++ b/tests/auto/widgets/widgets/qmainwindow/BLACKLIST @@ -0,0 +1,2 @@ +[QTBUG52175_tabifiedDockWidgetActivated] +macos arm ci diff --git a/tests/auto/widgets/widgets/qmainwindow/CMakeLists.txt b/tests/auto/widgets/widgets/qmainwindow/CMakeLists.txt index 5cbc23dade..93f94f78c7 100644 --- a/tests/auto/widgets/widgets/qmainwindow/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qmainwindow/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qmainwindow Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmainwindow LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmainwindow SOURCES tst_qmainwindow.cpp diff --git a/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp b/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp index 91aa651bab..093af90d1c 100644 --- a/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp +++ b/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -104,6 +104,7 @@ private slots: void restoreStateFromPreviousVersion(); void restoreStateSizeChanged_data(); void restoreStateSizeChanged(); + void restoreAndModify(); void createPopupMenu(); void hideBeforeLayout(); #ifdef QT_BUILD_INTERNAL @@ -1358,12 +1359,13 @@ void tst_QMainWindow::restoreState() //tests the restoration of the previous versions of window settings void tst_QMainWindow::restoreStateFromPreviousVersion() { - QList<QByteArray> restoreData; - restoreData << QByteArray((char*)restoreData41, sizeof(restoreData41)) - << QByteArray((char*)restoreData42, sizeof(restoreData42)) - << QByteArray((char*)restoreData43, sizeof(restoreData43)); + const QByteArray restoreData[] = { + QByteArray((char*)restoreData41, sizeof(restoreData41)), + QByteArray((char*)restoreData42, sizeof(restoreData42)), + QByteArray((char*)restoreData43, sizeof(restoreData43)), + }; - foreach(QByteArray ba, restoreData) { + for (const QByteArray &ba : restoreData) { QMainWindow win; win.setCentralWidget(new QTextEdit); @@ -1475,6 +1477,70 @@ void tst_QMainWindow::restoreStateSizeChanged() } } +/*! + If a main window's state is restored but also modified, then we + might have to forget the restored state to avoid dangling pointers. + See comment in QMainWindowLayout::applyRestoredState() and QTBUG-120025. +*/ +void tst_QMainWindow::restoreAndModify() +{ + class MainWindow : public QMainWindow + { + public: + MainWindow() + { + setCentralWidget(new QTextEdit); + + customers = new QDockWidget(tr("Customers"), this); + customers->setObjectName("Customers"); + customers->setAllowedAreas(Qt::LeftDockWidgetArea | + Qt::RightDockWidgetArea); + customers->setWidget(new QTextEdit); + addDockWidget(Qt::RightDockWidgetArea, customers); + + paragraphs = new QDockWidget(tr("Paragraphs"), this); + paragraphs->setObjectName("Paragraphs"); + paragraphs->setWidget(new QTextEdit); + addDockWidget(Qt::RightDockWidgetArea, paragraphs); + } + + void restore() + { + if (!savedGeometry.isEmpty()) + restoreGeometry(savedGeometry); + setWindowState(Qt::WindowMaximized); + if (!savedState.isEmpty()) + restoreState(savedState); + + tabifyDockWidget(customers, paragraphs); + } + protected: + void closeEvent(QCloseEvent *event) override + { + savedGeometry = saveGeometry(); + savedState = saveState(); + + return QMainWindow::closeEvent(event); + } + private: + QByteArray savedGeometry; + QByteArray savedState; + + QDockWidget *customers; + QDockWidget *paragraphs; + + } mainWindow; + + mainWindow.restore(); + mainWindow.show(); + QVERIFY(QTest::qWaitForWindowExposed(&mainWindow)); + mainWindow.close(); + + mainWindow.restore(); + mainWindow.show(); + QVERIFY(QTest::qWaitForWindowExposed(&mainWindow)); +} + void tst_QMainWindow::createPopupMenu() { { @@ -1671,25 +1737,25 @@ void MoveSeparator::apply(QMainWindow *mw) const QMap<QString, QRect> dockWidgetGeometries(QMainWindow *mw) { QMap<QString, QRect> result; - QList<QDockWidget*> dockWidgets = mw->findChildren<QDockWidget*>(); - foreach (QDockWidget *dw, dockWidgets) + const QList<QDockWidget*> dockWidgets = mw->findChildren<QDockWidget*>(); + for (QDockWidget *dw : dockWidgets) result.insert(dw->objectName(), dw->geometry()); return result; } #define COMPARE_DOCK_WIDGET_GEOS(_oldGeos, _newGeos) \ { \ - QMap<QString, QRect> __oldGeos = _oldGeos; \ - QMap<QString, QRect> __newGeos = _newGeos; \ - QCOMPARE(__newGeos.keys(), __oldGeos.keys()); \ - QStringList __keys = __newGeos.keys(); \ - foreach (const QString &key, __keys) { \ - QRect __r1 = __oldGeos[key]; \ - QRect __r2 = __newGeos[key]; \ - if (__r1 != __r2) \ - qWarning() << key << __r1 << __r2; \ + QMap<QString, QRect> _v_oldGeos = _oldGeos; \ + QMap<QString, QRect> _v_newGeos = _newGeos; \ + QCOMPARE(_v_newGeos.keys(), _v_oldGeos.keys()); \ + const QStringList _v_keys = _v_newGeos.keys(); \ + for (const QString &key : _v_keys) { \ + QRect _v_r1 = _v_oldGeos[key]; \ + QRect _v_r2 = _v_newGeos[key]; \ + if (_v_r1 != _v_r2) \ + qWarning() << key << _v_r1 << _v_r2; \ } \ - QCOMPARE(__newGeos, __oldGeos); \ + QCOMPARE(_v_newGeos, _v_oldGeos); \ } #ifdef QT_BUILD_INTERNAL @@ -1735,8 +1801,8 @@ void tst_QMainWindow::saveRestore_data() #ifdef QT_BUILD_INTERNAL void tst_QMainWindow::saveRestore() { - QFETCH(AddList, addList); - QFETCH(MoveList, moveList); + QFETCH(const AddList, addList); + QFETCH(const MoveList, moveList); QByteArray stateData; QMap<QString, QRect> dockWidgetGeos; @@ -1748,12 +1814,12 @@ void tst_QMainWindow::saveRestore() QTextEdit centralWidget("The rain in Spain falls mainly on the plains"); mainWindow.setCentralWidget(¢ralWidget); - foreach (const AddDockWidget &adw, addList) + for (const AddDockWidget &adw : addList) adw.apply(&mainWindow); mainWindow.show(); - foreach (const MoveSeparator &ms, moveList) + for (const MoveSeparator &ms : moveList) ms.apply(&mainWindow); dockWidgetGeos = dockWidgetGeometries(&mainWindow); @@ -1771,7 +1837,7 @@ void tst_QMainWindow::saveRestore() QTextEdit centralWidget("The rain in Spain falls mainly on the plains"); mainWindow.setCentralWidget(¢ralWidget); - foreach (const AddDockWidget &adw, addList) + for (const AddDockWidget &adw : addList) adw.apply(&mainWindow); mainWindow.show(); @@ -1788,7 +1854,7 @@ void tst_QMainWindow::saveRestore() QTextEdit centralWidget("The rain in Spain falls mainly on the plains"); mainWindow.setCentralWidget(¢ralWidget); - foreach (const AddDockWidget &adw, addList) + for (const AddDockWidget &adw : addList) adw.apply(&mainWindow); mainWindow.resize(size); mainWindow.restoreState(stateData); @@ -1864,7 +1930,7 @@ void tst_QMainWindow::addToolbarAfterShow() void tst_QMainWindow::centralWidgetSize() { if (qGuiApp->styleHints()->showIsFullScreen()) - QSKIP("The platform is auto maximizing, so the test makes no sense");; + QSKIP("The platform is auto maximizing, so the test makes no sense"); QMainWindow mainWindow; mainWindow.menuBar()->addMenu("menu"); @@ -2096,10 +2162,11 @@ void tst_QMainWindow::resizeDocks() mw.setDockNestingEnabled(true); mw.resize(1800, 600); - foreach (const AddDockWidget &i, addList) + for (const AddDockWidget &i : std::as_const(addList)) i.apply(&mw); - foreach (QDockWidget *dw, mw.findChildren<QDockWidget *>()) + const auto dockWidgets = mw.findChildren<QDockWidget *>(); + for (QDockWidget *dw : dockWidgets) dw->setStyleSheet( "* { background-color: " + dw->objectName() +" }"); mw.setCentralWidget(new QTextEdit); @@ -2108,11 +2175,11 @@ void tst_QMainWindow::resizeDocks() QVERIFY(QTest::qWaitForWindowExposed(&mw)); QFETCH(Qt::Orientation, orientation); - QFETCH(QStringList, docks); + QFETCH(const QStringList, docks); QFETCH(QList<int>, sizes); QList<QDockWidget *> list; - foreach (const QString &name, docks) { + for (const QString &name : docks) { QDockWidget *d = mw.findChild<QDockWidget *>(name); QVERIFY(d); list << d; diff --git a/tests/auto/widgets/widgets/qmdiarea/BLACKLIST b/tests/auto/widgets/widgets/qmdiarea/BLACKLIST index 3091b73269..c3234bf56c 100644 --- a/tests/auto/widgets/widgets/qmdiarea/BLACKLIST +++ b/tests/auto/widgets/widgets/qmdiarea/BLACKLIST @@ -1,15 +1,7 @@ [tileSubWindows] -ubuntu-16.04 -rhel-7.6 centos opensuse-leap macos -ubuntu-18.04 -ubuntu-20.04 -macos -rhel-7.4 -macos -opensuse-42.3 [resizeTimer] macos diff --git a/tests/auto/widgets/widgets/qmdiarea/CMakeLists.txt b/tests/auto/widgets/widgets/qmdiarea/CMakeLists.txt index 34332758b6..5f61dc8664 100644 --- a/tests/auto/widgets/widgets/qmdiarea/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qmdiarea/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qmdiarea Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmdiarea LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmdiarea SOURCES tst_qmdiarea.cpp diff --git a/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp b/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp index 946688f8b9..ef00dcaac0 100644 --- a/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp +++ b/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -176,7 +176,7 @@ static bool verifyArrangement(QMdiArea *mdiArea, Arrangement arrangement, const // QWidget::childAt with the position of the first one and subsequently adding // dx and dy. QPoint subWindowPos(20, 5); - foreach (int expectedIndex, expectedIndices) { + for (int expectedIndex : expectedIndices) { QMdiSubWindow *expected = subWindows.at(expectedIndex); expected->raise(); if (mdiArea->viewport()->childAt(subWindowPos) != expected) @@ -187,7 +187,7 @@ static bool verifyArrangement(QMdiArea *mdiArea, Arrangement arrangement, const } // Restore stacking order. - foreach (QMdiSubWindow *subWindow, activationOrderList) { + for (QMdiSubWindow *subWindow : activationOrderList) { mdiArea->setActiveSubWindow(subWindow); qApp->processEvents(); } @@ -260,6 +260,7 @@ private slots: void task_236750(); void qtbug92240_title_data(); void qtbug92240_title(); + void tabbedview_singleSubWindow(); void tabbedview_activefirst(); void tabbedview_activesecond(); void tabbedview_activethird(); @@ -514,7 +515,6 @@ void tst_QMdiArea::subWindowActivatedWithMinimize() QSignalSpy spy(workspace, SIGNAL(subWindowActivated(QMdiSubWindow*))); connect( workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(activeChanged(QMdiSubWindow*)) ); mw.show(); - QApplicationPrivate::setActiveWindow(&mw); QWidget *widget = new QWidget(workspace); widget->setAttribute(Qt::WA_DeleteOnClose); QMdiSubWindow *window1 = workspace->addSubWindow(widget); @@ -659,7 +659,6 @@ void tst_QMdiArea::changeWindowTitle() #endif mw->show(); - QApplicationPrivate::setActiveWindow(mw); #ifdef USE_SHOW mw->showFullScreen(); @@ -963,7 +962,6 @@ void tst_QMdiArea::activeSubWindow() mainWindow.addDockWidget(Qt::LeftDockWidgetArea, dockWidget); mainWindow.show(); - QApplicationPrivate::setActiveWindow(&mainWindow); QVERIFY(QTest::qWaitForWindowActive(&mainWindow)); QCOMPARE(mdiArea->activeSubWindow(), subWindow); QCOMPARE(qApp->focusWidget(), (QWidget *)subWindowLineEdit); @@ -986,10 +984,8 @@ void tst_QMdiArea::activeSubWindow() dummyTopLevel.show(); QVERIFY(QTest::qWaitForWindowExposed(&dummyTopLevel)); - QApplicationPrivate::setActiveWindow(&dummyTopLevel); QCOMPARE(mdiArea->activeSubWindow(), subWindow); - QApplicationPrivate::setActiveWindow(&mainWindow); QCOMPARE(mdiArea->activeSubWindow(), subWindow); //task 202657 @@ -1022,7 +1018,6 @@ void tst_QMdiArea::currentSubWindow() // Move focus to another top-level and check that we still // have an active window. - QApplicationPrivate::setActiveWindow(&dummyTopLevel); QCOMPARE(qApp->activeWindow(), (QWidget *)&dummyTopLevel); QVERIFY(mdiArea.activeSubWindow()); @@ -1044,11 +1039,9 @@ void tst_QMdiArea::currentSubWindow() QCOMPARE(mdiArea.activeSubWindow(), active); QCOMPARE(mdiArea.currentSubWindow(), active); - QApplicationPrivate::setActiveWindow(&dummyTopLevel); QVERIFY(mdiArea.activeSubWindow()); QCOMPARE(mdiArea.currentSubWindow(), active); - QApplicationPrivate::setActiveWindow(&mdiArea); active->show(); QCOMPARE(mdiArea.activeSubWindow(), active); @@ -1135,7 +1128,8 @@ void tst_QMdiArea::addAndRemoveWindows() } // removeSubWindow - foreach (QWidget *window, workspace.subWindowList()) { + const auto subWindows = workspace.subWindowList(); + for (QWidget *window : subWindows) { workspace.removeSubWindow(window); delete window; } @@ -1253,7 +1247,6 @@ void tst_QMdiArea::closeWindows() { QMdiArea workspace; workspace.show(); - QApplicationPrivate::setActiveWindow(&workspace); // Close widget QWidget *widget = new QWidget; @@ -1305,7 +1298,6 @@ void tst_QMdiArea::activateNextAndPreviousWindow() { QMdiArea workspace; workspace.show(); - QApplicationPrivate::setActiveWindow(&workspace); const int windowCount = 10; QMdiSubWindow *windows[windowCount]; @@ -1389,7 +1381,6 @@ void tst_QMdiArea::subWindowList() QMdiArea workspace; workspace.show(); - QApplicationPrivate::setActiveWindow(&workspace); QVERIFY(QTest::qWaitForWindowActive(&workspace)); QList<QMdiSubWindow *> activationOrder; @@ -1685,7 +1676,8 @@ void tst_QMdiArea::tileSubWindows() QTRY_COMPARE(workspace.size(), QSize(350, 150)); const QSize minSize(600, 130); - foreach (QMdiSubWindow *subWindow, workspace.subWindowList()) + const auto subWindows = workspace.subWindowList(); + for (QMdiSubWindow *subWindow : subWindows) subWindow->setMinimumSize(minSize); QCOMPARE(workspace.size(), QSize(350, 150)); @@ -1843,7 +1835,7 @@ void tst_QMdiArea::resizeMaximizedChildWindows() int newSize = startSize + increment * windowCount; QCOMPARE(workspaceSize, QSize(newSize, newSize)); - foreach (QWidget *window, windows) + for (QWidget *window : std::as_const(windows)) QCOMPARE(window->rect(), workspace.contentsRect()); } @@ -1877,7 +1869,6 @@ void tst_QMdiArea::dontMaximizeSubWindowOnActivation() QMdiArea mdiArea; mdiArea.show(); QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); - QApplicationPrivate::setActiveWindow(&mdiArea); // Add one maximized window. mdiArea.addSubWindow(new QWidget)->showMaximized(); @@ -1893,7 +1884,7 @@ void tst_QMdiArea::dontMaximizeSubWindowOnActivation() } // Verify that activated windows still are maximized on activation. - QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList(); + const QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList(); for (int i = 0; i < subWindows.size(); ++i) { mdiArea.activateNextSubWindow(); QMdiSubWindow *window = subWindows.at(i); @@ -1930,7 +1921,7 @@ void tst_QMdiArea::dontMaximizeSubWindowOnActivation() QVERIFY(mdiArea.activeSubWindow()->isMaximized()); // Minimize all windows. - foreach (QMdiSubWindow *window, subWindows) { + for (QMdiSubWindow *window : subWindows) { window->showMinimized(); QVERIFY(window->isMinimized()); qApp->processEvents(); @@ -2262,7 +2253,6 @@ void tst_QMdiArea::tabBetweenSubWindows() mdiArea.show(); QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); - QApplicationPrivate::setActiveWindow(&mdiArea); QWidget *focusWidget = subWindows.back()->widget(); QCOMPARE(qApp->focusWidget(), focusWidget); @@ -2317,7 +2307,7 @@ void tst_QMdiArea::setViewMode() QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); QMdiSubWindow *activeSubWindow = mdiArea.activeSubWindow(); - QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList(); + const QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList(); // Default. QVERIFY(!activeSubWindow->isMaximized()); @@ -2386,13 +2376,12 @@ void tst_QMdiArea::setViewMode() QVERIFY(tabBar->isTabEnabled(tabIndex)); // Remove sub-windows and make sure the tab is removed. - foreach (QMdiSubWindow *subWindow, subWindows) { + for (QMdiSubWindow *subWindow : subWindows) { if (subWindow != activeSubWindow) { mdiArea.removeSubWindow(subWindow); delete subWindow; } } - subWindows.clear(); QCOMPARE(tabBar->count(), 1); // Go back to default (QMdiArea::SubWindowView). @@ -2593,8 +2582,11 @@ void tst_QMdiArea::nativeSubWindows() // No native widgets. QVERIFY(!mdiArea.viewport()->internalWinId()); - foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList()) - QVERIFY(!subWindow->internalWinId()); + { + const auto subWindows = mdiArea.subWindowList(); + for (QMdiSubWindow *subWindow : subWindows) + QVERIFY(!subWindow->internalWinId()); + } QWidget *nativeWidget = new QWidget; QVERIFY(nativeWidget->winId()); // enforce native window. @@ -2603,8 +2595,11 @@ void tst_QMdiArea::nativeSubWindows() // The viewport and all the sub-windows must be native. QVERIFY(mdiArea.viewport()->internalWinId()); - foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList()) - QVERIFY(subWindow->internalWinId()); + { + const auto subWindows = mdiArea.subWindowList(); + for (QMdiSubWindow *subWindow : subWindows) + QVERIFY(subWindow->internalWinId()); + } // Add a non-native widget. This should become native. QMdiSubWindow *subWindow = new QMdiSubWindow; @@ -2625,8 +2620,11 @@ void tst_QMdiArea::nativeSubWindows() // The viewport and all the sub-windows must be native. QVERIFY(mdiArea.viewport()->internalWinId()); - foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList()) - QVERIFY(subWindow->internalWinId()); + { + const auto subWindows = mdiArea.subWindowList(); + for (QMdiSubWindow *subWindow : subWindows) + QVERIFY(subWindow->internalWinId()); + } } { // Make a sub-window native *after* it's added to the area. @@ -2642,9 +2640,12 @@ void tst_QMdiArea::nativeSubWindows() // All the sub-windows should be native at this point QVERIFY(mdiArea.viewport()->internalWinId()); - foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList()) + { + const auto subWindows = mdiArea.subWindowList(); + for (QMdiSubWindow *subWindow : subWindows) QVERIFY(subWindow->internalWinId()); } + } } void tst_QMdiArea::task_209615() @@ -2715,6 +2716,21 @@ void tst_QMdiArea::qtbug92240_title() QTRY_COMPARE(w.windowTitle(), QLatin1String("QTBUG-92240 - [2]")); } +void tst_QMdiArea::tabbedview_singleSubWindow() +{ + // With only one sub-window, setViewMode() before addSubWindow(); and addSubWindow() + // before show(), ensure the sub-window is properly activated. + QMdiArea mdiArea; + mdiArea.setViewMode(QMdiArea::TabbedView); + auto *w = new QWidget(&mdiArea); + mdiArea.addSubWindow(w); + mdiArea.show(); + QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); + auto *sub = mdiArea.subWindowList().at(0); + QCOMPARE(mdiArea.activeSubWindow(), sub); + QVERIFY(sub->isMaximized()); +} + static void setupMdiAreaWithTabbedView(QMdiArea &mdiArea) { mdiArea.setViewMode(QMdiArea::TabbedView); diff --git a/tests/auto/widgets/widgets/qmdisubwindow/CMakeLists.txt b/tests/auto/widgets/widgets/qmdisubwindow/CMakeLists.txt index 23b3b89296..45b7b74ac4 100644 --- a/tests/auto/widgets/widgets/qmdisubwindow/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qmdisubwindow/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qmdisubwindow Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmdisubwindow LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmdisubwindow SOURCES tst_qmdisubwindow.cpp diff --git a/tests/auto/widgets/widgets/qmdisubwindow/tst_qmdisubwindow.cpp b/tests/auto/widgets/widgets/qmdisubwindow/tst_qmdisubwindow.cpp index 9adaf81116..60b675b18a 100644 --- a/tests/auto/widgets/widgets/qmdisubwindow/tst_qmdisubwindow.cpp +++ b/tests/auto/widgets/widgets/qmdisubwindow/tst_qmdisubwindow.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qmdisubwindow.h" #include "private/qmdisubwindow_p.h" @@ -384,7 +384,6 @@ void tst_QMdiSubWindow::mainWindowSupport() mainWindow.setCentralWidget(workspace); mainWindow.show(); mainWindow.menuBar()->setVisible(true); - QApplicationPrivate::setActiveWindow(&mainWindow); bool nativeMenuBar = mainWindow.menuBar()->isNativeMenuBar(); // QMainWindow's window title is empty, so on a platform which does NOT have a native menubar, @@ -510,7 +509,6 @@ void tst_QMdiSubWindow::emittingOfSignals() workspace.setWindowTitle(QLatin1String(QTest::currentTestFunction())); workspace.show(); QCoreApplication::processEvents(); - QApplicationPrivate::setActiveWindow(&workspace); QMdiSubWindow *window = qobject_cast<QMdiSubWindow *>(workspace.addSubWindow(new QWidget)); QCoreApplication::processEvents(); window->show(); @@ -954,6 +952,8 @@ void tst_QMdiSubWindow::mouseDoubleClick() if (!window->style()->styleHint(QStyle::SH_TitleBar_NoBorder, &options, window)) height += window->isMinimized() ? 8 : 4; QPoint mousePosition(window->width() / 2, height - 1); + if (window->style()->inherits("QWindows11Style")) + mousePosition = QPoint(8, height - 1); sendMouseMove(window, mousePosition, Qt::NoButton); // Without Qt::WindowShadeButtonHint flag set @@ -981,8 +981,10 @@ void tst_QMdiSubWindow::mouseDoubleClick() window->showMinimized(); QVERIFY(window->isMinimized()); + //Process QEvent::WindowStateChange + QCoreApplication::processEvents(); sendMouseDoubleClick(window, mousePosition); - QVERIFY(!window->isMinimized()); + QTRY_VERIFY(!window->isMinimized()); QCOMPARE(window->geometry(), originalGeometry); } @@ -1228,7 +1230,6 @@ void tst_QMdiSubWindow::restoreFocusOverCreation() subWidget1->m_lineEdit2->setFocus(); subWindow1->show(); mdiArea.show(); - QApplicationPrivate::setActiveWindow(&mdiArea); QVERIFY(QTest::qWaitForWindowActive(&mdiArea)); QCOMPARE(QApplication::focusWidget(), subWidget1->m_lineEdit2); @@ -1373,7 +1374,7 @@ void tst_QMdiSubWindow::setWindowTitle() // other widgets which are not real top-level widgets). QCOMPARE(window->windowTitle(), expectedWindowTitle); - textEdit->setWindowModified(true);; + textEdit->setWindowModified(true); expectedWindowTitle = QLatin1String("Override child title"); window->setWindowTitle(expectedWindowTitle); QVERIFY(window->isWindowModified()); @@ -1867,7 +1868,6 @@ void tst_QMdiSubWindow::closeOnDoubleClick() void tst_QMdiSubWindow::setFont() { - QSKIP("This test function is unstable in CI, please see QTBUG-22544"); QMdiArea mdiArea; mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); QMdiSubWindow *subWindow = mdiArea.addSubWindow(new TestPushButton(QLatin1String("test"))); @@ -1953,7 +1953,6 @@ void tst_QMdiSubWindow::task_182852() mainWindow.setCentralWidget(workspace); mainWindow.show(); mainWindow.menuBar()->setVisible(true); - QApplicationPrivate::setActiveWindow(&mainWindow); if (mainWindow.menuBar()->isNativeMenuBar()) return; // The main window's title is not overwritten if we have a native menubar (macOS, Unity etc.) diff --git a/tests/auto/widgets/widgets/qmenu/BLACKLIST b/tests/auto/widgets/widgets/qmenu/BLACKLIST index dc1ab8ddd3..88484ddffa 100644 --- a/tests/auto/widgets/widgets/qmenu/BLACKLIST +++ b/tests/auto/widgets/widgets/qmenu/BLACKLIST @@ -16,6 +16,7 @@ android android [pushButtonPopulateOnAboutToShow] android +wayland [QTBUG8122_widgetActionCrashOnClose] android [click_while_dismissing_submenu] diff --git a/tests/auto/widgets/widgets/qmenu/CMakeLists.txt b/tests/auto/widgets/widgets/qmenu/CMakeLists.txt index d639119bb3..1716ab85da 100644 --- a/tests/auto/widgets/widgets/qmenu/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qmenu/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qmenu Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmenu LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmenu SOURCES tst_qmenu.cpp diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp index 73cdbe87fe..57cc404fc7 100644 --- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp +++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QtTest/private/qtesthelpers_p.h> @@ -28,6 +28,7 @@ #include <qpa/qplatformintegration.h> #include <QtWidgets/private/qapplication_p.h> +#include <QtWidgets/private/qmenu_p.h> using namespace QTestPrivate; @@ -114,6 +115,8 @@ private slots: void screenOrientationChangedCloseMenu(); void deleteWhenTriggered(); + void nestedTearOffDetached(); + protected slots: void onActivated(QAction*); void onHighlighted(QAction*); @@ -528,7 +531,6 @@ void tst_QMenu::overrideMenuAction() m->addAction(aQuit); w.show(); - QApplicationPrivate::setActiveWindow(&w); w.setFocus(); QVERIFY(QTest::qWaitForWindowActive(&w)); QVERIFY(w.hasFocus()); @@ -1143,14 +1145,18 @@ void tst_QMenu::pushButtonPopulateOnAboutToShow() QSKIP("Your window manager won't allow a window against the bottom of the screen"); } - QTimer::singleShot(300, buttonMenu, SLOT(hide())); QTest::mouseClick(&b, Qt::LeftButton, Qt::NoModifier, b.rect().center()); + QVERIFY(QTest::qWaitForWindowExposed(buttonMenu)); + QTest::qWait(300); + buttonMenu->hide(); QVERIFY2(!buttonMenu->geometry().intersects(b.geometry()), msgGeometryIntersects(buttonMenu->geometry(), b.geometry())); // note: we're assuming that, if we previously got the desired geometry, we'll get it here too b.move(10, screen.bottom()-buttonMenu->height()-5); - QTimer::singleShot(300, buttonMenu, SLOT(hide())); QTest::mouseClick(&b, Qt::LeftButton, Qt::NoModifier, b.rect().center()); + QVERIFY(QTest::qWaitForWindowExposed(buttonMenu)); + QTest::qWait(300); + buttonMenu->hide(); QVERIFY2(!buttonMenu->geometry().intersects(b.geometry()), msgGeometryIntersects(buttonMenu->geometry(), b.geometry())); } @@ -1606,7 +1612,6 @@ void tst_QMenu::transientParent() QWindow *topLevel = window.windowHandle(); QVERIFY(topLevel); - QApplicationPrivate::setActiveWindow(&window); window.setFocus(); QVERIFY(QTest::qWaitForWindowActive(&window)); QVERIFY(window.hasFocus()); @@ -2031,5 +2036,82 @@ void tst_QMenu::deleteWhenTriggered() QTRY_VERIFY(!menu); } +/* + QMenu uses the caused-stack to create the parent/child relationship + for tear-off menus. Since QTornOffMenu set the DeleteOnClose flag, closing a + tear-off in the parent chain will result in a null-pointer in the caused-stack. + Verify that we don't crash when traversing the chain, as reported in QTBUG-112217. + + The test has to open the submenus by hovering of the menu action, otherwise + the caused-stack remains empty and the issue doesn't reproduce. Due to QMenu's + timing and "sloppiness", we need to move the mouse within the action, with some + waiting and event processing in between to trigger the opening of the submenu. + If this fails we skip, as we then can't test what we are trying to test. +*/ +void tst_QMenu::nestedTearOffDetached() +{ + // Since QTornOffMenu is not declared in qmenuprivate.h we can't access the + // object even through QMenuPrivate. So use an event filter to watch out for + // a QTornOffMenu showing. + class TearOffWatcher : public QObject + { + public: + QMenu *tornOffMenu = nullptr; + protected: + bool eventFilter(QObject *receiver, QEvent *event) override + { + if (event->type() == QEvent::Show && receiver->inherits("QTornOffMenu")) + tornOffMenu = qobject_cast<QMenu *>(receiver); + return QObject::eventFilter(receiver, event); + } + } watcher; + qApp->installEventFilter(&watcher); + + QWidget widget; + QMenu *menu = new QMenu("Context", &widget); + + MenuMetrics mm(menu); + const int tearOffOffset = mm.fw + mm.vmargin + mm.tearOffHeight / 2; + + QMenu *subMenu = menu->addMenu("SubMenu"); + menu->setTearOffEnabled(true); + QMenu *subSubMenu = subMenu->addMenu("SubSubMenu"); + subMenu->setTearOffEnabled(true); + subSubMenu->addAction("Action!"); + subSubMenu->setTearOffEnabled(true); + + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + + // open and tear off context menu + menu->popup(widget.geometry().center()); + QTest::mouseClick(menu, Qt::LeftButton, {}, QPoint(menu->width() / 2, tearOffOffset)); + + QMenu *menuTorn = watcher.tornOffMenu; + watcher.tornOffMenu = nullptr; + QVERIFY(menuTorn); + QVERIFY(QTest::qWaitForWindowExposed(menuTorn)); + + // open second menu and tear-off + QTest::mouseMove(menuTorn, menuTorn->actionGeometry(subMenu->menuAction()).topLeft()); + QTest::qWait(100); + QTest::mouseMove(menuTorn, menuTorn->actionGeometry(subMenu->menuAction()).center()); + if (!QTest::qWaitFor([subMenu]{ return subMenu->isVisible(); })) + QSKIP("Menu failed to show, skipping test"); + + QTest::mouseClick(subMenu, Qt::LeftButton, {}, QPoint(subMenu->width() / 2, tearOffOffset)); + menuTorn = watcher.tornOffMenu; + QVERIFY(menuTorn); + QVERIFY(QTest::qWaitForWindowExposed(menuTorn)); + // close the top level tear off + menu->hideTearOffMenu(); + // open third menu and tear-off + QTest::mouseMove(menuTorn, menuTorn->actionGeometry(subSubMenu->menuAction()).topLeft()); + QTest::qWait(100); + QTest::mouseMove(menuTorn, menuTorn->actionGeometry(subSubMenu->menuAction()).center()); + QTRY_VERIFY(subSubMenu->isVisible()); + QTest::mouseClick(subSubMenu, Qt::LeftButton, {}, QPoint(subSubMenu->width() / 2, tearOffOffset)); +} + QTEST_MAIN(tst_QMenu) #include "tst_qmenu.moc" diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu_mac.mm b/tests/auto/widgets/widgets/qmenu/tst_qmenu_mac.mm index be58cff8e6..ba76c28fd4 100644 --- a/tests/auto/widgets/widgets/qmenu/tst_qmenu_mac.mm +++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu_mac.mm @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #import <AppKit/AppKit.h> diff --git a/tests/auto/widgets/widgets/qmenubar/BLACKLIST b/tests/auto/widgets/widgets/qmenubar/BLACKLIST index 05984b6039..516fd39f86 100644 --- a/tests/auto/widgets/widgets/qmenubar/BLACKLIST +++ b/tests/auto/widgets/widgets/qmenubar/BLACKLIST @@ -1,11 +1,5 @@ [check_menuPosition] -ubuntu-16.04 -#QTBUG-66255 -ubuntu-18.04 -ubuntu-20.04 ubuntu-22.04 -[activatedCount] -opensuse-42.3 # QTBUG-87421 [cornerWidgets] android diff --git a/tests/auto/widgets/widgets/qmenubar/CMakeLists.txt b/tests/auto/widgets/widgets/qmenubar/CMakeLists.txt index d8c136cd5c..f2b1c1bec6 100644 --- a/tests/auto/widgets/widgets/qmenubar/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qmenubar/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qmenubar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmenubar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmenubar SOURCES tst_qmenubar.cpp diff --git a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp index 86169ca1de..06750b099b 100644 --- a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp +++ b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -93,13 +93,6 @@ private slots: #endif void allowActiveAndDisabled(); void taskQTBUG56860_focus(); - void check_endKey(); - void check_homeKey(); - -// void check_mouse1_data(); -// void check_mouse1(); -// void check_mouse2_data(); -// void check_mouse2(); void check_altPress(); void check_altClosePress(); @@ -131,6 +124,9 @@ private slots: void platformMenu(); void addActionQt5connect(); void QTBUG_65488_hiddenActionTriggered(); + void pressDragRelease_data(); + void pressDragRelease(); + protected slots: void onSimpleActivated( QAction*); void onComplexActionTriggered(); @@ -148,6 +144,7 @@ private: int m_simpleActivatedCount; int m_complexTriggerCount[int('k')]; QMenuBar* taskQTBUG53205MenuBar; + QScopedPointer<QPointingDevice> m_touchScreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); }; // Testing get/set functions @@ -336,7 +333,6 @@ void tst_QMenuBar::accel() QMainWindow w; const TestMenu menu = initWindowWithSimpleMenuBar(w); w.show(); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); // shortcuts won't work unless the window is active QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_A, Qt::ControlModifier ); @@ -358,7 +354,6 @@ void tst_QMenuBar::activatedCount() QFETCH( bool, forceNonNative ); initWindowWithSimpleMenuBar(w, forceNonNative); w.show(); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_A, Qt::ControlModifier ); @@ -558,7 +553,6 @@ void tst_QMenuBar::check_accelKeys() QMainWindow w; initWindowWithComplexMenuBar(w); w.show(); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); // start with a bogus key that shouldn't trigger anything @@ -637,7 +631,6 @@ void tst_QMenuBar::check_cursorKeys1() QMainWindow w; initWindowWithComplexMenuBar(w); w.show(); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); // start with a ALT + 1 that activates the first popupmenu @@ -677,7 +670,6 @@ void tst_QMenuBar::check_cursorKeys2() QMainWindow w; initWindowWithComplexMenuBar(w); w.show(); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); // select popupmenu2 @@ -716,7 +708,6 @@ void tst_QMenuBar::check_cursorKeys3() QMainWindow w; initWindowWithComplexMenuBar(w); w.show(); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); // select Popupmenu 2 @@ -758,7 +749,6 @@ void tst_QMenuBar::taskQTBUG56860_focus() w.setCentralWidget(e); w.show(); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QTRY_COMPARE(QApplication::focusWidget(), e); @@ -785,85 +775,6 @@ void tst_QMenuBar::taskQTBUG56860_focus() } /*! - If a popupmenu is active you can use home to go quickly to the first item in the menu. -*/ -void tst_QMenuBar::check_homeKey() -{ - // I'm temporarily shutting up this testcase. - // Seems like the behaviour i'm expecting isn't ok. - QSKIP("This test has been \"temporarily\" disabled at least since 2009 :)"); - - QEXPECT_FAIL( "0", "Popupmenu should respond to a Home key", Abort ); - - QMainWindow w; - initWindowWithComplexMenuBar(w); - w.show(); - QApplicationPrivate::setActiveWindow(&w); - QVERIFY(QTest::qWaitForWindowActive(&w)); - - // select Popupmenu 2 - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_2, Qt::AltModifier ); - - // Simulate some keys - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Down ); - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Down ); - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Down ); - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Home ); - // and press ENTER - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Enter ); - // Let's see if the correct slot is called... -// QVERIFY2( m_complexActionTriggerCount[int('c')] == 1, "Popupmenu should respond to a Home key" ); - QCOMPARE(m_complexTriggerCount[int('c')], 1); - QCOMPARE(m_complexTriggerCount[3], 0); - QCOMPARE(m_complexTriggerCount[4], 0); - QCOMPARE(m_complexTriggerCount[int('a')], 0); - QCOMPARE(m_complexTriggerCount[int('b')], 0); - QCOMPARE(m_complexTriggerCount[int('d')], 0); - QCOMPARE(m_complexTriggerCount[int('e')], 0); - QCOMPARE(m_complexTriggerCount[int('f')], 0); - QCOMPARE(m_complexTriggerCount[int('g')], 0); - QCOMPARE(m_complexTriggerCount[int('h')], 0); -} - -/*! - If a popupmenu is active you can use end to go quickly to the last item in the menu. -*/ -void tst_QMenuBar::check_endKey() -{ - // I'm temporarily silenting this testcase. - // Seems like the behaviour i'm expecting isn't ok. - QSKIP("This test has been \"temporarily\" disabled at least since 2009 :)"); - - QEXPECT_FAIL( "0", "Popupmenu should respond to an End key", Abort ); - - QMainWindow w; - initWindowWithComplexMenuBar(w); - w.show(); - QApplicationPrivate::setActiveWindow(&w); - QVERIFY(QTest::qWaitForWindowActive(&w)); - - // select Popupmenu 2 - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_2, Qt::AltModifier ); - - // Simulate some keys - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_End ); - // and press ENTER - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Enter ); - // Let's see if the correct slot is called... -// QVERIFY2( m_complexActionTriggerCount[int('h')] == 1, "Popupmenu should respond to an End key" ); - QCOMPARE(m_complexTriggerCount[int('h')], 1);//, "Popupmenu should respond to an End key"); - QCOMPARE(m_complexTriggerCount[3], 0); - QCOMPARE(m_complexTriggerCount[4], 0); - QCOMPARE(m_complexTriggerCount[int('a')], 0); - QCOMPARE(m_complexTriggerCount[int('b')], 0); - QCOMPARE(m_complexTriggerCount[int('c')], 0); - QCOMPARE(m_complexTriggerCount[int('d')], 0); - QCOMPARE(m_complexTriggerCount[int('e')], 0); - QCOMPARE(m_complexTriggerCount[int('f')], 0); - QCOMPARE(m_complexTriggerCount[int('g')], 0); -} - -/*! If a popupmenu is active you can use esc to hide the menu and then the menubar should become active. If Down is pressed next the popup is activated again. @@ -880,7 +791,6 @@ void tst_QMenuBar::check_escKey() const TestMenu menu = initWindowWithComplexMenuBar(w); w.show(); w.setFocus(); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QVERIFY( !menu.menus.at(0)->isActiveWindow() ); @@ -918,112 +828,6 @@ void tst_QMenuBar::check_escKey() #endif -// void tst_QMenuBar::check_mouse1_data() -// { -// QTest::addColumn<QString>("popup_item"); -// QTest::addColumn<int>("itemA_count"); -// QTest::addColumn<int>("itemB_count"); - -// QTest::newRow( "A" ) << QString( "Item A Ctrl+A" ) << 1 << 0; -// QTest::newRow( "B" ) << QString( "Item B Ctrl+B" ) << 0 << 1; -// } - -// /*! -// Check if the correct signals are emitted if we select a popupmenu. -// */ -// void tst_QMenuBar::check_mouse1() -// { -// if (QSystem::curStyle() == "Motif") -// QSKIP("This fails in Motif due to a bug in the testing framework"); -// QFETCH( QString, popup_item ); -// QFETCH( int, itemA_count ); -// QFETCH( int, itemB_count ); - -// // initComplexMenubar(); -// QVERIFY( !pm1->isActiveWindow() ); -// QVERIFY( !pm2->isActiveWindow() ); - -// QTest::qWait(1000); -// QtTestMouse mouse; -// mouse.mouseEvent( QtTestMouse::MouseClick, mb, "Menu &1", Qt::LeftButton ); - -// QVERIFY( pm1->isActiveWindow() ); -// QVERIFY( !pm2->isActiveWindow() ); - -// QTest::qWait(1000); -// mouse.mouseEvent( QtTestMouse::MouseClick, pm1, popup_item, Qt::LeftButton ); - -// QCOMPARE(m_complexActionTriggerCount[3], 0); -// QCOMPARE(m_complexActionTriggerCount[4], 0); -// QCOMPARE(m_complexActionTriggerCount['a'], (uint)itemA_count); // this option should have fired -// QCOMPARE(m_complexActionTriggerCount['b'], (uint)itemB_count); -// QCOMPARE(m_complexActionTriggerCount['c'], 0); -// QCOMPARE(m_complexActionTriggerCount['d'], 0); -// QCOMPARE(m_complexActionTriggerCount['e'], 0); -// QCOMPARE(m_complexActionTriggerCount['f'], 0); -// QCOMPARE(m_complexActionTriggerCount['g'], 0); -// } - -// void tst_QMenuBar::check_mouse2_data() -// { -// QTest::addColumn<QString>("label"); -// QTest::addColumn<int>("itemA_count"); -// QTest::addColumn<int>("itemB_count"); -// QTest::addColumn<int>("itemC_count"); -// QTest::addColumn<int>("itemD_count"); -// QTest::addColumn<int>("itemE_count"); -// QTest::addColumn<int>("itemF_count"); -// QTest::addColumn<int>("itemG_count"); -// QTest::addColumn<int>("itemH_count"); -// QTest::addColumn<int>("menu3_count"); - -// QTest::newRow( "A" ) << QString( "Menu &1/Item A Ctrl+A" ) << 1 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; -// QTest::newRow( "B" ) << QString( "Menu &1/Item B Ctrl+B" ) << 0 << 1 << 0 << 0 << 0 << 0 << 0 << 0 << 0; -// QTest::newRow( "C" ) << QString( "Menu &2/Item C Ctrl+C" ) << 0 << 0 << 1 << 0 << 0 << 0 << 0 << 0 << 0; -// QTest::newRow( "D" ) << QString( "Menu &2/Item D Ctrl+D" ) << 0 << 0 << 0 << 1 << 0 << 0 << 0 << 0 << 0; -// QTest::newRow( "E" ) << QString( "Menu &2/Item E Ctrl+E" ) << 0 << 0 << 0 << 0 << 1 << 0 << 0 << 0 << 0; -// QTest::newRow( "F" ) << QString( "Menu &2/Item F Ctrl+F" ) << 0 << 0 << 0 << 0 << 0 << 1 << 0 << 0 << 0; -// QTest::newRow( "G" ) << QString( "Menu &2/Item G Ctrl+G" ) << 0 << 0 << 0 << 0 << 0 << 0 << 1 << 0 << 0; -// QTest::newRow( "H" ) << QString( "Menu &2/Item H Ctrl+H" ) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 1 << 0; -// QTest::newRow( "menu 3" ) << QString( "M&enu 3" ) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 1; -// } - -// /*! -// Check if the correct signals are emitted if we select a popupmenu. -// This time, we use a little bit more magic from the testframework. -// */ -// void tst_QMenuBar::check_mouse2() -// { -// if (QSystem::curStyle() == "Motif") -// QSKIP("This fails in Motif due to a bug in the testing framework"); -// QFETCH( QString, label ); -// QFETCH( int, itemA_count ); -// QFETCH( int, itemB_count ); -// QFETCH( int, itemC_count ); -// QFETCH( int, itemD_count ); -// QFETCH( int, itemE_count ); -// QFETCH( int, itemF_count ); -// QFETCH( int, itemG_count ); -// QFETCH( int, itemH_count ); -// QFETCH( int, menu3_count ); - -// // initComplexMenubar(); -// QtTestMouse mouse; -// mouse.click( QtTestMouse::Menu, label, Qt::LeftButton ); - -// // check if the correct signals have fired -// QCOMPARE(m_complexActionTriggerCount[3], (uint)menu3_count); -// QCOMPARE(m_complexActionTriggerCount[4], 0); -// QCOMPARE(m_complexActionTriggerCount['a'], (uint)itemA_count); -// QCOMPARE(m_complexActionTriggerCount['b'], (uint)itemB_count); -// QCOMPARE(m_complexActionTriggerCount['c'], (uint)itemC_count); -// QCOMPARE(m_complexActionTriggerCount['d'], (uint)itemD_count); -// QCOMPARE(m_complexActionTriggerCount['e'], (uint)itemE_count); -// QCOMPARE(m_complexActionTriggerCount['f'], (uint)itemF_count); -// QCOMPARE(m_complexActionTriggerCount['g'], (uint)itemG_count); -// QCOMPARE(m_complexActionTriggerCount['h'], (uint)itemH_count); -// } - void tst_QMenuBar::allowActiveAndDisabled() { QMenuBar menuBar; @@ -1078,7 +882,6 @@ void tst_QMenuBar::check_altPress() initWindowWithSimpleMenuBar(w); w.show(); w.setFocus(); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QTest::keyClick( &w, Qt::Key_Alt ); @@ -1108,7 +911,6 @@ void tst_QMenuBar::check_altClosePress() w.show(); w.move(QGuiApplication::primaryScreen()->availableGeometry().center()); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QTest::keyClick(&w, Qt::Key_F, Qt::AltModifier); @@ -1129,7 +931,6 @@ void tst_QMenuBar::check_shortcutPress() const TestMenu menu = initWindowWithComplexMenuBar(w); w.show(); w.setFocus(); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QCOMPARE(m_complexTriggerCount[3], 0); @@ -1186,7 +987,6 @@ void tst_QMenuBar::check_menuPosition() QAction *menu_action = w.menuBar()->addMenu(&menu); centerOnScreen(&w); w.show(); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); //the menu should be below the menubar item @@ -1305,7 +1105,6 @@ void tst_QMenuBar::task256322_highlight() centerOnScreen(&win); win.show(); - QApplicationPrivate::setActiveWindow(&win); QVERIFY(QTest::qWaitForWindowActive(&win)); const QPoint filePos = menuBarActionWindowPos(win.menuBar(), file); @@ -1361,8 +1160,8 @@ void tst_QMenuBar::menubarSizeHint() mb.setStyle(&style); //this is a list of arbitrary strings so that we check the geometry - QStringList list = QStringList() << "trer" << "ezrfgtgvqd" << "sdgzgzerzerzer" << "eerzertz" << "er"; - foreach(QString str, list) + const auto list = QStringList{"trer", "ezrfgtgvqd", "sdgzgzerzerzer", "eerzertz", "er"}; + for (const QString &str : list) mb.addAction(str); const int panelWidth = style.pixelMetric(QStyle::PM_MenuBarPanelWidth); @@ -1373,7 +1172,8 @@ void tst_QMenuBar::menubarSizeHint() centerOnScreen(&mb); mb.show(); QRect result; - foreach(QAction *action, mb.actions()) { + const auto actions = mb.actions(); + for (QAction *action : actions) { const QRect actionRect = mb.actionGeometry(action); if (!result.isNull()) //this is the first item QCOMPARE(actionRect.left() - result.right() - 1, spacing); @@ -1444,7 +1244,6 @@ void tst_QMenuBar::taskQTBUG11823_crashwithInvisibleActions() centerOnScreen(&menubar); menubar.show(); - QApplicationPrivate::setActiveWindow(&menubar); QVERIFY(QTest::qWaitForWindowActive(&menubar)); menubar.setActiveAction(m); QCOMPARE(menubar.activeAction(), m); @@ -1475,7 +1274,6 @@ void tst_QMenuBar::closeOnSecondClickAndOpenOnThirdClick() // QTBUG-32807, menu QMenu *fileMenu = menuBar->addMenu(QStringLiteral("OpenCloseOpen")); fileMenu->addAction(QStringLiteral("Quit")); mainWindow.show(); - QApplicationPrivate::setActiveWindow(&mainWindow); QVERIFY(QTest::qWaitForWindowActive(&mainWindow)); const QPoint center = menuBarActionWindowPos(mainWindow.menuBar(), fileMenu->menuAction()); @@ -1570,7 +1368,6 @@ void tst_QMenuBar::taskQTBUG53205_crashReparentNested() mainWindow.resize(300, 200); centerOnScreen(&mainWindow); const TestMenu testMenus = initWindowWithComplexMenuBar(mainWindow); - QApplicationPrivate::setActiveWindow(&mainWindow); // they can't be windows QWidget hiddenParent(&mainWindow, {}); @@ -1620,7 +1417,6 @@ void tst_QMenuBar::QTBUG_65488_hiddenActionTriggered() // resize to action's size to make Action1 hidden win.resize(actRect.width() - 10, win.size().height()); win.show(); - QApplicationPrivate::setActiveWindow(&win); QVERIFY(QTest::qWaitForWindowExposed(&win)); // click center of the blank area on the menubar where Action1 resided QTest::mouseClick(win.windowHandle(), Qt::LeftButton, Qt::NoModifier, win.menuBar()->geometry().center()); @@ -1628,6 +1424,70 @@ void tst_QMenuBar::QTBUG_65488_hiddenActionTriggered() QCOMPARE(spy.size(), 0); } +void tst_QMenuBar::pressDragRelease_data() +{ + QTest::addColumn<QPointingDevice *>("device"); + + QTest::newRow("mouse") << const_cast<QPointingDevice *>(QPointingDevice::primaryPointingDevice()); + QTest::newRow("touchscreen") << m_touchScreen.get(); +} + +void tst_QMenuBar::pressDragRelease() +{ + QFETCH(QPointingDevice *, device); + + QMainWindow w; + const TestMenu menu = initWindowWithComplexMenuBar(w); + const QMenu *firstMenu = menu.menus.first(); + QAction *hoveredAction = nullptr; + connect(firstMenu, &QMenu::hovered, firstMenu, [&hoveredAction](QAction *hov) { hoveredAction = hov; }); + w.show(); + QVERIFY(QTest::qWaitForWindowActive(&w)); + QWindow *win = w.windowHandle(); + const QPoint p1(50, w.menuBar()->geometry().height() / 2); + switch (device->type()) { + case QInputDevice::DeviceType::Mouse: + case QInputDevice::DeviceType::TouchPad: + QTest::mousePress(win, Qt::LeftButton, {}, p1); + break; + case QInputDevice::DeviceType::TouchScreen: + QTest::touchEvent(win, device).press(0, p1); + break; + default: + break; + } + + QTRY_VERIFY(firstMenu->isVisible()); + const QPoint firstMenuItemPos = firstMenu->geometry().center() - QPoint(0, 2); + const QPoint firstMenuItemPosInWin = w.mapFromGlobal(firstMenuItemPos); + switch (device->type()) { + case QInputDevice::DeviceType::Mouse: + case QInputDevice::DeviceType::TouchPad: + QTest::mouseMove(win, firstMenuItemPosInWin); + break; + case QInputDevice::DeviceType::TouchScreen: + QTest::touchEvent(win, device).move(0, firstMenuItemPosInWin); + break; + default: + break; + } + QVERIFY(hoveredAction); + QCOMPARE(hoveredAction, firstMenu->actionAt(firstMenu->mapFromGlobal(firstMenuItemPos))); + QSignalSpy triggeredSpy(hoveredAction, &QAction::triggered); + switch (device->type()) { + case QInputDevice::DeviceType::Mouse: + case QInputDevice::DeviceType::TouchPad: + QTest::mouseRelease(win, Qt::LeftButton, {}, firstMenuItemPosInWin); + break; + case QInputDevice::DeviceType::TouchScreen: + QTest::touchEvent(win, device).release(0, firstMenuItemPosInWin); + break; + default: + break; + } + QTRY_COMPARE(triggeredSpy.size(), 1); +} + // QTBUG-56526 void tst_QMenuBar::platformMenu() { @@ -1692,7 +1552,6 @@ void tst_QMenuBar::QTBUG_25669_menubarActionDoubleTriggered() QSignalSpy spy(win.menuBar(), &QMenuBar::triggered); win.show(); - QApplicationPrivate::setActiveWindow(&win); QVERIFY(QTest::qWaitForWindowExposed(&win)); QPoint posAct1 = menuBarActionWindowPos(win.menuBar(), act1); @@ -1729,7 +1588,6 @@ void tst_QMenuBar::taskQTBUG46812_doNotLeaveMenubarHighlighted() initWindowWithSimpleMenuBar(mainWindow); mainWindow.show(); - QApplicationPrivate::setActiveWindow(&mainWindow); QVERIFY(QTest::qWaitForWindowActive(&mainWindow)); QVERIFY(!mainWindow.menuBar()->hasFocus()); @@ -1841,7 +1699,6 @@ void tst_QMenuBar::taskQTBUG55966_subMenuRemoved() delete subMenu; window.show(); - QApplicationPrivate::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QTest::qWait(500); } diff --git a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar_mac.mm b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar_mac.mm index fd8086772a..55e983a4d1 100644 --- a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar_mac.mm +++ b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar_mac.mm @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #import <Cocoa/Cocoa.h> diff --git a/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST b/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST index 81fd4ddb72..6b96499889 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST +++ b/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST @@ -3,9 +3,6 @@ ubuntu rhel opensuse-leap -[stackWidgetOpaqueChildIsVisible] -windows-10 msvc-2017 - # QTBUG-87436 [clearAndGrab] android diff --git a/tests/auto/widgets/widgets/qopenglwidget/CMakeLists.txt b/tests/auto/widgets/widgets/qopenglwidget/CMakeLists.txt index 91dcf2d4f4..78cef5300a 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qopenglwidget/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qopenglwidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qopenglwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qopenglwidget LOWDPI SOURCES diff --git a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp index 07cce4cdc3..51f898c953 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp +++ b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtOpenGLWidgets/QOpenGLWidget> #include <QtGui/QOpenGLFunctions> @@ -24,8 +24,7 @@ #include <private/qguiapplication_p.h> #include <qpa/qplatformbackingstore.h> #include <qpa/qplatformintegration.h> -#include <private/qrhi_p.h> -#include <private/qrhigles2_p.h> +#include <rhi/qrhi.h> class tst_QOpenGLWidget : public QObject { @@ -204,7 +203,7 @@ void tst_QOpenGLWidget::deviceLoss() w->resize(640, 480); w->show(); - auto rhi = w->backingStore()->handle()->rhi(); + auto rhi = w->backingStore()->handle()->rhi(w->windowHandle()); QNativeInterface::QEGLContext *rhiContext = nullptr; if (rhi->backend() == QRhi::OpenGLES2) { auto rhiHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles()); @@ -327,10 +326,6 @@ void tst_QOpenGLWidget::reparentToNotYetCreated() void tst_QOpenGLWidget::reparentHidden() { -#ifdef Q_OS_ANDROID - if (QNativeInterface::QAndroidApplication::sdkVersion() >= 31) - QSKIP("Fails on Android 12 (QTBUG-111235)"); -#endif // Tests QTBUG-60896 QWidget topLevel1; @@ -574,6 +569,21 @@ void tst_QOpenGLWidget::showHide() QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); } +QtMessageHandler oldHandler = nullptr; + +void nativeWindowMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + if (oldHandler) + oldHandler(type, context, msg); + + if (type == QtWarningMsg + && (msg.contains("QOpenGLContext::makeCurrent() called with non-opengl surface") + || msg.contains("Failed to make context current"))) + { + QFAIL("Unexpected warning got printed"); + } +} + void tst_QOpenGLWidget::nativeWindow() { #ifdef Q_OS_ANDROID @@ -585,6 +595,10 @@ void tst_QOpenGLWidget::nativeWindow() // presented correctly as we can only do verification with // grabFramebuffer() here which only exercises a part of the pipeline. + // Install a message handler that looks for some typical warnings from + // QRhi/QOpenGLConext that occur when the RHI-related logic in widgets goes wrong. + oldHandler = qInstallMessageHandler(nativeWindowMessageHandler); + { QScopedPointer<ClearWidget> w(new ClearWidget(nullptr, 800, 600)); w->resize(800, 600); @@ -600,7 +614,33 @@ void tst_QOpenGLWidget::nativeWindow() QVERIFY(w->internalWinId()); } - // Now as a native child + // QTBUG-113557: a plain _raster_ QWidget that is a _native_ child in a toplevel + // combined with a RHI-based (non-native) widget (QOpenGLWidget in this case) + // in the same toplevel. + { + QWidget topLevel; + topLevel.resize(800, 600); + + ClearWidget *child = new ClearWidget(&topLevel, 800, 600); + child->setClearColor(1, 0, 0); + child->resize(400, 400); + child->move(23, 34); + + QWidget *raster = new QWidget(&topLevel); + raster->setGeometry(23, 240, 120, 120); + raster->setStyleSheet("QWidget { background-color: yellow; }"); + + raster->winId(); + + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + // Do not bother checking the output, i.e. if the yellow raster native child + // shows up as it should, but rather rely on the message handler catching the + // qWarnings if they occur. + } + + // Now with the QOpenGLWidget being a native child { QWidget topLevel; topLevel.resize(800, 600); @@ -666,7 +706,7 @@ void tst_QOpenGLWidget::nativeWindow() ClearWidget *child = new ClearWidget(nullptr, 800, 600); // set the parent separately, this is important, see next test case child->setParent(container); - child->setClearColor(0, 1, 0); + child->setClearColor(0, 0, 1); child->resize(400, 400); child->move(23, 34); @@ -680,7 +720,7 @@ void tst_QOpenGLWidget::nativeWindow() QImage image = child->grabFramebuffer(); QCOMPARE(image.width(), child->width()); QCOMPARE(image.height(), child->height()); - QVERIFY(image.pixel(30, 40) == qRgb(0, 255, 0)); + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); } // Again as a child of a native child, but this time specifying the parent @@ -692,7 +732,7 @@ void tst_QOpenGLWidget::nativeWindow() container->winId(); // parent it right away ClearWidget *child = new ClearWidget(container, 800, 600); - child->setClearColor(0, 1, 0); + child->setClearColor(0, 0, 1); child->resize(400, 400); child->move(23, 34); topLevel.show(); @@ -703,7 +743,12 @@ void tst_QOpenGLWidget::nativeWindow() QImage image = child->grabFramebuffer(); QCOMPARE(image.width(), child->width()); QCOMPARE(image.height(), child->height()); - QVERIFY(image.pixel(30, 40) == qRgb(0, 255, 0)); + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); + } + + if (oldHandler) { + qInstallMessageHandler(oldHandler); + oldHandler = nullptr; } } diff --git a/tests/auto/widgets/widgets/qplaintextedit/CMakeLists.txt b/tests/auto/widgets/widgets/qplaintextedit/CMakeLists.txt index 69746c24d1..b7528498cb 100644 --- a/tests/auto/widgets/widgets/qplaintextedit/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qplaintextedit/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qplaintextedit Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qplaintextedit LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qplaintextedit SOURCES tst_qplaintextedit.cpp diff --git a/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp b/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp index ed68c735d4..ca7cc6d4b4 100644 --- a/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp +++ b/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -133,6 +133,7 @@ private slots: void placeholderVisibility_data(); void placeholderVisibility(); void scrollBarSignals(); + void dontCrashWithCss(); private: void createSelection(); @@ -995,7 +996,7 @@ void tst_QPlainTextEdit::copyAvailable_data() //Tests the copyAvailable slot for several cases void tst_QPlainTextEdit::copyAvailable() { - QFETCH(pairListType,keystrokes); + QFETCH(const pairListType, keystrokes); QFETCH(QList<bool>, copyAvailable); QFETCH(QString, function); @@ -1008,9 +1009,8 @@ void tst_QPlainTextEdit::copyAvailable() QSignalSpy spyCopyAvailabe(ed, SIGNAL(copyAvailable(bool))); //Execute Keystrokes - foreach(keyPairType keyPair, keystrokes) { + for (keyPairType keyPair : keystrokes) QTest::keyClick(ed, keyPair.first, keyPair.second ); - } //Execute ed->"function" if (function == "cut") @@ -1830,7 +1830,7 @@ void tst_QPlainTextEdit::placeholderVisibility_data() QTest::addColumn<QList<SetupCommand>>("setupCommands"); QTest::addColumn<bool>("placeholderVisible"); QTest::addRow("no placeholder set + no text set") - << QList<SetupCommand>{} << true; + << QList<SetupCommand>{} << false; QTest::addRow("no placeholder set + text set or text set + no placeholder set") << QList<SetupCommand>{ SetContent } << false; QTest::addRow("no placeholder set + text set + empty text set") @@ -1840,7 +1840,7 @@ void tst_QPlainTextEdit::placeholderVisibility_data() << QList<SetupCommand>{ ClearContent, SetContent } << false; QTest::addRow("empty placeholder set + no text set") - << QList<SetupCommand>{ ClearPlaceHolder } << true; + << QList<SetupCommand>{ ClearPlaceHolder } << false; QTest::addRow("empty placeholder set + text set") << QList<SetupCommand>{ ClearPlaceHolder, SetContent } << false; @@ -1917,7 +1917,7 @@ void tst_QPlainTextEdit::placeholderVisibility() plainTextEdit.show(); QVERIFY(QTest::qWaitForWindowExposed(&plainTextEdit)); - QTRY_VERIFY(plainTextEdit_d->placeholderVisible == placeholderVisible); + QTRY_COMPARE(plainTextEdit_d->placeholderTextShown, placeholderVisible); } @@ -1945,5 +1945,14 @@ void tst_QPlainTextEdit::scrollBarSignals() QTRY_COMPARE(spy.count(), 5); } +void tst_QPlainTextEdit::dontCrashWithCss() +{ + qApp->setStyleSheet("QWidget { font: 10pt; }"); + QPlainTextEdit edit; + edit.show(); + qApp->setStyleSheet(QString()); +} + + QTEST_MAIN(tst_QPlainTextEdit) #include "tst_qplaintextedit.moc" diff --git a/tests/auto/widgets/widgets/qprogressbar/CMakeLists.txt b/tests/auto/widgets/widgets/qprogressbar/CMakeLists.txt index 04891c0e52..61ce6c2692 100644 --- a/tests/auto/widgets/widgets/qprogressbar/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qprogressbar/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qprogressbar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qprogressbar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qprogressbar SOURCES tst_qprogressbar.cpp diff --git a/tests/auto/widgets/widgets/qprogressbar/tst_qprogressbar.cpp b/tests/auto/widgets/widgets/qprogressbar/tst_qprogressbar.cpp index ae6013e764..4a90ed6667 100644 --- a/tests/auto/widgets/widgets/qprogressbar/tst_qprogressbar.cpp +++ b/tests/auto/widgets/widgets/qprogressbar/tst_qprogressbar.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qpushbutton/CMakeLists.txt b/tests/auto/widgets/widgets/qpushbutton/CMakeLists.txt index e6e84822e0..afd052fa17 100644 --- a/tests/auto/widgets/widgets/qpushbutton/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qpushbutton/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qpushbutton Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qpushbutton LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qpushbutton SOURCES tst_qpushbutton.cpp diff --git a/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp b/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp index 8acbfeb6cf..73360ae21d 100644 --- a/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp +++ b/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -15,6 +15,7 @@ #include <QGridLayout> #include <QStyleFactory> #include <QTabWidget> +#include <QStyleOption> #include <private/qguiapplication_p.h> #include <qpa/qplatformtheme.h> @@ -54,6 +55,7 @@ private slots: void hitButton(); void iconOnlyStyleSheet(); void mousePressAndMove(); + void reactToMenuClosed(); protected slots: void resetCounters(); @@ -337,7 +339,6 @@ void tst_QPushButton::setAccel() // The shortcut will not be activated unless the button is in a active // window and has focus - QApplicationPrivate::setActiveWindow(testWidget); testWidget->setFocus(); QVERIFY(QTest::qWaitForWindowActive(testWidget)); QTest::keyClick(testWidget, 'A', Qt::AltModifier); @@ -760,5 +761,51 @@ void tst_QPushButton::mousePressAndMove() QCOMPARE(releaseSpy.size(), 1); } +/* + Test checking that a QPushButton with a QMenu has a sunken style only + when the menu is open + QTBUG-120976 +*/ +void tst_QPushButton::reactToMenuClosed() +{ + // create a subclass of QPushButton to expose the initStyleOption method + class PushButton : public QPushButton { + public: + virtual void initStyleOption(QStyleOptionButton *option) const override + { + QPushButton::initStyleOption(option); + } + }; + + PushButton button; + QStyleOptionButton opt; + QMenu menu; + + // add a menu to the button + menu.addAction(tr("string")); + button.setMenu(&menu); + + // give the button a size and show it + button.setGeometry(0, 0, 50, 50); + button.show(); + QVERIFY(QTest::qWaitForWindowExposed(&button)); + + // click the button to open the menu + QTest::mouseClick(&button, Qt::LeftButton); + + // check the menu is visible and the button style is sunken + QTRY_VERIFY(menu.isVisible()); + button.initStyleOption(&opt); + QVERIFY(opt.state.testFlag(QStyle::StateFlag::State_Sunken)); + + // close the menu + menu.close(); + + // check the menu isn't visible and the style isn't sunken + QTRY_VERIFY(!menu.isVisible()); + button.initStyleOption(&opt); + QVERIFY(!opt.state.testFlag(QStyle::StateFlag::State_Sunken)); +} + QTEST_MAIN(tst_QPushButton) #include "tst_qpushbutton.moc" diff --git a/tests/auto/widgets/widgets/qradiobutton/CMakeLists.txt b/tests/auto/widgets/widgets/qradiobutton/CMakeLists.txt index 28af7e23a1..ce016975c8 100644 --- a/tests/auto/widgets/widgets/qradiobutton/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qradiobutton/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qradiobutton Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qradiobutton LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qradiobutton SOURCES tst_qradiobutton.cpp diff --git a/tests/auto/widgets/widgets/qradiobutton/tst_qradiobutton.cpp b/tests/auto/widgets/widgets/qradiobutton/tst_qradiobutton.cpp index 2fcf2e5545..144d91e9f2 100644 --- a/tests/auto/widgets/widgets/qradiobutton/tst_qradiobutton.cpp +++ b/tests/auto/widgets/widgets/qradiobutton/tst_qradiobutton.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qrhiwidget/CMakeLists.txt b/tests/auto/widgets/widgets/qrhiwidget/CMakeLists.txt new file mode 100644 index 0000000000..f8d18bcf53 --- /dev/null +++ b/tests/auto/widgets/widgets/qrhiwidget/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qrhiwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +file(GLOB_RECURSE qrhiwidget_resource_files + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + data/* +) + +qt_internal_add_test(tst_qrhiwidget + SOURCES + tst_qrhiwidget.cpp + LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + Qt::Widgets + TESTDATA ${qrhiwidget_resource_files} + BUILTIN_TESTDATA +) diff --git a/tests/auto/widgets/widgets/qrhiwidget/data/simple.frag b/tests/auto/widgets/widgets/qrhiwidget/data/simple.frag new file mode 100644 index 0000000000..2aa500e09a --- /dev/null +++ b/tests/auto/widgets/widgets/qrhiwidget/data/simple.frag @@ -0,0 +1,8 @@ +#version 440 + +layout(location = 0) out vec4 fragColor; + +void main() +{ + fragColor = vec4(1.0, 0.0, 0.0, 1.0); +} diff --git a/tests/auto/widgets/widgets/qrhiwidget/data/simple.frag.qsb b/tests/auto/widgets/widgets/qrhiwidget/data/simple.frag.qsb Binary files differnew file mode 100644 index 0000000000..40d0a296ac --- /dev/null +++ b/tests/auto/widgets/widgets/qrhiwidget/data/simple.frag.qsb diff --git a/tests/auto/widgets/widgets/qrhiwidget/data/simple.vert b/tests/auto/widgets/widgets/qrhiwidget/data/simple.vert new file mode 100644 index 0000000000..6b954cdaec --- /dev/null +++ b/tests/auto/widgets/widgets/qrhiwidget/data/simple.vert @@ -0,0 +1,8 @@ +#version 440 + +layout(location = 0) in vec4 position; + +void main() +{ + gl_Position = position; +} diff --git a/tests/auto/widgets/widgets/qrhiwidget/data/simple.vert.qsb b/tests/auto/widgets/widgets/qrhiwidget/data/simple.vert.qsb Binary files differnew file mode 100644 index 0000000000..5b7fd39668 --- /dev/null +++ b/tests/auto/widgets/widgets/qrhiwidget/data/simple.vert.qsb diff --git a/tests/auto/widgets/widgets/qrhiwidget/tst_qrhiwidget.cpp b/tests/auto/widgets/widgets/qrhiwidget/tst_qrhiwidget.cpp new file mode 100644 index 0000000000..7a102180e7 --- /dev/null +++ b/tests/auto/widgets/widgets/qrhiwidget/tst_qrhiwidget.cpp @@ -0,0 +1,834 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtWidgets/QRhiWidget> +#include <QtGui/QPainter> +#include <QTest> +#include <QSignalSpy> +#include <private/qguiapplication_p.h> +#include <qpa/qplatformintegration.h> +#include <rhi/qrhi.h> + +#include <QApplication> +#include <QFile> +#include <QVBoxLayout> +#include <QScrollArea> + +#if QT_CONFIG(vulkan) +#include <private/qvulkandefaultinstance_p.h> +#endif + +class tst_QRhiWidget : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void create_data(); + void create(); + void noCreate(); + void simple_data(); + void simple(); + void msaa_data(); + void msaa(); + void fixedSize_data(); + void fixedSize(); + void autoRt_data(); + void autoRt(); + void reparent_data(); + void reparent(); + void grabFramebufferWhileStillInvisible_data(); + void grabFramebufferWhileStillInvisible(); + void grabViaQWidgetGrab_data(); + void grabViaQWidgetGrab(); + void mirror_data(); + void mirror(); + +private: + void testData(); +}; + +void tst_QRhiWidget::initTestCase() +{ + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RhiBasedRendering)) + QSKIP("RhiBasedRendering capability is reported as unsupported on this platform."); + + qputenv("QT_RHI_LEAK_CHECK", "1"); +} + +void tst_QRhiWidget::testData() +{ + QTest::addColumn<QRhiWidget::Api>("api"); + +#ifndef Q_OS_WEBOS + QTest::newRow("Null") << QRhiWidget::Api::Null; +#endif + +#if QT_CONFIG(opengl) + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) + QTest::newRow("OpenGL") << QRhiWidget::Api::OpenGL; +#endif + +#if QT_CONFIG(vulkan) + // Have to probe to be sure Vulkan is actually working (the test cases + // themselves will assume QRhi init succeeds). + if (QVulkanDefaultInstance::instance()) { + QRhiVulkanInitParams vulkanInitParams; + vulkanInitParams.inst = QVulkanDefaultInstance::instance(); + if (QRhi::probe(QRhi::Vulkan, &vulkanInitParams)) + QTest::newRow("Vulkan") << QRhiWidget::Api::Vulkan; + } +#endif + +#if QT_CONFIG(metal) + QRhiMetalInitParams metalInitParams; + if (QRhi::probe(QRhi::Metal, &metalInitParams)) + QTest::newRow("Metal") << QRhiWidget::Api::Metal; +#endif + +#ifdef Q_OS_WIN + QTest::newRow("D3D11") << QRhiWidget::Api::Direct3D11; + // D3D12 needs to be probed too due to being disabled if the SDK headers + // are too old (clang, mingw). + QRhiD3D12InitParams d3d12InitParams; + if (QRhi::probe(QRhi::D3D12, &d3d12InitParams)) + QTest::newRow("D3D12") << QRhiWidget::Api::Direct3D12; +#endif +} + +void tst_QRhiWidget::create_data() +{ + testData(); +} + +void tst_QRhiWidget::create() +{ + QFETCH(QRhiWidget::Api, api); + + { + QRhiWidget w; + w.setApi(api); + w.resize(320, 240); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + } + + { + QWidget topLevel; + topLevel.resize(320, 240); + QRhiWidget *w = new QRhiWidget(&topLevel); + w->setApi(api); + w->resize(100, 100); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + } +} + +void tst_QRhiWidget::noCreate() +{ + // Now try something that is guaranteed to fail. + // E.g. try using Metal on Windows. + // The error signal should be emitted. The frame signal should not. +#ifdef Q_OS_WIN + qDebug("Warnings will be printed below, this is as expected"); + QRhiWidget rhiWidget; + rhiWidget.setApi(QRhiWidget::Api::Metal); + QSignalSpy frameSpy(&rhiWidget, &QRhiWidget::frameSubmitted); + QSignalSpy errorSpy(&rhiWidget, &QRhiWidget::renderFailed); + rhiWidget.resize(320, 240); + rhiWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&rhiWidget)); + QTRY_VERIFY(errorSpy.count() > 0); + QCOMPARE(frameSpy.count(), 0); +#endif +} + +static QShader getShader(const QString &name) +{ + QFile f(name); + return f.open(QIODevice::ReadOnly) ? QShader::fromSerialized(f.readAll()) : QShader(); +} + +static bool submitResourceUpdates(QRhi *rhi, QRhiResourceUpdateBatch *batch) +{ + QRhiCommandBuffer *cb = nullptr; + QRhi::FrameOpResult result = rhi->beginOffscreenFrame(&cb); + if (result != QRhi::FrameOpSuccess) { + qWarning("beginOffscreenFrame returned %d", result); + return false; + } + if (!cb) { + qWarning("No command buffer from beginOffscreenFrame"); + return false; + } + cb->resourceUpdate(batch); + rhi->endOffscreenFrame(); + return true; +} + +inline bool imageRGBAEquals(const QImage &a, const QImage &b, int maxFuzz = 1) +{ + if (a.size() != b.size()) + return false; + + const QImage image0 = a.convertToFormat(QImage::Format_RGBA8888_Premultiplied); + const QImage image1 = b.convertToFormat(QImage::Format_RGBA8888_Premultiplied); + + const int width = image0.width(); + const int height = image0.height(); + for (int y = 0; y < height; ++y) { + const quint32 *p0 = reinterpret_cast<const quint32 *>(image0.constScanLine(y)); + const quint32 *p1 = reinterpret_cast<const quint32 *>(image1.constScanLine(y)); + int x = width - 1; + while (x-- >= 0) { + const QRgb c0(*p0++); + const QRgb c1(*p1++); + const int red = qAbs(qRed(c0) - qRed(c1)); + const int green = qAbs(qGreen(c0) - qGreen(c1)); + const int blue = qAbs(qBlue(c0) - qBlue(c1)); + const int alpha = qAbs(qAlpha(c0) - qAlpha(c1)); + if (red > maxFuzz || green > maxFuzz || blue > maxFuzz || alpha > maxFuzz) + return false; + } + } + + return true; +} + +class SimpleRhiWidget : public QRhiWidget +{ +public: + SimpleRhiWidget(int sampleCount = 1, QWidget *parent = nullptr) + : QRhiWidget(parent), + m_sampleCount(sampleCount) + { } + + ~SimpleRhiWidget() + { + delete m_rt; + delete m_rp; + } + + void initialize(QRhiCommandBuffer *cb) override; + void render(QRhiCommandBuffer *cb) override; + void releaseResources() override; + + int m_sampleCount; + QRhi *m_rhi = nullptr; + std::unique_ptr<QRhiBuffer> m_vbuf; + std::unique_ptr<QRhiBuffer> m_ubuf; + std::unique_ptr<QRhiShaderResourceBindings> m_srb; + std::unique_ptr<QRhiGraphicsPipeline> m_pipeline; + QRhiTextureRenderTarget *m_rt = nullptr; // used when autoRenderTarget is off + QRhiRenderPassDescriptor *m_rp = nullptr; // used when autoRenderTarget is off + + friend class tst_QRhiWidget; +}; + +void SimpleRhiWidget::initialize(QRhiCommandBuffer *cb) +{ + if (m_rhi != rhi()) { + m_pipeline.reset(); + m_rhi = rhi(); + } + + if (!m_pipeline) { + if (!isAutoRenderTargetEnabled()) { + delete m_rt; + delete m_rp; + QRhiTextureRenderTargetDescription rtDesc; + if (colorTexture()) { + rtDesc.setColorAttachments({ colorTexture() }); + } else if (msaaColorBuffer()) { + QRhiColorAttachment att; + att.setRenderBuffer(msaaColorBuffer()); + rtDesc.setColorAttachments({ att }); + } + m_rt = m_rhi->newTextureRenderTarget(rtDesc); + m_rp = m_rt->newCompatibleRenderPassDescriptor(); + m_rt->setRenderPassDescriptor(m_rp); + m_rt->create(); + } + + static float vertexData[] = { + 0, 1, + -1, -1, + 1, -1 + }; + + m_vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData))); + m_vbuf->create(); + + m_ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64)); + m_ubuf->create(); + + m_srb.reset(m_rhi->newShaderResourceBindings()); + m_srb->create(); + + m_pipeline.reset(m_rhi->newGraphicsPipeline()); + m_pipeline->setShaderStages({ + { QRhiShaderStage::Vertex, getShader(QLatin1String(":/data/simple.vert.qsb")) }, + { QRhiShaderStage::Fragment, getShader(QLatin1String(":/data/simple.frag.qsb")) } + }); + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ + { 2 * sizeof(float) } + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float2, 0 } + }); + m_pipeline->setSampleCount(m_sampleCount); + m_pipeline->setVertexInputLayout(inputLayout); + m_pipeline->setShaderResourceBindings(m_srb.get()); + m_pipeline->setRenderPassDescriptor(renderTarget() ? renderTarget()->renderPassDescriptor() : m_rp); + m_pipeline->create(); + + QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch(); + resourceUpdates->uploadStaticBuffer(m_vbuf.get(), vertexData); + cb->resourceUpdate(resourceUpdates); + } +} + +void SimpleRhiWidget::render(QRhiCommandBuffer *cb) +{ + const QSize outputSize = colorTexture() ? colorTexture()->pixelSize() : msaaColorBuffer()->pixelSize(); + if (renderTarget()) { + QCOMPARE(outputSize, renderTarget()->pixelSize()); + if (rhi()->backend() != QRhi::Null && rhi()->supportedSampleCounts().contains(m_sampleCount)) + QCOMPARE(m_sampleCount, renderTarget()->sampleCount()); + } + + const QColor clearColor = QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f); + cb->beginPass(renderTarget() ? renderTarget() : m_rt, clearColor, { 1.0f, 0 }); + cb->setGraphicsPipeline(m_pipeline.get()); + cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height())); + cb->setShaderResources(); + const QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), 0); + cb->setVertexInput(0, 1, &vbufBinding); + cb->draw(3); + cb->endPass(); +} + +void SimpleRhiWidget::releaseResources() +{ + m_pipeline.reset(); + m_srb.reset(); + m_ubuf.reset(); + m_vbuf.reset(); + +} + +void tst_QRhiWidget::simple_data() +{ + testData(); +} + +void tst_QRhiWidget::simple() +{ + QFETCH(QRhiWidget::Api, api); + + SimpleRhiWidget *rhiWidget = new SimpleRhiWidget; + rhiWidget->setApi(api); + QSignalSpy frameSpy(rhiWidget, &QRhiWidget::frameSubmitted); + QSignalSpy errorSpy(rhiWidget, &QRhiWidget::renderFailed); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(rhiWidget); + + QWidget w; + w.setLayout(layout); + w.resize(1280, 720); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + QCOMPARE(rhiWidget->sampleCount(), 1); + QCOMPARE(rhiWidget->colorBufferFormat(), QRhiWidget::TextureFormat::RGBA8); + QVERIFY(rhiWidget->isAutoRenderTargetEnabled()); + + // Pull out the QRhiTexture (we know colorTexture() and rhi() and friends + // are all there even outside initialize() and render(), even though this + // is not quite documented), and read it back. + QRhiTexture *backingTexture = rhiWidget->colorTexture(); + QVERIFY(backingTexture); + QCOMPARE(backingTexture->format(), QRhiTexture::RGBA8); + QVERIFY(rhiWidget->depthStencilBuffer()); + QVERIFY(rhiWidget->renderTarget()); + QVERIFY(!rhiWidget->resolveTexture()); + QRhi *rhi = rhiWidget->rhi(); + QVERIFY(rhi); + + switch (api) { + case QRhiWidget::Api::OpenGL: + QCOMPARE(rhi->backend(), QRhi::OpenGLES2); + break; + case QRhiWidget::Api::Metal: + QCOMPARE(rhi->backend(), QRhi::Metal); + break; + case QRhiWidget::Api::Vulkan: + QCOMPARE(rhi->backend(), QRhi::Vulkan); + break; + case QRhiWidget::Api::Direct3D11: + QCOMPARE(rhi->backend(), QRhi::D3D11); + break; + case QRhiWidget::Api::Direct3D12: + QCOMPARE(rhi->backend(), QRhi::D3D12); + break; + case QRhiWidget::Api::Null: + QCOMPARE(rhi->backend(), QRhi::Null); + break; + default: + break; + } + + const int maxFuzz = 1; + QImage resultOne; + if (rhi->backend() != QRhi::Null) { + QRhiReadbackResult readResult; + bool readCompleted = false; + readResult.completed = [&readCompleted] { readCompleted = true; }; + QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch(); + rub->readBackTexture(backingTexture, &readResult); + QVERIFY(submitResourceUpdates(rhi, rub)); + QVERIFY(readCompleted); + + QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()), + readResult.pixelSize.width(), readResult.pixelSize.height(), + QImage::Format_RGBA8888); + if (rhi->isYUpInFramebuffer()) + resultOne = wrapperImage.mirrored(); + else + resultOne = wrapperImage.copy(); + + // result is now a red triangle upon greenish background, where the + // triangle's edges are (0, 1), (-1, -1), and (1, -1). + // It's upside down with Vulkan (Y is not corrected, clipSpaceCorrMatrix() is not used), + // but that won't matter for the test. + + // Check that the center is a red pixel. + QRgb c = resultOne.pixel(resultOne.width() / 2, resultOne.height() / 2); + QVERIFY(qRed(c) >= 255 - maxFuzz); + QVERIFY(qGreen(c) <= maxFuzz); + QVERIFY(qBlue(c) <= maxFuzz); + } + + // Now through grabFramebuffer(). + QImage resultTwo; + if (rhi->backend() != QRhi::Null) { + resultTwo = rhiWidget->grabFramebuffer(); + QCOMPARE(errorSpy.count(), 0); + QVERIFY(!resultTwo.isNull()); + QRgb c = resultTwo.pixel(resultTwo.width() / 2, resultTwo.height() / 2); + QVERIFY(qRed(c) >= 255 - maxFuzz); + QVERIFY(qGreen(c) <= maxFuzz); + QVERIFY(qBlue(c) <= maxFuzz); + } + + // Check we got the same result from our manual readback and when the + // texture was rendered to again and grabFramebuffer() was called. + QVERIFY(imageRGBAEquals(resultOne, resultTwo, maxFuzz)); +} + +void tst_QRhiWidget::msaa_data() +{ + testData(); +} + +void tst_QRhiWidget::msaa() +{ + QFETCH(QRhiWidget::Api, api); + + const int SAMPLE_COUNT = 4; + SimpleRhiWidget *rhiWidget = new SimpleRhiWidget(SAMPLE_COUNT); + rhiWidget->setApi(api); + rhiWidget->setSampleCount(SAMPLE_COUNT); + QSignalSpy frameSpy(rhiWidget, &QRhiWidget::frameSubmitted); + QSignalSpy errorSpy(rhiWidget, &QRhiWidget::renderFailed); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(rhiWidget); + + QWidget w; + w.setLayout(layout); + w.resize(1280, 720); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + QCOMPARE(rhiWidget->sampleCount(), 4); + QCOMPARE(rhiWidget->colorBufferFormat(), QRhiWidget::TextureFormat::RGBA8); + QVERIFY(!rhiWidget->colorTexture()); + QVERIFY(rhiWidget->msaaColorBuffer()); + QVERIFY(rhiWidget->depthStencilBuffer()); + QVERIFY(rhiWidget->renderTarget()); + QVERIFY(rhiWidget->resolveTexture()); + QCOMPARE(rhiWidget->resolveTexture()->format(), QRhiTexture::RGBA8); + QRhi *rhi = rhiWidget->rhi(); + QVERIFY(rhi); + + if (rhi->backend() != QRhi::Null) { + QRhiReadbackResult readResult; + QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch(); + rub->readBackTexture(rhiWidget->resolveTexture(), &readResult); + QVERIFY(submitResourceUpdates(rhi, rub)); + + QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()), + readResult.pixelSize.width(), readResult.pixelSize.height(), + QImage::Format_RGBA8888); + QImage result; + if (rhi->isYUpInFramebuffer()) + result = wrapperImage.mirrored(); + else + result = wrapperImage.copy(); + + // Check that the center is a red pixel. + const int maxFuzz = 1; + QRgb c = result.pixel(result.width() / 2, result.height() / 2); + QVERIFY(qRed(c) >= 255 - maxFuzz); + QVERIFY(qGreen(c) <= maxFuzz); + QVERIFY(qBlue(c) <= maxFuzz); + } + + // See if switching back and forth works. + frameSpy.clear(); + rhiWidget->m_pipeline.reset(); + rhiWidget->m_sampleCount = 1; + rhiWidget->setSampleCount(1); + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + QVERIFY(rhiWidget->colorTexture()); + QVERIFY(!rhiWidget->msaaColorBuffer()); + + frameSpy.clear(); + rhiWidget->m_pipeline.reset(); + rhiWidget->m_sampleCount = SAMPLE_COUNT; + rhiWidget->setSampleCount(SAMPLE_COUNT); + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + QVERIFY(!rhiWidget->colorTexture()); + QVERIFY(rhiWidget->msaaColorBuffer()); +} + +void tst_QRhiWidget::fixedSize_data() +{ + testData(); +} + +void tst_QRhiWidget::fixedSize() +{ + QFETCH(QRhiWidget::Api, api); + + SimpleRhiWidget *rhiWidget = new SimpleRhiWidget; + rhiWidget->setApi(api); + QSignalSpy frameSpy(rhiWidget, &QRhiWidget::frameSubmitted); + QSignalSpy errorSpy(rhiWidget, &QRhiWidget::renderFailed); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(rhiWidget); + + rhiWidget->setFixedColorBufferSize(QSize(320, 200)); + + QWidget w; + w.setLayout(layout); + w.resize(1280, 720); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + QVERIFY(rhiWidget->rhi()); + QVERIFY(rhiWidget->colorTexture()); + QCOMPARE(rhiWidget->colorTexture()->pixelSize(), QSize(320, 200)); + QVERIFY(rhiWidget->depthStencilBuffer()); + QCOMPARE(rhiWidget->depthStencilBuffer()->pixelSize(), QSize(320, 200)); + QVERIFY(rhiWidget->renderTarget()); + QVERIFY(!rhiWidget->resolveTexture()); + + frameSpy.clear(); + rhiWidget->setFixedColorBufferSize(640, 480); // should also trigger update() + QTRY_VERIFY(frameSpy.count() > 0); + + QVERIFY(rhiWidget->colorTexture()); + QCOMPARE(rhiWidget->colorTexture()->pixelSize(), QSize(640, 480)); + QVERIFY(rhiWidget->depthStencilBuffer()); + QCOMPARE(rhiWidget->depthStencilBuffer()->pixelSize(), QSize(640, 480)); + + frameSpy.clear(); + rhiWidget->setFixedColorBufferSize(QSize()); + QTRY_VERIFY(frameSpy.count() > 0); + + QVERIFY(rhiWidget->colorTexture()); + QVERIFY(rhiWidget->colorTexture()->pixelSize() != QSize(640, 480)); + QVERIFY(rhiWidget->depthStencilBuffer()); + QVERIFY(rhiWidget->depthStencilBuffer()->pixelSize() != QSize(640, 480)); +} + +void tst_QRhiWidget::autoRt_data() +{ + testData(); +} + +void tst_QRhiWidget::autoRt() +{ + QFETCH(QRhiWidget::Api, api); + + SimpleRhiWidget *rhiWidget = new SimpleRhiWidget; + rhiWidget->setApi(api); + QVERIFY(rhiWidget->isAutoRenderTargetEnabled()); + rhiWidget->setAutoRenderTarget(false); + QVERIFY(!rhiWidget->isAutoRenderTargetEnabled()); + QSignalSpy frameSpy(rhiWidget, &QRhiWidget::frameSubmitted); + QSignalSpy errorSpy(rhiWidget, &QRhiWidget::renderFailed); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(rhiWidget); + + QWidget w; + w.setLayout(layout); + w.resize(1280, 720); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + QVERIFY(rhiWidget->rhi()); + QVERIFY(rhiWidget->colorTexture()); + QVERIFY(!rhiWidget->depthStencilBuffer()); + QVERIFY(!rhiWidget->renderTarget()); + QVERIFY(!rhiWidget->resolveTexture()); + + QVERIFY(rhiWidget->m_rt); + QVERIFY(rhiWidget->m_rp); + QCOMPARE(rhiWidget->m_rt->description().cbeginColorAttachments()->texture(), rhiWidget->colorTexture()); + + frameSpy.clear(); + // do something that triggers creating a new backing texture + rhiWidget->setFixedColorBufferSize(QSize(320, 200)); + QTRY_VERIFY(frameSpy.count() > 0); + + QVERIFY(rhiWidget->colorTexture()); + QCOMPARE(rhiWidget->m_rt->description().cbeginColorAttachments()->texture(), rhiWidget->colorTexture()); +} + +void tst_QRhiWidget::reparent_data() +{ + testData(); +} + +void tst_QRhiWidget::reparent() +{ + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::MultipleWindows)) + QSKIP("MultipleWindows capability is reported as unsupported, skipping reparenting test."); + + QFETCH(QRhiWidget::Api, api); + + QWidget *windowOne = new QWidget; + windowOne->resize(1280, 720); + + SimpleRhiWidget *rhiWidget = new SimpleRhiWidget(1); + rhiWidget->setApi(api); + rhiWidget->resize(800, 600); + QSignalSpy frameSpy(rhiWidget, &QRhiWidget::frameSubmitted); + QSignalSpy errorSpy(rhiWidget, &QRhiWidget::renderFailed); + + rhiWidget->show(); + QVERIFY(QTest::qWaitForWindowExposed(rhiWidget)); + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + frameSpy.clear(); + rhiWidget->setParent(windowOne); + windowOne->show(); + QVERIFY(QTest::qWaitForWindowExposed(windowOne)); + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + frameSpy.clear(); + QWidget windowTwo; + windowTwo.resize(1280, 720); + + rhiWidget->setParent(&windowTwo); + + // There's nothing saying the old top-level parent is going to be around, + // which is interesting wrt to its QRhi and resources created with that; + // exercise this. + delete windowOne; + + windowTwo.show(); + QVERIFY(QTest::qWaitForWindowExposed(&windowTwo)); + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + // now reparent after show() has already been called + frameSpy.clear(); + QWidget windowThree; + windowThree.resize(1280, 720); + windowThree.show(); + QVERIFY(QTest::qWaitForWindowExposed(&windowThree)); + + rhiWidget->setParent(&windowThree); + // this case needs a show() on rhiWidget + rhiWidget->show(); + + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); +} + +void tst_QRhiWidget::grabFramebufferWhileStillInvisible_data() +{ + testData(); +} + +void tst_QRhiWidget::grabFramebufferWhileStillInvisible() +{ + QFETCH(QRhiWidget::Api, api); + + const int maxFuzz = 1; + + SimpleRhiWidget w; + w.setApi(api); + w.resize(1280, 720); + QSignalSpy errorSpy(&w, &QRhiWidget::renderFailed); + + QImage image = w.grabFramebuffer(); // creates its own QRhi just to render offscreen + QVERIFY(!image.isNull()); + QVERIFY(w.rhi()); + QVERIFY(w.colorTexture()); + QCOMPARE(errorSpy.count(), 0); + if (api != QRhiWidget::Api::Null) { + QRgb c = image.pixel(image.width() / 2, image.height() / 2); + QVERIFY(qRed(c) >= 255 - maxFuzz); + QVERIFY(qGreen(c) <= maxFuzz); + QVERIFY(qBlue(c) <= maxFuzz); + } + + // Make the window visible, this under the hood drops the QRhiWidget's + // own QRhi and attaches to the backingstore's. + QSignalSpy frameSpy(&w, &QRhiWidget::frameSubmitted); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + QTRY_VERIFY(frameSpy.count() > 0); + + QCOMPARE(errorSpy.count(), 0); + + if (api != QRhiWidget::Api::Null) { + QRhiReadbackResult readResult; + QRhiResourceUpdateBatch *rub = w.rhi()->nextResourceUpdateBatch(); + rub->readBackTexture(w.colorTexture(), &readResult); + QVERIFY(submitResourceUpdates(w.rhi(), rub)); + QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()), + readResult.pixelSize.width(), readResult.pixelSize.height(), + QImage::Format_RGBA8888); + if (w.rhi()->isYUpInFramebuffer()) + image = wrapperImage.mirrored(); + else + image = wrapperImage.copy(); + QRgb c = image.pixel(image.width() / 2, image.height() / 2); + QVERIFY(qRed(c) >= 255 - maxFuzz); + QVERIFY(qGreen(c) <= maxFuzz); + QVERIFY(qBlue(c) <= maxFuzz); + } +} + +void tst_QRhiWidget::grabViaQWidgetGrab_data() +{ + testData(); +} + +void tst_QRhiWidget::grabViaQWidgetGrab() +{ + QFETCH(QRhiWidget::Api, api); + + SimpleRhiWidget w; + w.setApi(api); + w.resize(1280, 720); + QSignalSpy frameSpy(&w, &QRhiWidget::frameSubmitted); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + QTRY_VERIFY(frameSpy.count() > 0); + + QImage image = w.grab().toImage(); + + if (w.rhi()->backend() != QRhi::Null) { + // It's upside down with Vulkan (Y is not corrected, clipSpaceCorrMatrix() is not used), + // but that won't matter for the test. + QRgb c = image.pixel(image.width() / 2, image.height() / 2); + const int maxFuzz = 1; + QVERIFY(qRed(c) >= 255 - maxFuzz); + QVERIFY(qGreen(c) <= maxFuzz); + QVERIFY(qBlue(c) <= maxFuzz); + } +} + +void tst_QRhiWidget::mirror_data() +{ + testData(); +} + +void tst_QRhiWidget::mirror() +{ + QFETCH(QRhiWidget::Api, api); + + SimpleRhiWidget *rhiWidget = new SimpleRhiWidget; + rhiWidget->setApi(api); + QVERIFY(!rhiWidget->isMirrorVerticallyEnabled()); + + QSignalSpy frameSpy(rhiWidget, &QRhiWidget::frameSubmitted); + QSignalSpy errorSpy(rhiWidget, &QRhiWidget::renderFailed); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(rhiWidget); + QWidget w; + w.setLayout(layout); + w.resize(1280, 720); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + frameSpy.clear(); + rhiWidget->setMirrorVertically(true); + QVERIFY(rhiWidget->isMirrorVerticallyEnabled()); + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + if (api != QRhiWidget::Api::Null) { + QRhi *rhi = rhiWidget->rhi(); + QRhiReadbackResult readResult; + QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch(); + rub->readBackTexture(rhiWidget->colorTexture(), &readResult); + QVERIFY(submitResourceUpdates(rhi, rub)); + QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()), + readResult.pixelSize.width(), readResult.pixelSize.height(), + QImage::Format_RGBA8888); + QImage image; + if (rhi->isYUpInFramebuffer()) + image = wrapperImage.mirrored(); + else + image = wrapperImage.copy(); + + const int maxFuzz = 1; + QRgb c = image.pixel(50, 5); + if (api != QRhiWidget::Api::Vulkan) { + // this should be the background (greenish), not the red triangle + QVERIFY(qGreen(c) > qRed(c)); + } else { + // remember that Vulkan is upside down due to not correcting for Y down in NDC + // hence this is red + QVERIFY(qRed(c) >= 255 - maxFuzz); + QVERIFY(qGreen(c) <= maxFuzz); + } + QVERIFY(qBlue(c) <= maxFuzz); + } +} + +QTEST_MAIN(tst_QRhiWidget) + +#include "tst_qrhiwidget.moc" diff --git a/tests/auto/widgets/widgets/qscrollarea/CMakeLists.txt b/tests/auto/widgets/widgets/qscrollarea/CMakeLists.txt index d6fdd1f617..5e84614407 100644 --- a/tests/auto/widgets/widgets/qscrollarea/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qscrollarea/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qscrollarea Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qscrollarea LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qscrollarea SOURCES tst_qscrollarea.cpp diff --git a/tests/auto/widgets/widgets/qscrollarea/tst_qscrollarea.cpp b/tests/auto/widgets/widgets/qscrollarea/tst_qscrollarea.cpp index edb59b5a7a..87a623b223 100644 --- a/tests/auto/widgets/widgets/qscrollarea/tst_qscrollarea.cpp +++ b/tests/auto/widgets/widgets/qscrollarea/tst_qscrollarea.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qscrollbar/CMakeLists.txt b/tests/auto/widgets/widgets/qscrollbar/CMakeLists.txt index a0c5416175..23e31327e1 100644 --- a/tests/auto/widgets/widgets/qscrollbar/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qscrollbar/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qscrollbar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qscrollbar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qscrollbar SOURCES tst_qscrollbar.cpp diff --git a/tests/auto/widgets/widgets/qscrollbar/tst_qscrollbar.cpp b/tests/auto/widgets/widgets/qscrollbar/tst_qscrollbar.cpp index 8b53c73361..fc836dec4a 100644 --- a/tests/auto/widgets/widgets/qscrollbar/tst_qscrollbar.cpp +++ b/tests/auto/widgets/widgets/qscrollbar/tst_qscrollbar.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qsizegrip/CMakeLists.txt b/tests/auto/widgets/widgets/qsizegrip/CMakeLists.txt index 2c4f1de384..2de1583233 100644 --- a/tests/auto/widgets/widgets/qsizegrip/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qsizegrip/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qsizegrip Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsizegrip LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsizegrip SOURCES tst_qsizegrip.cpp diff --git a/tests/auto/widgets/widgets/qsizegrip/tst_qsizegrip.cpp b/tests/auto/widgets/widgets/qsizegrip/tst_qsizegrip.cpp index e7a2cd5f42..a543efe44e 100644 --- a/tests/auto/widgets/widgets/qsizegrip/tst_qsizegrip.cpp +++ b/tests/auto/widgets/widgets/qsizegrip/tst_qsizegrip.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qslider/CMakeLists.txt b/tests/auto/widgets/widgets/qslider/CMakeLists.txt index a7a732bfd8..664e9a52af 100644 --- a/tests/auto/widgets/widgets/qslider/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qslider/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qslider Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qslider LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qslider SOURCES tst_qslider.cpp diff --git a/tests/auto/widgets/widgets/qslider/tst_qslider.cpp b/tests/auto/widgets/widgets/qslider/tst_qslider.cpp index c5f0b9a812..a70c8c484e 100644 --- a/tests/auto/widgets/widgets/qslider/tst_qslider.cpp +++ b/tests/auto/widgets/widgets/qslider/tst_qslider.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qspinbox/BLACKLIST b/tests/auto/widgets/widgets/qspinbox/BLACKLIST deleted file mode 100644 index 96a7732165..0000000000 --- a/tests/auto/widgets/widgets/qspinbox/BLACKLIST +++ /dev/null @@ -1,2 +0,0 @@ -[stepModifierPressAndHold] -opensuse-42.3 diff --git a/tests/auto/widgets/widgets/qspinbox/CMakeLists.txt b/tests/auto/widgets/widgets/qspinbox/CMakeLists.txt index c1a47f4ba7..826ce16d64 100644 --- a/tests/auto/widgets/widgets/qspinbox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qspinbox/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qspinbox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qspinbox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qspinbox SOURCES tst_qspinbox.cpp diff --git a/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp b/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp index 928d7f9c4e..27343edcde 100644 --- a/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp +++ b/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qdebug.h> #include <qapplication.h> @@ -927,7 +927,6 @@ void tst_QSpinBox::editingFinished() layout->addWidget(box2); testFocusWidget.show(); - QApplicationPrivate::setActiveWindow(&testFocusWidget); QVERIFY(QTest::qWaitForWindowActive(&testFocusWidget)); box->activateWindow(); box->setFocus(); @@ -1106,7 +1105,6 @@ void tst_QSpinBox::specialValue() spin.setValue(50); topWidget.show(); //make sure we have the focus (even if editingFinished fails) - QApplicationPrivate::setActiveWindow(&topWidget); topWidget.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&topWidget)); spin.setFocus(); @@ -1210,7 +1208,6 @@ void tst_QSpinBox::taskQTBUG_5008_textFromValueAndValidate() spinbox.show(); spinbox.activateWindow(); spinbox.setFocus(); - QApplicationPrivate::setActiveWindow(&spinbox); QVERIFY(QTest::qWaitForWindowActive(&spinbox)); QVERIFY(spinbox.hasFocus()); QTRY_COMPARE(static_cast<QWidget *>(&spinbox), QApplication::activeWindow()); @@ -1830,10 +1827,6 @@ void tst_QSpinBox::stepModifierPressAndHold() spin.setStyle(stepModifierStyle.data()); QSignalSpy spy(&spin, &SpinBox::valueChanged); - // TODO: remove debug output when QTBUG-69492 is fixed - connect(&spin, &SpinBox::valueChanged, [=]() { - qDebug() << QTime::currentTime() << "valueChanged emitted"; - }); spin.show(); QVERIFY(QTest::qWaitForWindowActive(&spin)); diff --git a/tests/auto/widgets/widgets/qsplashscreen/CMakeLists.txt b/tests/auto/widgets/widgets/qsplashscreen/CMakeLists.txt index 80f2de964c..12602328c3 100644 --- a/tests/auto/widgets/widgets/qsplashscreen/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qsplashscreen/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qsplashscreen Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsplashscreen LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsplashscreen SOURCES tst_qsplashscreen.cpp diff --git a/tests/auto/widgets/widgets/qsplashscreen/tst_qsplashscreen.cpp b/tests/auto/widgets/widgets/qsplashscreen/tst_qsplashscreen.cpp index f397777065..f57634152a 100644 --- a/tests/auto/widgets/widgets/qsplashscreen/tst_qsplashscreen.cpp +++ b/tests/auto/widgets/widgets/qsplashscreen/tst_qsplashscreen.cpp @@ -1,9 +1,10 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSplashScreen> +#include <QTimer> class tst_QSplashScreen : public QObject { @@ -11,7 +12,7 @@ class tst_QSplashScreen : public QObject private slots: void checkCloseTime(); - void checkScreenConstructor(); + void checkConstructorAndShow(); }; class CloseEventSplash : public QSplashScreen @@ -20,7 +21,7 @@ public: CloseEventSplash(const QPixmap &pix) : QSplashScreen(pix), receivedCloseEvent(false) {} bool receivedCloseEvent; protected: - void closeEvent(QCloseEvent *event) + void closeEvent(QCloseEvent *event) override { receivedCloseEvent = true; QSplashScreen::closeEvent(event); @@ -35,23 +36,26 @@ void tst_QSplashScreen::checkCloseTime() QVERIFY(!splash.receivedCloseEvent); QWidget w; splash.show(); - QTimer::singleShot(500, &w, SLOT(show())); + QTimer::singleShot(10, &w, &QWidget::show); QVERIFY(!splash.receivedCloseEvent); splash.finish(&w); QVERIFY(splash.receivedCloseEvent); // We check the window handle because if this is not valid, then // it can't have been exposed QVERIFY(w.windowHandle()); - QVERIFY(w.windowHandle()->isExposed()); + QVERIFY(w.windowHandle()->isVisible()); } -void tst_QSplashScreen::checkScreenConstructor() +void tst_QSplashScreen::checkConstructorAndShow() { - for (const auto screen : QGuiApplication::screens()) { - QSplashScreen splash(screen); + QPixmap pix(100, 100); + pix.fill(Qt::red); + for (auto *screen : QGuiApplication::screens()) { + QSplashScreen splash(screen, pix); splash.show(); QCOMPARE(splash.screen(), screen); QVERIFY(splash.windowHandle()); + QVERIFY(splash.windowHandle()->isVisible()); QCOMPARE(splash.windowHandle()->screen(), screen); } } diff --git a/tests/auto/widgets/widgets/qsplitter/CMakeLists.txt b/tests/auto/widgets/widgets/qsplitter/CMakeLists.txt index 3c826c153b..16244c8834 100644 --- a/tests/auto/widgets/widgets/qsplitter/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qsplitter/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qsplitter Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsplitter LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data list(APPEND test_data "extradata.txt") list(APPEND test_data "setSizes3.dat") diff --git a/tests/auto/widgets/widgets/qsplitter/tst_qsplitter.cpp b/tests/auto/widgets/widgets/qsplitter/tst_qsplitter.cpp index ec3df1f03d..071e6d4cbc 100644 --- a/tests/auto/widgets/widgets/qsplitter/tst_qsplitter.cpp +++ b/tests/auto/widgets/widgets/qsplitter/tst_qsplitter.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -1032,7 +1032,7 @@ void tst_QSplitter::taskQTBUG_4101_ensureOneNonCollapsedWidget() QFETCH(bool, testingHide); MyFriendlySplitter s; - QLabel *l; + QLabel *l = nullptr; for (int i = 0; i < 5; ++i) { l = new QLabel(QString("Label ") + QChar('A' + i)); l->setAlignment(Qt::AlignCenter); diff --git a/tests/auto/widgets/widgets/qstackedwidget/CMakeLists.txt b/tests/auto/widgets/widgets/qstackedwidget/CMakeLists.txt index 3d18ecae88..0c79cf29f4 100644 --- a/tests/auto/widgets/widgets/qstackedwidget/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qstackedwidget/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qstackedwidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qstackedwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qstackedwidget SOURCES tst_qstackedwidget.cpp diff --git a/tests/auto/widgets/widgets/qstackedwidget/tst_qstackedwidget.cpp b/tests/auto/widgets/widgets/qstackedwidget/tst_qstackedwidget.cpp index 10563137c3..3c28ad324a 100644 --- a/tests/auto/widgets/widgets/qstackedwidget/tst_qstackedwidget.cpp +++ b/tests/auto/widgets/widgets/qstackedwidget/tst_qstackedwidget.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -159,7 +159,6 @@ void tst_QStackedWidget::dynamicPages() le11->setFocus(); // set focus to second widget in the page sw->resize(200, 200); sw->show(); - QApplicationPrivate::setActiveWindow(sw); QVERIFY(QTest::qWaitForWindowActive(sw)); QTRY_COMPARE(QApplication::focusWidget(), le11); diff --git a/tests/auto/widgets/widgets/qstatusbar/CMakeLists.txt b/tests/auto/widgets/widgets/qstatusbar/CMakeLists.txt index 9794718bd3..3bda170936 100644 --- a/tests/auto/widgets/widgets/qstatusbar/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qstatusbar/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qstatusbar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qstatusbar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qstatusbar SOURCES tst_qstatusbar.cpp diff --git a/tests/auto/widgets/widgets/qstatusbar/tst_qstatusbar.cpp b/tests/auto/widgets/widgets/qstatusbar/tst_qstatusbar.cpp index 15007e8a90..b0a53ba01a 100644 --- a/tests/auto/widgets/widgets/qstatusbar/tst_qstatusbar.cpp +++ b/tests/auto/widgets/widgets/qstatusbar/tst_qstatusbar.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qtabbar/BLACKLIST b/tests/auto/widgets/widgets/qtabbar/BLACKLIST deleted file mode 100644 index 735b044d8b..0000000000 --- a/tests/auto/widgets/widgets/qtabbar/BLACKLIST +++ /dev/null @@ -1,2 +0,0 @@ -[sizeHints] -redhatenterpriselinuxworkstation-6.6 diff --git a/tests/auto/widgets/widgets/qtabbar/CMakeLists.txt b/tests/auto/widgets/widgets/qtabbar/CMakeLists.txt index 4abe0685a5..b79e763819 100644 --- a/tests/auto/widgets/widgets/qtabbar/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qtabbar/CMakeLists.txt @@ -5,10 +5,17 @@ ## tst_qtabbar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtabbar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtabbar SOURCES tst_qtabbar.cpp LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp b/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp index 1d7e999b10..03131cebe4 100644 --- a/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp +++ b/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> @@ -14,6 +14,8 @@ #include <QScreen> #include <QWindow> +#include <QtWidgets/private/qtabbar_p.h> + using namespace Qt::StringLiterals; class TabBar; @@ -48,6 +50,7 @@ private slots: void hideTab_data(); void hideTab(); void hideAllTabs(); + void checkHiddenTab(); void setElideMode_data(); void setElideMode(); @@ -94,6 +97,13 @@ private slots: void hoverTab_data(); void hoverTab(); + void resizeKeepsScroll_data(); + void resizeKeepsScroll(); + void changeTabTextKeepsScroll(); + void settingCurrentTabBeforeShowDoesntScroll(); + void checkPositionsAfterShapeChange(); + void checkScrollOffsetAfterTabRemoval(); + private: void checkPositions(const TabBar &tabbar, const QList<int> &positions); }; @@ -360,6 +370,25 @@ void tst_QTabBar::hideAllTabs() QVERIFY(sizeHint.width() < prevSizeHint.width()); } +void tst_QTabBar::checkHiddenTab() +{ + QTabBar tabbar; + + tabbar.addTab("foo"); + tabbar.addTab("bar"); + tabbar.addTab("baz"); + tabbar.setCurrentIndex(0); + tabbar.setTabVisible(1, false); + + QKeyEvent keyRight(QKeyEvent::KeyPress, Qt::Key_Right, Qt::NoModifier); + QVERIFY(QApplication::sendEvent(&tabbar, &keyRight)); + QCOMPARE(tabbar.currentIndex(), 2); + + QKeyEvent keyLeft(QKeyEvent::KeyPress, Qt::Key_Left, Qt::NoModifier); + QVERIFY(QApplication::sendEvent(&tabbar, &keyLeft)); + QCOMPARE(tabbar.currentIndex(), 0); +} + void tst_QTabBar::setElideMode_data() { QTest::addColumn<int>("tabElideMode"); @@ -1346,5 +1375,226 @@ void tst_QTabBar::hoverTab() QCOMPARE(tabbar.styleOptions[1].state & QStyle::State_MouseOver, QStyle::State_None); } + +void tst_QTabBar::resizeKeepsScroll_data() +{ + QTest::addColumn<QTabBar::Shape>("tabShape"); + QTest::addColumn<bool>("expanding"); + + QTest::addRow("North, expanding") << QTabBar::RoundedNorth << true; + QTest::addRow("East, expanding") << QTabBar::RoundedEast << true; + QTest::addRow("South, expanding") << QTabBar::RoundedSouth << true; + QTest::addRow("West, expanding") << QTabBar::RoundedWest << true; + + QTest::addRow("North, not expanding") << QTabBar::RoundedNorth << false; + QTest::addRow("South, not expanding") << QTabBar::RoundedSouth << false; +} + +void tst_QTabBar::resizeKeepsScroll() +{ + QFETCH(QTabBar::Shape, tabShape); + QFETCH(const bool, expanding); + + QTabBar tabBar; + TabBarScrollingProxyStyle proxyStyle; + tabBar.setStyle(&proxyStyle); + + for (int i = 0; i < 10; ++i) + tabBar.addTab(u"Tab Number %1"_s.arg(i)); + + tabBar.setShape(tabShape); + tabBar.setUsesScrollButtons(true); + tabBar.setExpanding(expanding); + + // resize to half + const QSize fullSize = tabBar.sizeHint(); + const bool horizontal = fullSize.width() > fullSize.height(); + if (horizontal) + tabBar.resize(fullSize.width() / 2, fullSize.height()); + else + tabBar.resize(fullSize.width(), fullSize.height() / 2); + + tabBar.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabBar)); + + const auto getScrollOffset = [&]() -> int { + return static_cast<QTabBarPrivate *>(QObjectPrivate::get(&tabBar))->scrollOffset; + }; + + // select a tab outside, this will scroll + tabBar.setCurrentIndex(6); + // the first tab is now scrolled out + const int scrollOffset = getScrollOffset(); + QCOMPARE_GT(scrollOffset, 0); + // the current index is now fully visible, with margin on both sides + tabBar.setCurrentIndex(5); + + // make the tab bar a bit larger, by the width of a tab + if (horizontal) + tabBar.resize(tabBar.width() + tabBar.tabRect(5).width(), tabBar.height()); + else + tabBar.resize(tabBar.width(), tabBar.height() + tabBar.tabRect(5).height()); + + // this should not change the scroll + QCOMPARE(getScrollOffset(), scrollOffset); + + // make the tab bar large enough to fit everything with extra space + tabBar.resize(fullSize + QSize(50, 50)); + + // there should be no scroll + QCOMPARE(getScrollOffset(), 0); + + for (int i = 0; i < tabBar.count(); ++i) { + tabBar.setCurrentIndex(i); + QCOMPARE(getScrollOffset(), 0); + } +} + +void tst_QTabBar::changeTabTextKeepsScroll() +{ + QTabBar tabBar; + TabBarScrollingProxyStyle proxyStyle; + tabBar.setStyle(&proxyStyle); + + for (int i = 0; i < 6; ++i) + tabBar.addTab(u"Tab Number %1"_s.arg(i)); + + const QSize fullSize = tabBar.sizeHint(); + tabBar.resize(fullSize.width() / 2, fullSize.height()); + + tabBar.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabBar)); + + const auto getScrollOffset = [&]() -> int { + return static_cast<QTabBarPrivate *>(QObjectPrivate::get(&tabBar))->scrollOffset; + }; + + tabBar.setCurrentIndex(3); + const int scrollOffset = getScrollOffset(); + tabBar.setTabText(3, "New title"); + QCOMPARE(getScrollOffset(), scrollOffset); +} + +void tst_QTabBar::settingCurrentTabBeforeShowDoesntScroll() +{ + QTabBar tabBar; + TabBarScrollingProxyStyle proxyStyle; + tabBar.setStyle(&proxyStyle); + + for (int i = 0; i < 6; ++i) + tabBar.addTab(u"Tab Number %1"_s.arg(i)); + + const auto getScrollOffset = [&]() -> int { + return static_cast<QTabBarPrivate *>(QObjectPrivate::get(&tabBar))->scrollOffset; + }; + + tabBar.setCurrentIndex(5); + + // changing the current index while the tab bar isn't visible shouldn't scroll yet + QCOMPARE(getScrollOffset(), 0); + + // now show the tab bar with a size that's too small to fit the current index + const QSize fullSize = tabBar.sizeHint(); + tabBar.resize(fullSize.width() / 2, fullSize.height()); + + tabBar.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabBar)); + + // this should scroll + QCOMPARE_GT(getScrollOffset(), 0); +} + +void tst_QTabBar::checkPositionsAfterShapeChange() +{ + class TabWidget : public QTabWidget + { + public: + using QTabWidget::QTabWidget; + using QTabWidget::setTabBar; + }; + + class TabBar : public QTabBar + { + public: + using QTabBar::initStyleOption; + void resizeEvent(QResizeEvent *e) override + { + QTabBar::resizeEvent(e); + resized = true; + } + bool resized = false; + }; + + TabWidget tabWidget; + auto *tabBar = new TabBar; + tabWidget.setTabBar(tabBar); + for (int i = 0; i < 3; ++i) + tabWidget.addTab(new QWidget, u"Tab %1"_s.arg(i)); + tabWidget.setTabPosition(QTabWidget::North); + tabWidget.setCurrentIndex(2); + tabWidget.resize(300, 300); + tabWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabWidget)); + + tabBar->resized = false; + tabWidget.setTabPosition(QTabWidget::East); + QVERIFY(QTest::qWaitFor([&]() { return tabBar->resized; })); + QStyleOptionTab opt; + tabBar->initStyleOption(&opt, 2); + QVERIFY(opt.rect.top() > 0); +} + +void tst_QTabBar::checkScrollOffsetAfterTabRemoval() +{ + QTabWidget tabWidget; + QTabBar *tabBar = tabWidget.tabBar(); + for (int i = 0; i < 10; ++i) + tabWidget.addTab(new QWidget, u"Tab %1"_s.arg(i)); + tabWidget.setTabPosition(QTabWidget::North); + tabWidget.resize(300, 300); + tabWidget.setCurrentIndex(0); + tabWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabWidget)); + + auto *rightButton = tabBar->findChild<QAbstractButton *>(u"ScrollRightButton"_s); + auto *leftButton = tabBar->findChild<QAbstractButton *>(u"ScrollLeftButton"_s); + QVERIFY(leftButton); + QVERIFY(rightButton); + QVERIFY(rightButton->isEnabled()); + QVERIFY(!leftButton->isEnabled()); + // scroll to the right + tabBar->setCurrentIndex(9); + QVERIFY(!rightButton->isEnabled()); + QVERIFY(leftButton->isEnabled()); + // scroll to the center + tabBar->setCurrentIndex(2); + QVERIFY(rightButton->isEnabled()); + QVERIFY(leftButton->isEnabled()); + + const auto getScrollOffset = [&]() -> int { + return static_cast<QTabBarPrivate *>(QObjectPrivate::get(tabBar))->scrollOffset; + }; + // the scroll offset should not change when a tab right outside + // the scroll rect is removed + auto oldOffset = getScrollOffset(); + tabWidget.removeTab(9); + QCOMPARE(getScrollOffset(), oldOffset); + // the scroll offset must change when a tab left outside + // the scroll rect is removed + oldOffset = getScrollOffset(); + tabWidget.removeTab(0); + QVERIFY(getScrollOffset() < oldOffset); + + // the scroll offset must change when there is empty + // place in the right after tab removal + oldOffset = getScrollOffset(); + QVERIFY(oldOffset > 0); + for (int i : { 7, 6, 5, 4, 3 }) + tabWidget.removeTab(i); + QCOMPARE(getScrollOffset(), 0); + QVERIFY(!rightButton->isVisible()); + QVERIFY(!leftButton->isVisible()); +} + QTEST_MAIN(tst_QTabBar) #include "tst_qtabbar.moc" diff --git a/tests/auto/widgets/widgets/qtabwidget/CMakeLists.txt b/tests/auto/widgets/widgets/qtabwidget/CMakeLists.txt index d6f1fa1541..a8b48e925c 100644 --- a/tests/auto/widgets/widgets/qtabwidget/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qtabwidget/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qtabwidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtabwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtabwidget SOURCES tst_qtabwidget.cpp diff --git a/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp b/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp index 00cb26c2d3..d7bfdfaad2 100644 --- a/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp +++ b/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -60,6 +60,7 @@ private slots: void tabPosition(); void tabEnabled(); void tabHidden(); + void checkHiddenTab(); void tabText(); void tabShape(); void tabTooltip(); @@ -79,6 +80,9 @@ private slots: void moveCurrentTab(); void autoHide(); + void setCurrentBeforeShow_data(); + void setCurrentBeforeShow(); + private: int addPage(); void removePage(int index); @@ -249,6 +253,32 @@ void tst_QTabWidget::tabHidden() } } +void tst_QTabWidget::checkHiddenTab() +{ + tw->addTab(new QWidget(), "foo"); + tw->addTab(new QWidget(), "bar"); + tw->addTab(new QWidget(), "baz"); + QCOMPARE(tw->count(), 3); + tw->setCurrentIndex(0); + tw->setTabVisible(1, false); + + QKeyEvent keyTab(QKeyEvent::KeyPress, Qt::Key_Tab, Qt::ControlModifier); + QVERIFY(QApplication::sendEvent(tw, &keyTab)); + QCOMPARE(tw->currentIndex(), 2); + QVERIFY(QApplication::sendEvent(tw, &keyTab)); + QCOMPARE(tw->currentIndex(), 0); + QVERIFY(QApplication::sendEvent(tw, &keyTab)); + QCOMPARE(tw->currentIndex(), 2); + + QKeyEvent keyBacktab(QKeyEvent::KeyPress, Qt::Key_Backtab, Qt::ControlModifier); + QVERIFY(QApplication::sendEvent(tw, &keyBacktab)); + QCOMPARE(tw->currentIndex(), 0); + QVERIFY(QApplication::sendEvent(tw, &keyBacktab)); + QCOMPARE(tw->currentIndex(), 2); + QVERIFY(QApplication::sendEvent(tw, &keyBacktab)); + QCOMPARE(tw->currentIndex(), 0); +} + void tst_QTabWidget::tabText() { // Test bad arguments @@ -750,5 +780,39 @@ void tst_QTabWidget::autoHide() QVERIFY(heightForWidth1 > tabWidget.heightForWidth(20)); } +void tst_QTabWidget::setCurrentBeforeShow_data() +{ + QTest::addColumn<QTabWidget::TabPosition>("tabPosition"); + QTest::newRow("West") << QTabWidget::West; + QTest::newRow("North") << QTabWidget::North; + QTest::newRow("East") << QTabWidget::East; + QTest::newRow("South") << QTabWidget::South; +} + +void tst_QTabWidget::setCurrentBeforeShow() +{ + QFETCH(QTabWidget::TabPosition, tabPosition); + + QTabWidget tabWidget; + tabWidget.setTabPosition(tabPosition); + + QPixmap pm(50, 50); + pm.fill(Qt::red); + const QIcon icon(pm); + for (int i = 0; i < 4; ++i) + tabWidget.addTab(new QWidget, icon, QString("Tab %1").arg(i)); + + // the tab widget has space for the entire tab bar + tabWidget.resize(tabWidget.tabBar()->sizeHint() + QSize(50, 50)); + tabWidget.setCurrentIndex(2); + tabWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabWidget)); + + QCOMPARE_GE(tabWidget.tabBar()->tabRect(0).x(), 0); + QCOMPARE_GE(tabWidget.tabBar()->tabRect(0).y(), 0); + + QTest::qWait(2000); +} + QTEST_MAIN(tst_QTabWidget) #include "tst_qtabwidget.moc" diff --git a/tests/auto/widgets/widgets/qtextbrowser/CMakeLists.txt b/tests/auto/widgets/widgets/qtextbrowser/CMakeLists.txt index b2d6664783..4a80068d75 100644 --- a/tests/auto/widgets/widgets/qtextbrowser/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qtextbrowser/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qtextbrowser Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtextbrowser LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp b/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp index 8bc991a962..6dfab07fed 100644 --- a/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp +++ b/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -19,7 +19,6 @@ class TestBrowser : public QTextBrowser public: inline TestBrowser() { show(); - QApplicationPrivate::setActiveWindow(this); activateWindow(); setFocus(); QVERIFY(QTest::qWaitForWindowActive(this)); diff --git a/tests/auto/widgets/widgets/qtextedit/CMakeLists.txt b/tests/auto/widgets/widgets/qtextedit/CMakeLists.txt index fdaf6a7046..e406e088ca 100644 --- a/tests/auto/widgets/widgets/qtextedit/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qtextedit/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qtextedit Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtextedit LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data list(APPEND test_data "fullWidthSelection") diff --git a/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp b/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp index 241b0e64f3..ddf2bcfa85 100644 --- a/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp +++ b/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -198,6 +198,8 @@ private slots: void nextFormatAfterEnterPressed_data(); void nextFormatAfterEnterPressed(); + void dontCrashWithCss(); + private: void createSelection(); int blockCount() const; @@ -757,6 +759,11 @@ void tst_QTextEdit::cursorPositionChanged() QCOMPARE(spy2.cursorPositions.size(), 1); QCOMPARE(spy2.cursorPositions.at(0), 0); QCOMPARE(ed->textCursor().position(), 0); + + ed->selectAll(); + QCOMPARE(spy2.cursorPositions.size(), 2); + QCOMPARE(spy2.cursorPositions.at(1), 11); + QCOMPARE(ed->textCursor().position(), 11); } void tst_QTextEdit::setTextCursor() @@ -1347,7 +1354,7 @@ void tst_QTextEdit::copyAvailable_data() //Tests the copyAvailable slot for several cases void tst_QTextEdit::copyAvailable() { - QFETCH(pairListType,keystrokes); + QFETCH(const pairListType, keystrokes); QFETCH(QList<bool>, copyAvailable); QFETCH(QString, function); @@ -1360,9 +1367,8 @@ void tst_QTextEdit::copyAvailable() QSignalSpy spyCopyAvailabe(ed, SIGNAL(copyAvailable(bool))); //Execute Keystrokes - foreach(keyPairType keyPair, keystrokes) { + for (keyPairType keyPair : keystrokes) QTest::keyClick(ed, keyPair.first, keyPair.second ); - } //Execute ed->"function" if (function == "cut") @@ -2539,7 +2545,6 @@ void tst_QTextEdit::inputMethodEvent() // test that input method gets chance to commit preedit when removing focus ed->setText(""); - QApplicationPrivate::setActiveWindow(ed); QTRY_VERIFY(QApplication::focusWindow()); QCOMPARE(qApp->focusObject(), ed); @@ -3060,5 +3065,14 @@ void tst_QTextEdit::nextFormatAfterEnterPressed() QCOMPARE(prevBlockCursor.charFormat().property(it.key()), it.value()); } +void tst_QTextEdit::dontCrashWithCss() +{ + qApp->setStyleSheet("QWidget { font: 10pt; }"); + QTextEdit edit; + edit.show(); + qApp->setStyleSheet(QString()); +} + + QTEST_MAIN(tst_QTextEdit) #include "tst_qtextedit.moc" diff --git a/tests/auto/widgets/widgets/qtoolbar/CMakeLists.txt b/tests/auto/widgets/widgets/qtoolbar/CMakeLists.txt index f2543fb75f..e9fb01c3e8 100644 --- a/tests/auto/widgets/widgets/qtoolbar/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qtoolbar/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qtoolbar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtoolbar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtoolbar SOURCES tst_qtoolbar.cpp diff --git a/tests/auto/widgets/widgets/qtoolbar/tst_qtoolbar.cpp b/tests/auto/widgets/widgets/qtoolbar/tst_qtoolbar.cpp index 670839feb6..8b8c74b1e7 100644 --- a/tests/auto/widgets/widgets/qtoolbar/tst_qtoolbar.cpp +++ b/tests/auto/widgets/widgets/qtoolbar/tst_qtoolbar.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -1022,7 +1022,6 @@ void tst_QToolBar::accel() QSignalSpy spy(action, SIGNAL(triggered(bool))); mw.show(); - QApplicationPrivate::setActiveWindow(&mw); QVERIFY(QTest::qWaitForWindowActive(&mw)); QTest::keyClick(&mw, Qt::Key_T, Qt::AltModifier); diff --git a/tests/auto/widgets/widgets/qtoolbox/CMakeLists.txt b/tests/auto/widgets/widgets/qtoolbox/CMakeLists.txt index 2a18840839..362fba25a4 100644 --- a/tests/auto/widgets/widgets/qtoolbox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qtoolbox/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qtoolbox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtoolbox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtoolbox SOURCES tst_qtoolbox.cpp diff --git a/tests/auto/widgets/widgets/qtoolbox/tst_qtoolbox.cpp b/tests/auto/widgets/widgets/qtoolbox/tst_qtoolbox.cpp index 5fe06707be..fb14ceb79c 100644 --- a/tests/auto/widgets/widgets/qtoolbox/tst_qtoolbox.cpp +++ b/tests/auto/widgets/widgets/qtoolbox/tst_qtoolbox.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qtoolbutton/CMakeLists.txt b/tests/auto/widgets/widgets/qtoolbutton/CMakeLists.txt index 7d4c36ddb2..7c8b41b5e6 100644 --- a/tests/auto/widgets/widgets/qtoolbutton/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qtoolbutton/CMakeLists.txt @@ -5,6 +5,12 @@ ## tst_qtoolbutton Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtoolbutton LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtoolbutton SOURCES tst_qtoolbutton.cpp diff --git a/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp b/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp index c5ed718edb..b77a9c0eec 100644 --- a/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp +++ b/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -34,6 +34,7 @@ private slots: void qtbug_26956_popupTimerDone(); void qtbug_34759_sizeHintResetWhenSettingMenu(); void defaultActionSynced(); + void deleteInHandler(); protected slots: void sendMouseClick(); @@ -181,7 +182,6 @@ void tst_QToolButton::task176137_autoRepeatOfAction() label->move(0, 50); mainWidget.show(); - QApplicationPrivate::setActiveWindow(&mainWidget); QVERIFY(QTest::qWaitForWindowActive(&mainWidget)); QSignalSpy spy(&action,SIGNAL(triggered())); @@ -316,5 +316,22 @@ void tst_QToolButton::defaultActionSynced() QCOMPARE(bSpy.size(), ++bToggledCount); } +void tst_QToolButton::deleteInHandler() +{ + // Tests that if something deletes the button + // while its event handler is still on the callstack, we don't crash + + QPointer<QToolButton> tb = new QToolButton(); + tb->show(); + QVERIFY(QTest::qWaitForWindowActive(tb)); + + connect(tb, &QToolButton::clicked, this, [tb] { + delete tb; + }); + + QTest::mouseClick(tb, Qt::LeftButton); + QVERIFY(!tb); +} + QTEST_MAIN(tst_QToolButton) #include "tst_qtoolbutton.moc" |