aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/qml/debugger/CMakeLists.txt4
-rw-r--r--tests/auto/qml/debugger/qqmldebugtranslationclient/tst_qqmldebugtranslationclient.cpp3
-rw-r--r--tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp3
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/data/debuggerCrashOnAttach.qml36
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp19
-rw-r--r--tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp2
-rw-r--r--tests/auto/qml/debugger/shared/debugutil.cpp4
-rw-r--r--tests/auto/qml/debugger/shared/debugutil_p.h3
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp2
-rw-r--r--tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp40
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/conversionDecrement.qml18
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/failures.qml9
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/functionTakingVar.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h23
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/javaScriptArgument.qml33
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/listIndices.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/listPropertyAsModel.qml16
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/throwObjectName.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/undefinedResets.qml3
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp92
-rw-r--r--tests/auto/qml/qmlformat/data/forWithLet.formatted.qml8
-rw-r--r--tests/auto/qml/qmlformat/data/forWithLet.qml8
-rw-r--r--tests/auto/qml/qmlformat/tst_qmlformat.cpp6
-rw-r--r--tests/auto/qml/qmltyperegistrar/CMakeLists.txt2
-rw-r--r--tests/auto/qml/qmltyperegistrar/VersionZero/CMakeLists.txt20
-rw-r--r--tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h17
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp10
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h1
-rw-r--r--tests/auto/qml/qqmlapplicationengine/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt18
-rw-r--r--tests/auto/qml/qqmlapplicationengine/androidassets/qml/main.qml13
-rw-r--r--tests/auto/qml/qqmlapplicationengine/androidassets/qml/pages/MainPage.qml11
-rw-r--r--tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp82
-rw-r--r--tests/auto/qml/qqmlecmascript/BLACKLIST2
-rw-r--r--tests/auto/qml/qqmlecmascript/data/aliasPropertyToIC.qml11
-rw-r--r--tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js29
-rw-r--r--tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml13
-rw-r--r--tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs.qml8
-rw-r--r--tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs2.qml9
-rw-r--r--tests/auto/qml/qqmlecmascript/data/qpropertyBindingNoQPropertyCapture.qml19
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp133
-rw-r--r--tests/auto/qml/qqmlfile/tst_qqmlfile.cpp153
-rw-r--r--tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp14
-rw-r--r--tests/auto/qml/qqmljsscope/CMakeLists.txt14
-rw-r--r--tests/auto/qml/qqmljsscope/QQmlJSScopeTests/CMakeLists.txt15
-rw-r--r--tests/auto/qml/qqmljsscope/QQmlJSScopeTests/extensiontypes.h114
-rw-r--r--tests/auto/qml/qqmljsscope/data/extensions.qml8
-rw-r--r--tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp51
-rw-r--r--tests/auto/qml/qqmllanguage/data/alias.15a.qml12
-rw-r--r--tests/auto/qml/qqmllanguage/data/badGroupedProperty.qml10
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.h46
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp66
-rw-r--r--tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp3
-rw-r--r--tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp20
-rw-r--r--tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp4
-rw-r--r--tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp30
-rw-r--r--tests/auto/qml/qv4mm/tst_qv4mm.cpp89
-rw-r--r--tests/auto/qmlls/qmlls/tst_qmlls.cpp3
-rw-r--r--tests/auto/quick/pointerhandlers/mousearea_interop/data/dragHandlerInMouseAreaGrandparent.qml20
-rw-r--r--tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp19
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp47
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp43
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp39
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp2
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml (renamed from tests/auto/quick/pointerhandlers/qquicktaphandler/data/tapHandlersOverlapped.qml)0
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp40
-rw-r--r--tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp3
-rw-r--r--tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp9
-rw-r--r--tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp9
-rw-r--r--tests/auto/quick/qquickdroparea/data/ignoreRetriggerEvent.qml15
-rw-r--r--tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp29
-rw-r--r--tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml24
-rw-r--r--tests/auto/quick/qquickflickable/data/fractionalExtent.qml14
-rw-r--r--tests/auto/quick/qquickflickable/data/nestedmouseareapce.qml26
-rw-r--r--tests/auto/quick/qquickflickable/tst_qquickflickable.cpp219
-rw-r--r--tests/auto/quick/qquickfontloader/tst_qquickfontloader.cpp4
-rw-r--r--tests/auto/quick/qquickgridview/tst_qquickgridview.cpp4
-rw-r--r--tests/auto/quick/qquickitemlayer/data/itemImageLayer.qml14
-rw-r--r--tests/auto/quick/qquickitemlayer/data/qt-logo.pngbin0 -> 1301 bytes
-rw-r--r--tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp23
-rw-r--r--tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml50
-rw-r--r--tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml50
-rw-r--r--tests/auto/quick/qquicklistview/BLACKLIST6
-rw-r--r--tests/auto/quick/qquicklistview/data/appendDuringScrollDown.qml3
-rw-r--r--tests/auto/quick/qquicklistview/data/displayMargin.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/headerCrash.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/listview-itematindex.qml3
-rw-r--r--tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/sectionSnapping.qml3
-rw-r--r--tests/auto/quick/qquicklistview/data/snapOneItemWrongDirection.qml3
-rw-r--r--tests/auto/quick/qquicklistview/data/strictlyenforcerange-resize.qml3
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp92
-rw-r--r--tests/auto/quick/qquicklistview2/BLACKLIST5
-rw-r--r--tests/auto/quick/qquicklistview2/data/buttonDelegate.qml27
-rw-r--r--tests/auto/quick/qquicklistview2/data/mouseAreaDelegate.qml30
-rw-r--r--tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp153
-rw-r--r--tests/auto/quick/qquickloader/tst_qquickloader.cpp2
-rw-r--r--tests/auto/quick/qquickmousearea/data/simple.qml3
-rw-r--r--tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp35
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/BLACKLIST2
-rw-r--r--tests/auto/quick/qquickpathview/tst_qquickpathview.cpp182
-rw-r--r--tests/auto/quick/qquickpincharea/data/pinchAreaInPathView.qml48
-rw-r--r--tests/auto/quick/qquickpincharea/data/pinchproperties.qml6
-rw-r--r--tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp145
-rw-r--r--tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp80
-rw-r--r--tests/auto/quick/qquickstates/data/removeBindingWithTransition.qml23
-rw-r--r--tests/auto/quick/qquickstates/tst_qquickstates.cpp21
-rw-r--r--tests/auto/quick/qquicktext/BLACKLIST2
-rw-r--r--tests/auto/quick/qquicktext/tst_qquicktext.cpp68
-rw-r--r--tests/auto/quick/qquicktextedit/data/threeLines.qml7
-rw-r--r--tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp99
-rw-r--r--tests/auto/quick/qquickwindow/tst_qquickwindow.cpp1
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_container.qml84
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_control.qml82
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_dialogbuttonbox.qml82
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_popup.qml16
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_scrollview.qml93
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_selectionrectangle.qml2
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_splitview.qml430
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_stackview.qml33
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_tooltip.qml8
-rw-r--r--tests/auto/quickcontrols2/cursor/tst_cursor.cpp18
-rw-r--r--tests/auto/quickcontrols2/customization/tst_customization.cpp9
-rw-r--r--tests/auto/quickcontrols2/platform/CMakeLists.txt1
-rw-r--r--tests/auto/quickcontrols2/platform/data/tst_menu.qml71
-rw-r--r--tests/auto/quickcontrols2/platform/tst_platform.cpp28
-rw-r--r--tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp26
-rw-r--r--tests/auto/quickcontrols2/qquickheaderview/tst_qquickheaderview.cpp4
-rw-r--r--tests/auto/quickcontrols2/qquickmenu/BLACKLIST7
-rw-r--r--tests/auto/quickcontrols2/qquickmenu/data/customMenuCullItems.qml55
-rw-r--r--tests/auto/quickcontrols2/qquickmenu/data/customMenuUseRepeaterAsTheContentItem.qml65
-rw-r--r--tests/auto/quickcontrols2/qquickmenu/data/subMenus.qml3
-rw-r--r--tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp169
-rw-r--r--tests/auto/quickcontrols2/qquickmenubar/tst_qquickmenubar.cpp36
-rw-r--r--tests/auto/quickcontrols2/qquickpopup/data/applicationwindow-hover.qml7
-rw-r--r--tests/auto/quickcontrols2/qquickpopup/data/applicationwindow.qml11
-rw-r--r--tests/auto/quickcontrols2/qquickpopup/data/window-hover.qml6
-rw-r--r--tests/auto/quickcontrols2/qquickpopup/data/window.qml11
-rw-r--r--tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp149
-rw-r--r--tests/auto/quickcontrols2/qquickstyle/tst_qquickstyle.cpp2
-rw-r--r--tests/auto/quickcontrols2/snippets/CMakeLists.txt18
-rw-r--r--tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp2
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST3
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/data/setSelectedFile.qml71
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp701
-rw-r--r--tests/auto/quickdialogs/qquickfontdialogimpl/tst_qquickfontdialogimpl.cpp5
-rw-r--r--tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp8
-rw-r--r--tests/auto/quicktest/signalspy/tst_signalspy.cpp2
-rw-r--r--tests/auto/quickwidgets/qquickwidget/BLACKLIST2
-rw-r--r--tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp15
-rw-r--r--tests/baseline/scenegraph/data/text/text_nativerendering_no_antialiasing.qml25
-rw-r--r--tests/benchmarks/quickcontrols2/creationtime/CMakeLists.txt6
-rw-r--r--tests/benchmarks/quickcontrols2/objectcount/CMakeLists.txt6
-rw-r--r--tests/manual/quickcontrols2/testbench/controls/DelayButton.qml5
-rw-r--r--tests/manual/quickdialogs/dialogs/FileDialogPage.qml2
158 files changed, 4848 insertions, 710 deletions
diff --git a/tests/auto/qml/debugger/CMakeLists.txt b/tests/auto/qml/debugger/CMakeLists.txt
index edd268cb94..a8bb089e15 100644
--- a/tests/auto/qml/debugger/CMakeLists.txt
+++ b/tests/auto/qml/debugger/CMakeLists.txt
@@ -5,7 +5,7 @@ add_subdirectory(qqmldebugtranslationservice)
add_subdirectory(qpacketprotocol)
add_subdirectory(qqmlnativeconnector)
-if(NOT CMAKE_CROSSCOMPILING)
+if(NOT (CMAKE_CROSSCOMPILING OR (MACOS AND TEST_architecture_arch STREQUAL "x86_64")))
add_subdirectory(qdebugmessageservice)
add_subdirectory(qqmldebugtranslationclient)
add_subdirectory(qqmlenginedebugservice)
@@ -23,7 +23,7 @@ if(QT_FEATURE_private_tests)
add_subdirectory(qqmldebugclient)
add_subdirectory(qqmldebuglocal)
add_subdirectory(qv4debugger)
- if(NOT CMAKE_CROSSCOMPILING)
+ if(NOT (CMAKE_CROSSCOMPILING OR (MACOS AND TEST_architecture_arch STREQUAL "x86_64")))
add_subdirectory(qqmldebugservice)
endif()
endif()
diff --git a/tests/auto/qml/debugger/qqmldebugtranslationclient/tst_qqmldebugtranslationclient.cpp b/tests/auto/qml/debugger/qqmldebugtranslationclient/tst_qqmldebugtranslationclient.cpp
index 2cdf0a5029..28dad81be6 100644
--- a/tests/auto/qml/debugger/qqmldebugtranslationclient/tst_qqmldebugtranslationclient.cpp
+++ b/tests/auto/qml/debugger/qqmldebugtranslationclient/tst_qqmldebugtranslationclient.cpp
@@ -60,9 +60,10 @@ public:
}
private slots:
- void init()
+ void init() override
{
QQmlDebugTest::initTestCase();
+ QQmlDebugTest::init();
initDebugTranslationConnection();
QVersionedPacket<QQmlDebugConnector> packet;
diff --git a/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp b/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp
index 7519dd3043..85d4c77dd6 100644
--- a/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp
+++ b/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp
@@ -76,8 +76,9 @@ private slots:
QVERIFY(hooks->qt_qmlDebugEnableService(qPrintable(QQmlDebugTranslationServiceImpl::s_key)));
}
- void init()
+ void init() override
{
+ QQmlDebugTest::init();
hooks->qt_qmlDebugClearBuffer();
QVERIFY(currentDebugServiceMessage().isEmpty());
}
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/data/debuggerCrashOnAttach.qml b/tests/auto/qml/debugger/qqmlenginedebugservice/data/debuggerCrashOnAttach.qml
new file mode 100644
index 0000000000..ea99518425
--- /dev/null
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/data/debuggerCrashOnAttach.qml
@@ -0,0 +1,36 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtQuick.Controls 2.5
+
+Text {
+ id:text
+ font.weight: Font.Bold
+}
+
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
index bed8cb9c46..e33b9fd0e8 100644
--- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
@@ -160,6 +160,7 @@ private slots:
void watch_expression_data();
void watch_context();
void watch_file();
+ void debuggerCrashOnAttach();
void queryAvailableEngines();
void queryRootContexts();
@@ -355,7 +356,8 @@ void tst_QQmlEngineDebugService::initTestCase()
"itemWithFunctions.qml",
"rectangleWithTransitions.qml",
"customTypes.qml",
- "jsonTest.qml"
+ "jsonTest.qml",
+ "debuggerCrashOnAttach.qml"
};
for (auto file : fileNames) {
@@ -503,6 +505,7 @@ void tst_QQmlEngineDebugService::watch_object()
int origWidth = m_rootItem->property("width").toInt();
int origHeight = m_rootItem->property("height").toInt();
+
m_rootItem->setProperty("width", origWidth*2);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant))));
m_rootItem->setProperty("height", origHeight*2);
@@ -670,7 +673,7 @@ void tst_QQmlEngineDebugService::queryRootContexts()
// root context query sends only root object data - it doesn't fill in
// the children or property info
QCOMPARE(context.objects.count(), 0);
- QCOMPARE(context.contexts.count(), 7);
+ QCOMPARE(context.contexts.count(), 8);
QVERIFY(context.contexts[0].debugId >= 0);
QCOMPARE(context.contexts[0].name, QString("tst_QQmlDebug_childContext"));
}
@@ -1380,6 +1383,18 @@ void tst_QQmlEngineDebugService::createObjectOnDestruction()
QCOMPARE(spy.count(), 2);
}
+void tst_QQmlEngineDebugService::debuggerCrashOnAttach() {
+ QQmlEngineDebugObjectReference obj = findRootObject(6);
+ QVERIFY(!obj.className.isEmpty());
+
+ bool success;
+
+ m_dbg->addWatch(obj, &success);
+ QVERIFY(success);
+ QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
+ QCOMPARE(m_dbg->valid(), true);
+}
+
int main(int argc, char *argv[])
{
int _argc = argc + 1;
diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
index beaee6c61a..d4f205ea18 100644
--- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
+++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
@@ -298,7 +298,7 @@ public:
tst_qv4debugger();
private slots:
- void init();
+ void init() override;
void cleanup();
// breakpoints:
diff --git a/tests/auto/qml/debugger/shared/debugutil.cpp b/tests/auto/qml/debugger/shared/debugutil.cpp
index 4598e288c7..a4a7c2168b 100644
--- a/tests/auto/qml/debugger/shared/debugutil.cpp
+++ b/tests/auto/qml/debugger/shared/debugutil.cpp
@@ -34,8 +34,8 @@
#include <QtCore/qeventloop.h>
#include <QtCore/qtimer.h>
-QQmlDebugTest::QQmlDebugTest(const char *qmlTestDataDir)
- : QQmlDataTest(qmlTestDataDir)
+QQmlDebugTest::QQmlDebugTest(const char *qmlTestDataDir, FailOnWarningsPolicy failOnWarningsPolicy)
+ : QQmlDataTest(qmlTestDataDir, failOnWarningsPolicy)
{
}
diff --git a/tests/auto/qml/debugger/shared/debugutil_p.h b/tests/auto/qml/debugger/shared/debugutil_p.h
index 3ef2c56414..e3d46135f3 100644
--- a/tests/auto/qml/debugger/shared/debugutil_p.h
+++ b/tests/auto/qml/debugger/shared/debugutil_p.h
@@ -48,7 +48,8 @@ class QQmlDebugTest : public QQmlDataTest
{
Q_OBJECT
public:
- QQmlDebugTest(const char *qmlTestDataDir);
+ QQmlDebugTest(const char *qmlTestDataDir,
+ FailOnWarningsPolicy failOnWarningsPolicy = FailOnWarningsPolicy::DoNotFailOnWarnings);
public:
static bool waitForSignal(QObject *receiver, const char *member, int timeout = 5000);
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index 4982085172..f08d247148 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -5421,7 +5421,7 @@ void tst_QJSEngine::urlObject()
check(QStringLiteral("href"), url.toString());
check(QStringLiteral("origin"), QStringLiteral("http://example.com:777"));
- check(QStringLiteral("protocol"), url.scheme());
+ check(QStringLiteral("protocol"), url.scheme() + QLatin1Char(':'));
check(QStringLiteral("username"), url.userName());
check(QStringLiteral("password"), url.password());
check(QStringLiteral("host"), url.host() + u':' + QString::number(url.port()));
diff --git a/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp b/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp
index b0e2c288b8..4c12f6d44c 100644
--- a/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp
+++ b/tests/auto/qml/qjsonbinding/tst_qjsonbinding.cpp
@@ -77,6 +77,9 @@ private slots:
void writeProperty_javascriptExpression_data();
void writeProperty_javascriptExpression();
+ void cyclicStringify();
+ void recursiveStringify();
+
private:
QByteArray readAsUtf8(const QString &fileName);
static QJsonValue valueFromJson(const QByteArray &json);
@@ -514,6 +517,43 @@ void tst_qjsonbinding::writeProperty_javascriptExpression()
QCOMPARE(ret.toString(), expectedJson);
}
+void tst_qjsonbinding::cyclicStringify()
+{
+ QJSEngine e;
+ const QString program = QStringLiteral(R"(
+ var a = {};
+ a.a = a;
+ JSON.stringify(a);
+ )");
+
+ QJSValue result = e.evaluate(program);
+ QVERIFY(result.isError());
+ QCOMPARE(result.errorType(), QJSValue::TypeError);
+ QVERIFY(result.toString().contains(QLatin1String("Cannot convert circular structure to JSON")));
+}
+
+void tst_qjsonbinding::recursiveStringify()
+{
+ QJSEngine e;
+ const QString program = QStringLiteral(R"(
+ function create() {
+ var a = {}
+ Object.defineProperty(a, "a", {
+ enumerable: true,
+ get: function() { return create(); }
+ });
+ return a;
+ }
+
+ JSON.stringify(create());
+ )");
+
+ QJSValue result = e.evaluate(program);
+ QVERIFY(result.isError());
+ QCOMPARE(result.errorType(), QJSValue::RangeError);
+ QVERIFY(result.toString().contains(QLatin1String("Maximum call stack size exceeded")));
+}
+
QTEST_MAIN(tst_qjsonbinding)
#include "tst_qjsonbinding.moc"
diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
index e1ac1ca22d..5f7dbba8d7 100644
--- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
@@ -3,6 +3,7 @@ set(cpp_sources
birthdayparty.cpp birthdayparty.h
cppbaseclass.h
dynamicmeta.h
+ gadgetwithenum.h
invisible.h
objectwithmethod.h
person.cpp person.h
@@ -52,6 +53,7 @@ set(qml_files
compositesingleton.qml
construct.qml
contextParam.qml
+ conversionDecrement.qml
conversions.qml
conversions2.qml
curlygrouped.qml
@@ -74,6 +76,7 @@ set(qml_files
functionLookup.qml
funcWithParams.qml
functionReturningVoid.qml
+ functionTakingVar.qml
globals.qml
idAccess.qml
immediateQuit.qml
@@ -87,12 +90,14 @@ set(qml_files
interactive.qml
interceptor.qml
isnan.qml
+ javaScriptArgument.qml
jsMathObject.qml
jsimport.qml
jsmoduleimport.qml
layouts.qml
library.js
listIndices.qml
+ listPropertyAsModel.qml
listlength.qml
math.qml
methods.qml
@@ -128,6 +133,7 @@ set(qml_files
text.qml
themerbad.qml
themergood.qml
+ throwObjectName.qml
undefinedResets.qml
unknownAttached.qml
unknownParameter.qml
diff --git a/tests/auto/qml/qmlcppcodegen/data/conversionDecrement.qml b/tests/auto/qml/qmlcppcodegen/data/conversionDecrement.qml
new file mode 100644
index 0000000000..fdce0fe65c
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/conversionDecrement.qml
@@ -0,0 +1,18 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ id: panelGrid
+ property var pages: 4
+ property int currentPageIndex: 0
+
+ onPagesChanged: {
+ if (panelGrid.currentPageIndex === 0) {
+ panelGrid.currentPageIndex = panelGrid.pages - 2
+ } else if (panelGrid.currentPageIndex === panelGrid.pages - 1) {
+ panelGrid.currentPageIndex = 0
+ } else {
+ panelGrid.currentPageIndex -= 1
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/failures.qml b/tests/auto/qml/qmlcppcodegen/data/failures.qml
index 6b8c62d1b3..b8331c0ad4 100644
--- a/tests/auto/qml/qmlcppcodegen/data/failures.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/failures.qml
@@ -1,5 +1,6 @@
import QtQml
import TestTypes
+import TestTypes as TT2
import Ambiguous 1.2
QtObject {
@@ -27,4 +28,12 @@ QtObject {
}
Component.onCompleted: doesNotExist()
+
+ signal foo()
+ signal bar()
+ // Cannot assign potential undefined
+ onFoo: objectName = self.bar()
+
+ property int enumFromGadget1: GadgetWithEnum.CONNECTED + 1
+ property int enumFromGadget2: TT2.GadgetWithEnum.CONNECTED + 1
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/functionTakingVar.qml b/tests/auto/qml/qmlcppcodegen/data/functionTakingVar.qml
new file mode 100644
index 0000000000..1765bfcab9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/functionTakingVar.qml
@@ -0,0 +1,11 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property var c;
+
+ function a(b: var) {
+ c = b;
+ }
+
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h b/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h
new file mode 100644
index 0000000000..d146b9f654
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef GADGETWITHENUM_H
+#define GADGETWITHENUM_H
+
+#include <QtCore/qobject.h>
+#include <QtQmlIntegration/qqmlintegration.h>
+
+class GadgetWithEnum : public QObject {
+ Q_GADGET
+ QML_ELEMENT
+
+public:
+ enum State {
+ DISCONNECTED,
+ CONNECTING,
+ CONNECTED
+ };
+ Q_ENUM(State)
+};
+
+#endif // GADGETWITHENUM_H
diff --git a/tests/auto/qml/qmlcppcodegen/data/javaScriptArgument.qml b/tests/auto/qml/qmlcppcodegen/data/javaScriptArgument.qml
new file mode 100644
index 0000000000..f52325e915
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/javaScriptArgument.qml
@@ -0,0 +1,33 @@
+import QtQml
+
+QtObject {
+ function absMinusOne(amount: real) : real {
+ // Access it before the condition below, to make sure we still get the original
+ var minusOne = amount !== 0 ? -1 : 0;
+
+ // The condition causes the original arguemnt to be overwritten rather than a new
+ // register to be allocated
+ if (amount < 0)
+ amount = -amount;
+
+ return amount + minusOne;
+ }
+
+ property real a: absMinusOne(-5)
+ property real b: absMinusOne(10)
+
+ function stringMinusOne(amount: real) : string {
+ // Access it before the condition below, to make sure we still get the original
+ var minusOne = amount !== 0 ? -1 : 0;
+
+ // The condition causes the original arguemnt to be overwritten rather than a new
+ // register to be allocated
+ if (amount < 0)
+ amount = -amount + "t";
+
+ return amount + minusOne;
+ }
+
+ property string c: stringMinusOne(-5)
+ property string d: stringMinusOne(10)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/listIndices.qml b/tests/auto/qml/qmlcppcodegen/data/listIndices.qml
index b5fda4ef0d..9df172b2e6 100644
--- a/tests/auto/qml/qmlcppcodegen/data/listIndices.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/listIndices.qml
@@ -5,10 +5,18 @@ QtObject {
id: self
property list<QtObject> items
property int numItems: items.length
+ property QtObject fractional: items[2.25]
+ property QtObject negativeZero: items[-1 * 0]
+ property QtObject infinity: items[1 / 0]
+ property QtObject nan: items[1 - "a"]
Component.onCompleted: {
items.length = 3
for (var i = 0; i < 3; ++i)
items[i] = self
+
+ items[2.25] = null
+ items[1 / 0] = self
+ items[1 - "a"] = self
}
}
diff --git a/tests/auto/qml/qmlcppcodegen/data/listPropertyAsModel.qml b/tests/auto/qml/qmlcppcodegen/data/listPropertyAsModel.qml
new file mode 100644
index 0000000000..77a284d179
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/listPropertyAsModel.qml
@@ -0,0 +1,16 @@
+pragma Strict
+import QtQuick
+
+Item {
+ Item {
+ id: child
+ Item {}
+ Item {}
+ Item {}
+ }
+ Repeater {
+ id: self
+ Item {}
+ Component.onCompleted: self.model = child.children
+ }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/throwObjectName.qml b/tests/auto/qml/qmlcppcodegen/data/throwObjectName.qml
new file mode 100644
index 0000000000..98282998e9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/throwObjectName.qml
@@ -0,0 +1,6 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ objectName: { throw "ouch" }
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/undefinedResets.qml b/tests/auto/qml/qmlcppcodegen/data/undefinedResets.qml
index fae040a1a5..60302c64a5 100644
--- a/tests/auto/qml/qmlcppcodegen/data/undefinedResets.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/undefinedResets.qml
@@ -1,6 +1,7 @@
-pragma Strict
import TestTypes
Person {
name: shoeSize === 11 ? undefined : "Marge"
+
+ onObjectNameChanged: name = undefined
}
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index bd6fb37677..6bae57a1c7 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -129,6 +129,11 @@ private slots:
void evadingAmbiguity();
void fromBoolValue();
void invisibleTypes();
+ void functionTakingVar();
+ void javaScriptArgument();
+ void throwObjectName();
+ void conversionDecrement();
+ void listPropertyAsModel();
};
void tst_QmlCppCodegen::simpleBinding()
@@ -1447,6 +1452,11 @@ void tst_QmlCppCodegen::undefinedResets()
person->setShoeSize(10);
QCOMPARE(person->shoeSize(), 10);
QCOMPARE(person->name(), u"Marge"_qs);
+
+ person->setName(u"no one"_qs);
+ QCOMPARE(person->name(), u"no one"_qs);
+ person->setObjectName(u"the one"_qs);
+ QCOMPARE(person->name(), u"Bart"_qs);
}
void tst_QmlCppCodegen::innerObjectNonShadowable()
@@ -1553,6 +1563,10 @@ void tst_QmlCppCodegen::listIndices()
for (int i = 0; i < 3; ++i)
QCOMPARE(list.at(i), o.data());
QCOMPARE(o->property("numItems").toInt(), 3);
+ QCOMPARE(qvariant_cast<QObject *>(o->property("fractional")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(o->property("negativeZero")), o.data());
+ QCOMPARE(qvariant_cast<QObject *>(o->property("infinity")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(o->property("nan")), nullptr);
}
void tst_QmlCppCodegen::jsMathObject()
@@ -1977,6 +1991,84 @@ void tst_QmlCppCodegen::invisibleTypes()
// QCOMPARE(meta->className(), "DerivedFromInvisible");
}
+void tst_QmlCppCodegen::functionTakingVar()
+{
+ QQmlEngine engine;
+ const QUrl document(u"qrc:/TestTypes/functionTakingVar.qml"_qs);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ QVERIFY(!o->property("c").isValid());
+
+ int value = 11;
+ QQmlEnginePrivate *e = QQmlEnginePrivate::get(&engine);
+ void *args[] = { nullptr, reinterpret_cast<void *>(std::addressof(value)) };
+ QMetaType types[] = { QMetaType::fromType<void>(), QMetaType::fromType<std::decay_t<int>>() };
+ e->executeRuntimeFunction(document, 0, o.data(), 1, args, types);
+
+ QCOMPARE(o->property("c"), QVariant::fromValue<int>(11));
+}
+
+void tst_QmlCppCodegen::javaScriptArgument()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/javaScriptArgument.qml"_qs));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("a").toDouble(), 4.0);
+ QCOMPARE(o->property("b").toDouble(), 9.0);
+ QCOMPARE(o->property("c").toString(), u"5t-1"_qs);
+ QCOMPARE(o->property("d").toString(), u"9"_qs);
+}
+
+void tst_QmlCppCodegen::throwObjectName()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/throwObjectName.qml"_qs));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(QtWarningMsg, "qrc:/TestTypes/throwObjectName.qml:5:5: ouch");
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QVERIFY(o->objectName().isEmpty());
+}
+
+void tst_QmlCppCodegen::conversionDecrement()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/conversionDecrement.qml"_qs));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("currentPageIndex").toInt(), 0);
+ o->setProperty("pages", 5);
+ QCOMPARE(o->property("currentPageIndex").toInt(), 3);
+ o->setProperty("pages", 4);
+ QCOMPARE(o->property("currentPageIndex").toInt(), 0);
+ o->setProperty("pages", 6);
+ QCOMPARE(o->property("currentPageIndex").toInt(), 4);
+ o->setProperty("pages", 60);
+ QCOMPARE(o->property("currentPageIndex").toInt(), 3);
+}
+
+void tst_QmlCppCodegen::listPropertyAsModel()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/listPropertyAsModel.qml"_qs));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QQmlListReference children(o.data(), "children");
+ QCOMPARE(children.count(), 5);
+}
+
void tst_QmlCppCodegen::runInterpreted()
{
#ifdef Q_OS_ANDROID
diff --git a/tests/auto/qml/qmlformat/data/forWithLet.formatted.qml b/tests/auto/qml/qmlformat/data/forWithLet.formatted.qml
new file mode 100644
index 0000000000..5fecc1d180
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/forWithLet.formatted.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+QtObject {
+ function foo() {
+ for (let i = 0; i < 5; ++i)
+ console.log(i);
+ }
+}
diff --git a/tests/auto/qml/qmlformat/data/forWithLet.qml b/tests/auto/qml/qmlformat/data/forWithLet.qml
new file mode 100644
index 0000000000..afed76ebce
--- /dev/null
+++ b/tests/auto/qml/qmlformat/data/forWithLet.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+QtObject {
+function foo() {
+for (let i = 0; i < 5; ++i)
+console.log(i);
+}
+}
diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
index 6f35282164..4cc19e5a9c 100644
--- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp
+++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
@@ -80,8 +80,9 @@ private:
bool isInvalidFile(const QFileInfo &fileName) const;
};
+// Don't fail on warnings because we read a lot of QML files that might intentionally be malformed.
TestQmlformat::TestQmlformat()
- : QQmlDataTest(QT_QMLTEST_DATADIR)
+ : QQmlDataTest(QT_QMLTEST_DATADIR, FailOnWarningsPolicy::DoNotFailOnWarnings)
{
}
@@ -297,6 +298,9 @@ void TestQmlformat::testFormat_data()
QTest::newRow("settings") << "settings/Example1.qml"
<< "settings/Example1.formatted_mac_cr.qml" << QStringList {}
<< RunOption::OrigToCopy;
+ QTest::newRow("forWithLet")
+ << "forWithLet.qml"
+ << "forWithLet.formatted.qml" << QStringList {} << RunOption::OnCopy;
}
void TestQmlformat::testFormat()
diff --git a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
index 5f6f8e83b2..d1ad5858a5 100644
--- a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
+++ b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
@@ -74,3 +74,5 @@ qt_add_qml_module(tst-qmltyperegistrar-with-dashes
foo.cpp foo.h
)
qt_autogen_tools_initial_setup(tst-qmltyperegistrar-with-dashesplugin)
+
+add_subdirectory(VersionZero)
diff --git a/tests/auto/qml/qmltyperegistrar/VersionZero/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/VersionZero/CMakeLists.txt
new file mode 100644
index 0000000000..39dfccebd1
--- /dev/null
+++ b/tests/auto/qml/qmltyperegistrar/VersionZero/CMakeLists.txt
@@ -0,0 +1,20 @@
+qt_add_library(tst_qmltyperegistrar_major_version_zero)
+qt_autogen_tools_initial_setup(tst_qmltyperegistrar_major_version_zero)
+target_link_libraries(tst_qmltyperegistrar_major_version_zero PRIVATE Qt::Core Qt::Qml)
+qt_enable_autogen_tool(tst_qmltyperegistrar_major_version_zero "moc" ON)
+qt_add_qml_module(tst_qmltyperegistrar_major_version_zero
+ URI VersionZero
+ VERSION 0.1
+ SOURCES
+ version_zero_type.h
+)
+qt_autogen_tools_initial_setup(tst_qmltyperegistrar_major_version_zeroplugin)
+
+# Make sure the backing library is found on Windows next to the executable
+set_target_properties(
+ tst_qmltyperegistrar_major_version_zero
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/..
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/..
+ ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/..
+)
diff --git a/tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h b/tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h
new file mode 100644
index 0000000000..a3277e6ab3
--- /dev/null
+++ b/tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef VERSION_ZERO_TYPE_H
+#define VERSION_ZERO_TYPE_H
+
+#include <QtQml/qqml.h>
+
+class TypeInModuleMajorVersionZero : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ TypeInModuleMajorVersionZero(QObject *parent = nullptr) : QObject(parent) {}
+};
+
+#endif // VERSION_ZERO_TYPE_H
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
index 3fc746552e..68a21d8db8 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
@@ -396,4 +396,14 @@ void tst_qmltyperegistrar::foreignRevisionedProperty()
}
#endif
+void tst_qmltyperegistrar::typeInModuleMajorVersionZero()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+ c.setData(QStringLiteral("import VersionZero\n"
+ "TypeInModuleMajorVersionZero {}\n").toUtf8(),
+ QUrl(QTest::currentDataTag()));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+}
+
QTEST_MAIN(tst_qmltyperegistrar)
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
index 385d3b6666..f2ccaba618 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
@@ -532,6 +532,7 @@ private slots:
void addRemoveVersion_data();
void addRemoveVersion();
+ void typeInModuleMajorVersionZero();
private:
QByteArray qmltypesData;
diff --git a/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt b/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt
index d9fbf6517d..caf1e2edaa 100644
--- a/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt
+++ b/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt
@@ -55,3 +55,4 @@ qt_internal_extend_target(tst_qqmlapplicationengine CONDITION NOT ANDROID AND NO
QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\"
)
add_subdirectory(testapp)
+add_subdirectory(androidassets)
diff --git a/tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt b/tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt
new file mode 100644
index 0000000000..1c0d305311
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt
@@ -0,0 +1,18 @@
+qt_internal_add_test(tst_androidassets
+ SOURCES
+ tst_androidassets.cpp
+ PUBLIC_LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::Quick
+)
+
+# add qml/*.qml files as assets instead of resources
+
+file(
+ COPY qml/main.qml
+ DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/android-build/assets/qml/")
+
+file(
+ COPY qml/pages/MainPage.qml
+ DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/android-build/assets/qml/pages/")
diff --git a/tests/auto/qml/qqmlapplicationengine/androidassets/qml/main.qml b/tests/auto/qml/qqmlapplicationengine/androidassets/qml/main.qml
new file mode 100644
index 0000000000..6901572b00
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/androidassets/qml/main.qml
@@ -0,0 +1,13 @@
+import QtQuick
+
+// relative import: has to work when loading from android assets by path or by URL
+import "pages"
+
+Window {
+ width: 640
+ height: 480
+ visible: true
+ title: qsTr("Hello World")
+
+ MainPage { }
+}
diff --git a/tests/auto/qml/qqmlapplicationengine/androidassets/qml/pages/MainPage.qml b/tests/auto/qml/qqmlapplicationengine/androidassets/qml/pages/MainPage.qml
new file mode 100644
index 0000000000..c9549a928a
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/androidassets/qml/pages/MainPage.qml
@@ -0,0 +1,11 @@
+import QtQuick
+
+Rectangle {
+ anchors.fill: parent
+ color: "#ddd"
+
+ Text {
+ anchors.centerIn: parent
+ text: "Qt 6"
+ }
+}
diff --git a/tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp b/tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp
new file mode 100644
index 0000000000..0751e23f45
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQml/qqmlapplicationengine.h>
+#include <QtTest/qsignalspy.h>
+#include <QtTest/qtest.h>
+
+class tst_AndroidAssets : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void loadsFromAssetsPath();
+ void loadsFromAssetsUrl();
+
+private:
+
+ static QString pathPrefix()
+ {
+#ifdef Q_OS_ANDROID
+ return QStringLiteral("assets:");
+#else
+ // Even when not running on android we can check that the copying to build dir worked.
+ return QCoreApplication::applicationDirPath() + QStringLiteral("/android-build/assets");
+#endif
+ }
+
+ static QString urlPrefix() {
+#ifdef Q_OS_ANDROID
+ return pathPrefix();
+#else
+ return QStringLiteral("file:") + pathPrefix();
+#endif
+ }
+};
+
+
+void tst_AndroidAssets::loadsFromAssetsPath()
+{
+ QQmlApplicationEngine engine;
+
+ // load QML file from assets, by path:
+ engine.load(pathPrefix() + QStringLiteral("/qml/main.qml"));
+ QTRY_VERIFY(engine.rootObjects().length() == 1);
+}
+
+void tst_AndroidAssets::loadsFromAssetsUrl()
+{
+ QQmlApplicationEngine engine;
+
+ // load QML file from assets, by URL:
+ engine.load(QUrl(urlPrefix() + QStringLiteral("/qml/main.qml")));
+ QTRY_VERIFY(engine.rootObjects().length() == 1);
+}
+
+QTEST_MAIN(tst_AndroidAssets)
+
+#include "tst_androidassets.moc"
diff --git a/tests/auto/qml/qqmlecmascript/BLACKLIST b/tests/auto/qml/qqmlecmascript/BLACKLIST
deleted file mode 100644
index bd25566eef..0000000000
--- a/tests/auto/qml/qqmlecmascript/BLACKLIST
+++ /dev/null
@@ -1,2 +0,0 @@
-[gcCrashRegressionTest]
-macos arm
diff --git a/tests/auto/qml/qqmlecmascript/data/aliasPropertyToIC.qml b/tests/auto/qml/qqmlecmascript/data/aliasPropertyToIC.qml
new file mode 100644
index 0000000000..17116bb091
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/aliasPropertyToIC.qml
@@ -0,0 +1,11 @@
+import QtQml
+
+
+QtObject {
+ id: root
+
+ component Test : QtObject {}
+
+ property alias myalias: other
+ property var direct: Test { id: other }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js
new file mode 100644
index 0000000000..f51ab662ab
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js
@@ -0,0 +1,29 @@
+function init() {
+ Array.prototype.doPush = Array.prototype.push
+}
+
+function nasty() {
+ var sc_Vector = Array;
+ var push = sc_Vector.prototype.doPush;
+
+ // Change the memberData to hold something nasty on the current internalClass
+ sc_Vector.prototype.doPush = 5;
+
+ // Trigger a re-allocation of memberData
+ for (var i = 0; i < 256; ++i)
+ sc_Vector.prototype[i + "string"] = function() { return 98; }
+
+ // Change the (new) memberData back, to hold our doPush function again.
+ // This should propagate a protoId change all the way up to the lookup.
+ sc_Vector.prototype.doPush = push;
+}
+
+function func() {
+ var b = [];
+
+ // This becomes a lookup internally, which stores protoId and a pointer
+ // into the memberData. It should get invalidated when memberData is re-allocated.
+ b.doPush(3);
+
+ return b;
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml
new file mode 100644
index 0000000000..e313770bf5
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml
@@ -0,0 +1,13 @@
+import QtQml
+
+import "internalClassParentGc.js" as Foo
+
+QtObject {
+ Component.onCompleted: {
+ gc();
+ Foo.init();
+ Foo.func();
+ Foo.nasty();
+ objectName = Foo.func()[0];
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs.qml b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs.qml
new file mode 100644
index 0000000000..7f1b5b0317
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs.qml
@@ -0,0 +1,8 @@
+import QtQml
+
+QtObject {
+ id: root
+ required property QtObject invokableObject
+
+ Component.onCompleted: root.invokableObject.method_QObject(Component)
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs2.qml b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs2.qml
new file mode 100644
index 0000000000..1904740b26
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs2.qml
@@ -0,0 +1,9 @@
+import QtQml
+import QtQml as NS
+
+QtObject {
+ id: root
+ required property QtObject invokableObject
+
+ Component.onCompleted: root.invokableObject.method_QObject(NS)
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/qpropertyBindingNoQPropertyCapture.qml b/tests/auto/qml/qqmlecmascript/data/qpropertyBindingNoQPropertyCapture.qml
new file mode 100644
index 0000000000..d3a151efe3
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/qpropertyBindingNoQPropertyCapture.qml
@@ -0,0 +1,19 @@
+import QtQuick
+
+Item {
+ objectName: "redRectangle"
+ id: redRectangle
+
+ property bool b: false
+ function toggle() { b = !b }
+ width: b ? 600 : 500
+
+ Item {
+ id: blueRectangle
+ objectName: "blueRectangle"
+ // width: b ? (100 + redRectangle.width / 2) : 25
+ width: b ? redRectangle.width : 25
+ }
+
+ property int blueRectangleWidth: blueRectangle.width
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 7ea5ef6e4b..4beedc4e88 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -119,6 +119,7 @@ private slots:
void outerBindingOverridesInnerBinding();
void aliasPropertyAndBinding();
void aliasPropertyReset();
+ void aliasPropertyToIC();
void nonExistentAttachedObject();
void scope();
void importScope();
@@ -315,6 +316,7 @@ private slots:
void bindingBoundFunctions();
void qpropertyAndQtBinding();
void qpropertyBindingReplacement();
+ void qpropertyBindingNoQPropertyCapture();
void deleteRootObjectInCreation();
void onDestruction();
void onDestructionViaGC();
@@ -409,6 +411,7 @@ private slots:
void urlConstruction();
void urlPropertyInvalid();
void urlPropertySet();
+ void colonAfterProtocol();
void urlSearchParamsConstruction();
void urlSearchParamsMethods();
void variantConversionMethod();
@@ -430,6 +433,8 @@ private slots:
void functionNameInFunctionScope();
void functionAsDefaultArgument();
+ void internalClassParentGc();
+
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
static void verifyContextLifetime(const QQmlRefPointer<QQmlContextData> &ctxt);
@@ -1932,6 +1937,24 @@ void tst_qqmlecmascript::aliasPropertyReset()
QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
}
+void tst_qqmlecmascript::aliasPropertyToIC()
+{
+ QQmlEngine engine;
+ std::unique_ptr<QObject> root;
+
+ // test that a manual write (of undefined) to a resettable aliased property succeeds
+ QQmlComponent c(&engine, testFileUrl("aliasPropertyToIC.qml"));
+ root.reset(c.create());
+ QVERIFY(root);
+ auto mo = root->metaObject();
+ int aliasIndex = mo->indexOfProperty("myalias");
+ auto prop = mo->property(aliasIndex);
+ QVERIFY(prop.isAlias());
+ auto fromAlias = prop.read(root.get()).value<QObject *>();
+ auto direct = root->property("direct").value<QObject *>();
+ QCOMPARE(fromAlias, direct);
+}
+
void tst_qqmlecmascript::componentCreation_data()
{
QTest::addColumn<QString>("method");
@@ -3144,6 +3167,26 @@ void tst_qqmlecmascript::callQtInvokables()
QCOMPARE(o->actuals().count(), 1);
QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr));
+ {
+ o->reset();
+ QQmlComponent comp(&qmlengine, testFileUrl("qmlTypeWrapperArgs.qml"));
+ QScopedPointer<QObject> root {comp.createWithInitialProperties({{"invokableObject", QVariant::fromValue(o)}}) };
+ QVERIFY(root);
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), 13);
+ QCOMPARE(o->actuals().count(), 1);
+ QCOMPARE(o->actuals().at(0).value<QObject *>()->metaObject()->className(), "QQmlComponentAttached");
+ }
+
+ {
+ o->reset();
+ QQmlComponent comp(&qmlengine, testFileUrl("qmlTypeWrapperArgs2.qml"));
+ QScopedPointer<QObject> root {comp.createWithInitialProperties({{"invokableObject", QVariant::fromValue(o)}}) };
+ QVERIFY(root);
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), -1); // no function got called due to incompatible arguments
+ }
+
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", QV4::Primitive::undefinedValue()));
QCOMPARE(o->error(), false);
@@ -7741,6 +7784,28 @@ void tst_qqmlecmascript::qpropertyBindingReplacement()
QCOMPARE(root->objectName(), u"overwritten"_qs);
}
+void tst_qqmlecmascript::qpropertyBindingNoQPropertyCapture()
+{
+
+ QQmlEngine engine;
+ QQmlComponent comp(&engine, testFileUrl("qpropertyBindingNoQPropertyCapture.qml"));
+ std::unique_ptr<QObject> root(comp.create());
+ QVERIFY2(root, qPrintable(comp.errorString()));
+ auto redRectangle = root.get();
+
+ QQmlProperty blueRectangleWidth(redRectangle, "blueRectangleWidth", &engine);
+
+ auto toggle = [&](){
+ QMetaObject::invokeMethod(root.get(), "toggle");
+ };
+
+ QCOMPARE(blueRectangleWidth.read().toInt(), 25);
+ toggle();
+ QCOMPARE(blueRectangleWidth.read().toInt(), 600);
+ toggle();
+ QCOMPARE(blueRectangleWidth.read().toInt(), 25);
+}
+
void tst_qqmlecmascript::deleteRootObjectInCreation()
{
QQmlEngine engine;
@@ -9565,7 +9630,7 @@ void tst_qqmlecmascript::urlConstruction()
QV4::UrlObject *validUrl = ret->as<QV4::UrlObject>();
QVERIFY(validUrl != nullptr);
- QCOMPARE(validUrl->protocol(), "https");
+ QCOMPARE(validUrl->protocol(), "https:");
QCOMPARE(validUrl->hostname(), "example.com");
QCOMPARE(validUrl->username(), "username");
QCOMPARE(validUrl->password(), "password");
@@ -9585,7 +9650,7 @@ void tst_qqmlecmascript::urlConstruction()
QV4::UrlObject *validRelativeUrl = retRel->as<QV4::UrlObject>();
QVERIFY(validRelativeUrl != nullptr);
- QCOMPARE(validRelativeUrl->protocol(), "https");
+ QCOMPARE(validRelativeUrl->protocol(), "https:");
QCOMPARE(validRelativeUrl->hostname(), "example.com");
QCOMPARE(validRelativeUrl->username(), "username");
QCOMPARE(validRelativeUrl->password(), "password");
@@ -9645,7 +9710,7 @@ void tst_qqmlecmascript::urlPropertySet()
// protocol
QVERIFY(EVALUATE("this.url.protocol = 'https';"));
- QCOMPARE(url->protocol(), "https");
+ QCOMPARE(url->protocol(), "https:");
QCOMPARE(url->href(), "https://localhost/a/b/c");
QCOMPARE(url->origin(), "https://localhost");
@@ -9708,7 +9773,7 @@ void tst_qqmlecmascript::urlPropertySet()
"this.url.href = "
"'https://username:password@example.com:1234/path/to/something?search=value#hash';"));
- QCOMPARE(url->protocol(), "https");
+ QCOMPARE(url->protocol(), "https:");
QCOMPARE(url->hostname(), "example.com");
QCOMPARE(url->username(), "username");
QCOMPARE(url->password(), "password");
@@ -9722,6 +9787,57 @@ void tst_qqmlecmascript::urlPropertySet()
QCOMPARE(url->hash(), "#hash");
}
+void tst_qqmlecmascript::colonAfterProtocol()
+{
+ QQmlEngine qmlengine;
+
+ QObject *o = new QObject(&qmlengine);
+
+ QV4::ExecutionEngine *engine = qmlengine.handle();
+ QV4::Scope scope(engine);
+
+ QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine, o));
+
+ QV4::ScopedValue ret(scope, EVALUATE("this.url = new URL('http://localhost/a/b/c');"));
+ QV4::UrlObject *url = ret->as<QV4::UrlObject>();
+ QVERIFY(url != nullptr);
+
+ // https without colon
+ QVERIFY(EVALUATE("this.url.protocol = 'https';"));
+ QCOMPARE(url->protocol(), "https:");
+ QCOMPARE(url->href(), "https://localhost/a/b/c");
+ QCOMPARE(url->origin(), "https://localhost");
+
+ QV4::ScopedValue retHttps(scope, EVALUATE("this.url = new URL('https://localhost/a/b/c');"));
+ QV4::UrlObject *urlHttps = retHttps->as<QV4::UrlObject>();
+ QVERIFY(urlHttps != nullptr);
+
+ // ftp with a colon
+ QVERIFY(EVALUATE("this.url.protocol = 'ftp:';"));
+ QCOMPARE(urlHttps->protocol(), "ftp:");
+ QCOMPARE(urlHttps->href(), "ftp://localhost/a/b/c");
+ QCOMPARE(urlHttps->origin(), "ftp://localhost");
+
+ QV4::ScopedValue retHttp(scope, EVALUATE("this.url = new URL('http://localhost/a/b/c');"));
+ QV4::UrlObject *ftpHttps = retHttp->as<QV4::UrlObject>();
+ QVERIFY(ftpHttps != nullptr);
+
+ // ftp with three colons
+ QVERIFY(EVALUATE("this.url.protocol = 'ftp:::';"));
+ QCOMPARE(ftpHttps->protocol(), "ftp:");
+ QCOMPARE(ftpHttps->href(), "ftp://localhost/a/b/c");
+ QCOMPARE(ftpHttps->origin(), "ftp://localhost");
+
+ QV4::ScopedValue retWss(scope, EVALUATE("this.url = new URL('wss://localhost/a/b/c');"));
+ QV4::UrlObject *urlFtpHttp = retWss->as<QV4::UrlObject>();
+ QVERIFY(urlFtpHttp != nullptr);
+
+ // ftp and http with a colon inbetween
+ QVERIFY(EVALUATE("this.url.protocol = 'ftp:http:';"));
+ QCOMPARE(urlFtpHttp->protocol(), "ftp:");
+ QCOMPARE(urlFtpHttp->href(), "ftp://localhost/a/b/c");
+ QCOMPARE(urlFtpHttp->origin(), "ftp://localhost");
+}
void tst_qqmlecmascript::urlSearchParamsConstruction()
{
@@ -10090,6 +10206,15 @@ void tst_qqmlecmascript::functionAsDefaultArgument()
QCOMPARE(root->objectName(), "didRun");
}
+void tst_qqmlecmascript::internalClassParentGc()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("internalClassParentGc.qml"));
+ QScopedPointer root(component.create());
+ QVERIFY(root);
+ QCOMPARE(root->objectName(), "3");
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"
diff --git a/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp b/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp
index a1c8daddcf..9f3bfe368d 100644
--- a/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp
+++ b/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp
@@ -38,19 +38,166 @@ public:
tst_qqmlfile() {}
private Q_SLOTS:
+ void isLocalFile_data();
+ void isLocalFile();
+
+ void urlToLocalFileOrQrcOverloads_data();
void urlToLocalFileOrQrcOverloads();
+
+private:
+ void urlData();
};
+void tst_qqmlfile::urlData()
+{
+ QTest::addColumn<QString>("urlString");
+ QTest::addColumn<bool>("isLocal");
+ QTest::addColumn<QString>("localPath");
+
+ const QString invalid;
+ const QString relative = QStringLiteral("foo/bar");
+ const QString absolute = QStringLiteral("/foo/bar");
+
+ QTest::addRow("plain empty") << QStringLiteral("") << false << invalid;
+ QTest::addRow("plain no slash") << QStringLiteral("foo/bar") << false << invalid;
+ QTest::addRow("plain 1 slash") << QStringLiteral("/foo/bar") << false << invalid;
+ QTest::addRow("plain 2 slashes") << QStringLiteral("//foo/bar") << false << invalid;
+ QTest::addRow("plain 3 slashes") << QStringLiteral("///foo/bar") << false << invalid;
+
+ QTest::addRow(": empty") << QStringLiteral(":") << false << invalid;
+ QTest::addRow(": no slash") << QStringLiteral(":foo/bar") << false << invalid;
+ QTest::addRow(": 1 slash") << QStringLiteral(":/foo/bar") << false << invalid;
+ QTest::addRow(": 2 slashes") << QStringLiteral("://foo/bar") << false << invalid;
+ QTest::addRow(": 3 slashes") << QStringLiteral(":///foo/bar") << false << invalid;
+
+ QTest::addRow("C empty") << QStringLiteral("C:") << false << invalid;
+ QTest::addRow("C no slash") << QStringLiteral("C:foo/bar") << false << invalid;
+ QTest::addRow("C 1 slash") << QStringLiteral("C:/foo/bar") << false << invalid;
+ QTest::addRow("C 2 slashes") << QStringLiteral("C://foo/bar") << false << invalid;
+ QTest::addRow("C 3 slashes") << QStringLiteral("C:///foo/bar") << false << invalid;
+
+ QTest::addRow("file empty") << QStringLiteral("file:") << true << QString();
+ QTest::addRow("file no slash") << QStringLiteral("file:foo/bar") << true << relative;
+ QTest::addRow("file 1 slash") << QStringLiteral("file:/foo/bar") << true << absolute;
+ QTest::addRow("file 2 slashes") << QStringLiteral("file://foo/bar") << true << QStringLiteral("//foo/bar");
+ QTest::addRow("file 3 slashes") << QStringLiteral("file:///foo/bar") << true << absolute;
+
+ QTest::addRow("qrc empty") << QStringLiteral("qrc:") << true << QStringLiteral(":");
+ QTest::addRow("qrc no slash") << QStringLiteral("qrc:foo/bar") << true << u':' + relative;
+ QTest::addRow("qrc 1 slash") << QStringLiteral("qrc:/foo/bar") << true << u':' + absolute;
+ QTest::addRow("qrc 2 slashes") << QStringLiteral("qrc://foo/bar") << false << invalid;
+ QTest::addRow("qrc 3 slashes") << QStringLiteral("qrc:///foo/bar") << true << u':' + absolute;
+
+ QTest::addRow("file+stuff empty") << QStringLiteral("file+stuff:") << false << invalid;
+ QTest::addRow("file+stuff no slash") << QStringLiteral("file+stuff:foo/bar") << false << invalid;
+ QTest::addRow("file+stuff 1 slash") << QStringLiteral("file+stuff:/foo/bar") << false << invalid;
+ QTest::addRow("file+stuff 2 slashes") << QStringLiteral("file+stuff://foo/bar") << false << invalid;
+ QTest::addRow("file+stuff 3 slashes") << QStringLiteral("file+stuff:///foo/bar") << false << invalid;
+
+ // "assets:" and "content:" URLs are only treated as local files on android. In contrast to
+ // "qrc:" and "file:" we're not trying to be clever about multiple slashes. Two slashes are
+ // prohibited as that says part of what we would recognize as path is actually a URL authority.
+ // Everything else is android's problem.
+
+#ifdef Q_OS_ANDROID
+ const bool hasAssetsAndContent = true;
+#else
+ const bool hasAssetsAndContent = false;
+#endif
+
+ const QString assetsEmpty = hasAssetsAndContent ? QStringLiteral("assets:") : invalid;
+ const QString assetsRelative = hasAssetsAndContent ? (QStringLiteral("assets:") + relative) : invalid;
+ const QString assetsAbsolute = hasAssetsAndContent ? (QStringLiteral("assets:") + absolute) : invalid;
+ const QString assetsThreeSlashes = hasAssetsAndContent ? (QStringLiteral("assets://") + absolute) : invalid;
+
+ QTest::addRow("assets empty") << QStringLiteral("assets:") << hasAssetsAndContent << assetsEmpty;
+ QTest::addRow("assets no slash") << QStringLiteral("assets:foo/bar") << hasAssetsAndContent << assetsRelative;
+ QTest::addRow("assets 1 slash") << QStringLiteral("assets:/foo/bar") << hasAssetsAndContent << assetsAbsolute;
+ QTest::addRow("assets 2 slashes") << QStringLiteral("assets://foo/bar") << false << invalid;
+ QTest::addRow("assets 3 slashes") << QStringLiteral("assets:///foo/bar") << hasAssetsAndContent << assetsThreeSlashes;
+
+ const QString contentEmpty = hasAssetsAndContent ? QStringLiteral("content:") : invalid;
+ const QString contentRelative = hasAssetsAndContent ? (QStringLiteral("content:") + relative) : invalid;
+ const QString contentAbsolute = hasAssetsAndContent ? (QStringLiteral("content:") + absolute) : invalid;
+ const QString contentThreeSlashes = hasAssetsAndContent ? (QStringLiteral("content://") + absolute) : invalid;
+
+ QTest::addRow("content empty") << QStringLiteral("content:") << hasAssetsAndContent << contentEmpty;
+ QTest::addRow("content no slash") << QStringLiteral("content:foo/bar") << hasAssetsAndContent << contentRelative;
+ QTest::addRow("content 1 slash") << QStringLiteral("content:/foo/bar") << hasAssetsAndContent << contentAbsolute;
+ QTest::addRow("content 2 slashes") << QStringLiteral("content://foo/bar") << false << invalid;
+ QTest::addRow("content 3 slashes") << QStringLiteral("content:///foo/bar") << hasAssetsAndContent << contentThreeSlashes;
+
+
+ // These are local files everywhere. Their paths are only meaningful on android, though.
+ // The inner slashes of the path do not influence the URL parsing.
+
+ QTest::addRow("file:assets empty") << QStringLiteral("file:assets:") << true << QStringLiteral("assets:");
+ QTest::addRow("file:assets no slash") << QStringLiteral("file:assets:foo/bar") << true << QStringLiteral("assets:foo/bar");
+ QTest::addRow("file:assets 1 slash") << QStringLiteral("file:assets:/foo/bar") << true << QStringLiteral("assets:/foo/bar");
+ QTest::addRow("file:assets 2 slashes") << QStringLiteral("file:assets://foo/bar") << true << QStringLiteral("assets://foo/bar");
+ QTest::addRow("file:assets 3 slashes") << QStringLiteral("file:assets:///foo/bar") << true << QStringLiteral("assets:///foo/bar");
+
+ QTest::addRow("file:content empty") << QStringLiteral("file:content:") << true << QStringLiteral("content:");
+ QTest::addRow("file:content no slash") << QStringLiteral("file:content:foo/bar") << true << QStringLiteral("content:foo/bar");
+ QTest::addRow("file:content 1 slash") << QStringLiteral("file:content:/foo/bar") << true << QStringLiteral("content:/foo/bar");
+ QTest::addRow("file:content 2 slashes") << QStringLiteral("file:content://foo/bar") << true << QStringLiteral("content://foo/bar");
+ QTest::addRow("file:content 3 slashes") << QStringLiteral("file:content:///foo/bar") << true << QStringLiteral("content:///foo/bar");
+
+ const QString contentExternalstoragePath = hasAssetsAndContent ?
+ QStringLiteral("content://com.android.externalstorage.documents/foo") : invalid;
+ const QString contentDownloadsPath = hasAssetsAndContent ?
+ QStringLiteral("content://com.android.providers.downloads.documents/foo") : invalid;
+ const QString contentMediaPath = hasAssetsAndContent ?
+ QStringLiteral("content://com.android.providers.media.documents") : invalid;
+
+ QTest::addRow("content externalstorage") << QStringLiteral("content://com.android.externalstorage.documents/foo")
+ << hasAssetsAndContent << contentExternalstoragePath;
+ QTest::addRow("content downloads documents") << QStringLiteral("content://com.android.providers.downloads.documents/foo")
+ << hasAssetsAndContent << contentDownloadsPath;
+ QTest::addRow("content media documents") << QStringLiteral("content://com.android.providers.media.documents")
+ << hasAssetsAndContent << contentMediaPath;
+
+ QTest::addRow("assets externalstorage") << QStringLiteral("assets://com.android.externalstorage.documents/foo")
+ << false << invalid;
+ QTest::addRow("assets downloads documents") << QStringLiteral("assets://com.android.providers.downloads.documents/foo")
+ << false << invalid;
+ QTest::addRow("assets media documents") << QStringLiteral("assets://com.android.providers.media.documents")
+ << false << invalid;
+}
+
+void tst_qqmlfile::isLocalFile_data()
+{
+ urlData();
+}
+
+void tst_qqmlfile::isLocalFile()
+{
+ QFETCH(QString, urlString);
+ QFETCH(bool, isLocal);
+
+ const QUrl url(urlString);
+
+ QCOMPARE(QQmlFile::isLocalFile(urlString), isLocal);
+ QCOMPARE(QQmlFile::isLocalFile(url), isLocal);
+}
+
+void tst_qqmlfile::urlToLocalFileOrQrcOverloads_data()
+{
+ urlData();
+}
+
void tst_qqmlfile::urlToLocalFileOrQrcOverloads()
{
- const QString urlString = QStringLiteral("qrc:///example.qml");
+ QFETCH(QString, urlString);
+ QFETCH(QString, localPath);
+
const QUrl url(urlString);
const QString pathForUrlString = QQmlFile::urlToLocalFileOrQrc(urlString);
const QString pathForUrl = QQmlFile::urlToLocalFileOrQrc(url);
- QCOMPARE(pathForUrlString, pathForUrl);
- QCOMPARE(pathForUrlString, QStringLiteral(":/example.qml"));
+ QCOMPARE(pathForUrlString, localPath);
+ QCOMPARE(pathForUrl, localPath);
}
QTEST_GUILESS_MAIN(tst_qqmlfile)
diff --git a/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp b/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp
index 35c17ce588..4c99bcf9c8 100644
--- a/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp
+++ b/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp
@@ -86,19 +86,13 @@ void tst_qqmlfileselector::basicTestCached()
void tst_qqmlfileselector::applicationEngineTest()
{
QQmlApplicationEngine engine;
-QT_WARNING_PUSH
-QT_WARNING_DISABLE_DEPRECATED
- QQmlFileSelector* selector = QQmlFileSelector::get(&engine);
-QT_WARNING_POP
- QVERIFY(selector != nullptr);
- selector->setExtraSelectors(QStringList() << "basic");
+ engine.setExtraFileSelectors(QStringList() << "basic");
+ engine.load(testFileUrl("basicTest.qml"));
- QQmlComponent component(&engine, testFileUrl("basicTest.qml"));
- QObject *object = component.create();
+ QVERIFY(!engine.rootObjects().isEmpty());
+ QObject *object = engine.rootObjects().at(0);
QVERIFY(object != nullptr);
QCOMPARE(object->property("value").toString(), QString("selected"));
-
- delete object;
}
QTEST_MAIN(tst_qqmlfileselector)
diff --git a/tests/auto/qml/qqmljsscope/CMakeLists.txt b/tests/auto/qml/qqmljsscope/CMakeLists.txt
index 53cfda55cf..4ea4b3fa8c 100644
--- a/tests/auto/qml/qqmljsscope/CMakeLists.txt
+++ b/tests/auto/qml/qqmljsscope/CMakeLists.txt
@@ -1,3 +1,9 @@
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data/*)
+list(APPEND test_data ${test_data_glob})
+
qt_internal_add_test(tst_qqmljsscope
SOURCES
tst_qqmljsscope.cpp
@@ -5,8 +11,12 @@ qt_internal_add_test(tst_qqmljsscope
Qt::QmlPrivate
Qt::QmlCompilerPrivate
Qt::QuickTestUtilsPrivate
+
+ qqmljsscope_test_module
+ qqmljsscope_test_moduleplugin
+
TESTDATA
- data/orderedBindings.qml
+ ${test_data}
)
add_dependencies(tst_qqmljsscope Qt::Quick) # we need QtQuick QML module
@@ -20,3 +30,5 @@ qt_internal_extend_target(tst_qqmljsscope CONDITION NOT ANDROID AND NOT IOS
DEFINES
QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\"
)
+
+add_subdirectory(QQmlJSScopeTests)
diff --git a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/CMakeLists.txt b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/CMakeLists.txt
new file mode 100644
index 0000000000..60156139c8
--- /dev/null
+++ b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/CMakeLists.txt
@@ -0,0 +1,15 @@
+qt_add_library(qqmljsscope_test_module STATIC)
+qt_autogen_tools_initial_setup(qqmljsscope_test_module)
+
+# use PUBLIC everywhere to simplify the build of the test binary
+target_include_directories(qqmljsscope_test_module PUBLIC cpptypes/)
+target_link_libraries(qqmljsscope_test_module PUBLIC Qt::Core Qt::Qml Qt::Gui)
+
+qt6_add_qml_module(qqmljsscope_test_module
+ VERSION 1.0
+ URI QQmlJSScopeTests
+ SOURCES
+ extensiontypes.h
+)
+
+qt_autogen_tools_initial_setup(qqmljsscope_test_moduleplugin)
diff --git a/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/extensiontypes.h b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/extensiontypes.h
new file mode 100644
index 0000000000..01712e3251
--- /dev/null
+++ b/tests/auto/qml/qqmljsscope/QQmlJSScopeTests/extensiontypes.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef EXTENSIONTYPES_H
+#define EXTENSIONTYPES_H
+
+#include <QtCore/qobject.h>
+#include <qqml.h>
+
+class Extension : public QObject
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+ Q_PROPERTY(int count READ getCount WRITE setCount NOTIFY countChanged)
+
+public:
+ Extension(QObject *parent = nullptr) : QObject(parent) { }
+ int getCount() const { return 42; }
+ void setCount(int) { }
+Q_SIGNALS:
+ void countChanged();
+};
+
+class IndirectExtension : public Extension
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+public:
+ IndirectExtension(QObject *parent = nullptr) : Extension(parent) { }
+};
+
+class Extended : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_EXTENDED(Extension)
+ Q_PROPERTY(double count READ getCount WRITE setCount NOTIFY countChanged)
+
+public:
+ Extended(QObject *parent = nullptr) : QObject(parent) { }
+ double getCount() const { return 0.0; }
+ void setCount(double) { }
+Q_SIGNALS:
+ void countChanged();
+};
+
+class ExtendedIndirect : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_EXTENDED(IndirectExtension)
+ Q_PROPERTY(double count READ getCount WRITE setCount NOTIFY countChanged)
+
+public:
+ ExtendedIndirect(QObject *parent = nullptr) : QObject(parent) { }
+ double getCount() const { return 0; }
+ void setCount(double) { }
+Q_SIGNALS:
+ void countChanged();
+};
+
+class Extension2 : public QObject
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+ Q_PROPERTY(QString str READ getStr WRITE setStr NOTIFY strChanged)
+
+public:
+ Extension2(QObject *parent = nullptr) : QObject(parent) { }
+ QString getStr() const { return QStringLiteral("42"); }
+ void setStr(QString) { }
+Q_SIGNALS:
+ void strChanged();
+};
+
+class ExtendedTwice : public Extended
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_EXTENDED(Extension2)
+ Q_PROPERTY(QByteArray str READ getStr WRITE setStr)
+
+public:
+ ExtendedTwice(QObject *parent = nullptr) : Extended(parent) { }
+ QByteArray getStr() const { return QByteArray(); }
+ void setStr(QByteArray) { }
+};
+
+#endif // EXTENSIONTYPES_H
diff --git a/tests/auto/qml/qqmljsscope/data/extensions.qml b/tests/auto/qml/qqmljsscope/data/extensions.qml
new file mode 100644
index 0000000000..0f4a3fbfec
--- /dev/null
+++ b/tests/auto/qml/qqmljsscope/data/extensions.qml
@@ -0,0 +1,8 @@
+import QtQuick
+import QQmlJSScopeTests 1.0
+
+Item {
+ Extended { }
+ ExtendedIndirect { }
+ ExtendedTwice { }
+}
diff --git a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp
index bac60d4a54..20375e9f31 100644
--- a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp
+++ b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp
@@ -77,19 +77,13 @@ class tst_qqmljsscope : public QQmlDataTest
if (!error.message.isEmpty())
return QQmlJSScope::ConstPtr();
- const QStringList importPaths = {
- QLibraryInfo::path(QLibraryInfo::QmlImportsPath),
- dataDirectory(),
- };
-
- QQmlJSImporter importer { importPaths, /* resource file mapper */ nullptr };
QQmlJSLogger logger;
logger.setFileName(url);
logger.setCode(sourceCode);
logger.setSilent(true);
QQmlJSScope::Ptr target = QQmlJSScope::create();
- QQmlJSImportVisitor visitor(target, &importer, &logger, dataDirectory());
- QQmlJSTypeResolver typeResolver { &importer };
+ QQmlJSImportVisitor visitor(target, &m_importer, &logger, dataDirectory());
+ QQmlJSTypeResolver typeResolver { &m_importer };
typeResolver.init(&visitor, document.program);
return visitor.result();
}
@@ -100,9 +94,26 @@ private Q_SLOTS:
void orderedBindings();
void signalCreationDifferences();
void descriptiveNameOfNull();
+ void extensions();
public:
- tst_qqmljsscope() : QQmlDataTest(QT_QMLTEST_DATADIR) { }
+ tst_qqmljsscope()
+ : QQmlDataTest(QT_QMLTEST_DATADIR)
+ , m_importer(
+ {
+ QLibraryInfo::path(QLibraryInfo::QmlImportsPath),
+ dataDirectory(),
+ // Note: to be able to import the QQmlJSScopeTests
+ // correctly, we need an additional import path. Use
+ // this application's binary directory as done by
+ // QQmlImportDatabase
+ QCoreApplication::applicationDirPath(),
+ },
+ nullptr)
+ {}
+
+private:
+ QQmlJSImporter m_importer;
};
void tst_qqmljsscope::initTestCase()
@@ -180,5 +191,27 @@ void tst_qqmljsscope::descriptiveNameOfNull()
QCOMPARE(unscoped.descriptiveName(), u"bar of (invalid type)::foo with type baz"_qs);
}
+void tst_qqmljsscope::extensions()
+{
+ QQmlJSScope::ConstPtr root = run(u"extensions.qml"_qs);
+ QVERIFY(root);
+ QVERIFY(root->isFullyResolved());
+
+ const auto childScopes = root->childScopes();
+ QCOMPARE(childScopes.size(), 3);
+
+ QCOMPARE(childScopes[0]->baseTypeName(), u"Extended"_qs);
+ QCOMPARE(childScopes[1]->baseTypeName(), u"ExtendedIndirect"_qs);
+ QCOMPARE(childScopes[2]->baseTypeName(), u"ExtendedTwice"_qs);
+ QVERIFY(childScopes[0]->isFullyResolved());
+ QVERIFY(childScopes[1]->isFullyResolved());
+ QVERIFY(childScopes[2]->isFullyResolved());
+
+ QCOMPARE(childScopes[0]->property(u"count"_qs).typeName(), u"int"_qs);
+ QCOMPARE(childScopes[1]->property(u"count"_qs).typeName(), u"double"_qs);
+ QCOMPARE(childScopes[2]->property(u"count"_qs).typeName(), u"int"_qs);
+ QCOMPARE(childScopes[2]->property(u"str"_qs).typeName(), u"QString"_qs);
+}
+
QTEST_MAIN(tst_qqmljsscope)
#include "tst_qqmljsscope.moc"
diff --git a/tests/auto/qml/qqmllanguage/data/alias.15a.qml b/tests/auto/qml/qqmllanguage/data/alias.15a.qml
new file mode 100644
index 0000000000..ba8097c997
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/alias.15a.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.15
+
+Item {
+ id: root
+
+ property alias symbol: symbol
+ symbol.layer.enabled: true
+
+ Item {
+ id: symbol
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/badGroupedProperty.qml b/tests/auto/qml/qqmllanguage/data/badGroupedProperty.qml
new file mode 100644
index 0000000000..1b8ba61725
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/badGroupedProperty.qml
@@ -0,0 +1,10 @@
+import QtQml
+
+QtObject {
+ id: testItem
+ property rect rect
+ onComplete {
+ rect.x: 2
+ rect.width: 22
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h
index 64fe898ff5..3b227291e2 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.h
+++ b/tests/auto/qml/qqmllanguage/testtypes.h
@@ -1582,6 +1582,33 @@ public:
int own() const { return 93; }
};
+class ExtendedNamespaceByObject : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_EXTENDED_NAMESPACE(Extension)
+
+ Q_PROPERTY(QString dummy READ dummy CONSTANT)
+ Q_PROPERTY(int extension READ extension WRITE setExtension NOTIFY extensionChanged)
+
+ int m_ext = 0;
+
+public:
+ ExtendedNamespaceByObject(QObject *parent = nullptr) : QObject(parent) {}
+ QString dummy() const { return QStringLiteral("dummy"); }
+ int extension() const { return m_ext; }
+ void setExtension(int e)
+ {
+ if (e != m_ext) {
+ m_ext = e;
+ Q_EMIT extensionChanged();
+ }
+ }
+
+Q_SIGNALS:
+ void extensionChanged();
+};
+
class FactorySingleton : public QObject
{
Q_OBJECT
@@ -1972,6 +1999,25 @@ private:
QString m_name;
};
+class BindableOnly : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QByteArray data READ data WRITE setData BINDABLE dataBindable FINAL)
+ QML_ELEMENT
+public:
+ BindableOnly(QObject *parent = nullptr)
+ : QObject(parent)
+ {}
+
+ QBindable<QByteArray> dataBindable() { return QBindable<QByteArray>(&m_data); }
+
+ QByteArray data() const { return m_data.value(); }
+ void setData(const QByteArray &newData) { m_data.setValue(newData); }
+
+private:
+ QProperty<QByteArray> m_data;
+};
+
void registerTypes();
#endif // TESTTYPES_H
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index b25dec67f1..e5586d27c9 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -355,6 +355,7 @@ private slots:
void checkURLtoURLObject();
void registerValueTypes();
void extendedNamespace();
+ void extendedNamespaceByObject();
void factorySingleton();
void extendedSingleton();
void qtbug_85932();
@@ -396,6 +397,9 @@ private slots:
void componentMix();
void uncreatableAttached();
+ void bindableOnly();
+ void badGroupedProperty();
+
private:
QQmlEngine engine;
QStringList defaultImportPathList;
@@ -2170,6 +2174,22 @@ void tst_qqmllanguage::aliasProperties()
QCOMPARE(subItem->property("y").toInt(), 1);
}
+ // Nested property bindings on group properties that are actually aliases (QTBUG-94983)
+ {
+ QQmlComponent component(&engine, testFileUrl("alias.15a.qml"));
+ VERIFY_ERRORS(0);
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QPointer<QObject> subItem = qvariant_cast<QObject*>(object->property("symbol"));
+ QVERIFY(!subItem.isNull());
+
+ QPointer<QObject> subSubItem = qvariant_cast<QObject*>(subItem->property("layer"));
+
+ QCOMPARE(subSubItem->property("enabled").value<bool>(), true);
+ }
+
// Alias to sub-object with binding (QTBUG-57041)
{
// This is shold *not* crash.
@@ -4418,9 +4438,6 @@ void tst_qqmllanguage::deepProperty()
void tst_qqmllanguage::groupAssignmentFailure()
{
auto ep = std::make_unique<QQmlEngine>();
- QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QQmlComponent: Component destroyed while completion pending");
- QTest::ignoreMessage(QtMsgType::QtWarningMsg, "This may have been caused by one of the following errors:");
- QTest::ignoreMessage(QtMsgType::QtWarningMsg, QRegularExpression(".*Cannot set properties on b as it is null.*"));
QTest::ignoreMessage(QtMsgType::QtWarningMsg, QRegularExpression(".*Invalid property assignment: url expected - Assigning null to incompatible properties in QML is deprecated. This will become a compile error in future versions of Qt..*"));
QQmlComponent component(ep.get(), testFileUrl("groupFailure.qml"));
QScopedPointer<QObject> o(component.create());
@@ -6257,6 +6274,22 @@ void tst_qqmllanguage::extendedNamespace()
QCOMPARE(obj->property("fromExtension").toInt(), 9);
}
+void tst_qqmllanguage::extendedNamespaceByObject()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+ c.setData("import StaticTest\n"
+ "import QtQml\n"
+ "ExtendedNamespaceByObject {\n"
+ " extension: 10\n"
+ "}", QUrl());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(!obj.isNull());
+
+ QCOMPARE(obj->property("extension").toInt(), 10);
+}
+
void tst_qqmllanguage::factorySingleton()
{
QQmlEngine engine;
@@ -6873,6 +6906,33 @@ void tst_qqmllanguage::uncreatableAttached()
QLatin1String("Could not create attached properties object 'ItemAttached'")));
}
+void tst_qqmllanguage::bindableOnly()
+{
+ qmlRegisterTypesAndRevisions<BindableOnly>("ABC", 1);
+ QQmlEngine engine;
+
+ QQmlComponent c(&engine);
+ c.setData("import ABC\nBindableOnly {\n"
+ " data: \"sc\" + \"ore\"\n"
+ " objectName: data\n"
+ "}", QUrl(u"bindableOnly.qml"_qs));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("data").value<QByteArray>(), QByteArray("score"));
+ QCOMPARE(o->objectName(), QStringLiteral("score"));
+}
+
+void tst_qqmllanguage::badGroupedProperty()
+{
+ QQmlEngine engine;
+ const QUrl url = testFileUrl("badGroupedProperty.qml");
+ QQmlComponent c(&engine, url);
+ QVERIFY(c.isError());
+ QCOMPARE(c.errorString(),
+ QStringLiteral("%1:6 Cannot assign to non-existent property \"onComplete\"\n")
+ .arg(url.toString()));
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"
diff --git a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
index 25eaa22b86..6310ef7d54 100644
--- a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
+++ b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
@@ -361,7 +361,8 @@ void tst_qqmlnotifier::deleteFromHandler()
process.setProgram(QCoreApplication::applicationFilePath());
process.setArguments({"deleteFromHandler"});
process.start();
- QTRY_COMPARE(process.exitStatus(), QProcess::CrashExit);
+ const bool ok = process.waitForFinished(90000);
+ QVERIFY(ok);
const QByteArray output = process.readAllStandardOutput();
QVERIFY(output.contains("QFATAL"));
QVERIFY(output.contains("destroyed while one of its QML signal handlers is in progress"));
diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
index 7e1dc2f3e5..14af747024 100644
--- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
+++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
@@ -572,10 +572,28 @@ class TestClassWithClassInfo : public QObject
#define ARRAY_SIZE(arr) \
int(sizeof(arr) / sizeof(arr[0]))
+template <typename T, typename = void>
+struct SizeofOffsetsAndSizes_helper
+{
+ static constexpr size_t value = sizeof(T::offsetsAndSize); // old moc
+};
+
+template <typename T>
+struct SizeofOffsetsAndSizes_helper<T, std::void_t<decltype(T::offsetsAndSizes)>>
+{
+ static constexpr size_t value = sizeof(T::offsetsAndSizes); // new moc
+};
+
+template <typename T>
+constexpr size_t sizeofOffsetsAndSizes(const T &)
+{
+ return SizeofOffsetsAndSizes_helper<T>::value;
+}
+
#define TEST_CLASS(Class) \
QTest::newRow(#Class) \
<< &Class::staticMetaObject << ARRAY_SIZE(qt_meta_data_##Class) \
- << int(sizeof(qt_meta_stringdata_##Class.offsetsAndSize) / (sizeof(uint) * 2))
+ << int(sizeofOffsetsAndSizes(qt_meta_stringdata_##Class) / (sizeof(uint) * 2))
Q_DECLARE_METATYPE(const QMetaObject*);
diff --git a/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp b/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp
index baaf126be3..72624457c4 100644
--- a/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp
+++ b/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp
@@ -46,7 +46,7 @@ public:
private slots:
void initTestCase() override;
- void init();
+ void init() override;
void cleanup();
void basic();
@@ -161,6 +161,8 @@ void tst_QQmlSettings::initTestCase()
void tst_QQmlSettings::init()
{
+ QQmlDataTest::init();
+
QSettings settings;
settings.clear();
diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
index 38e1e585ce..f7dd8f8939 100644
--- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
+++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
@@ -102,6 +102,7 @@ private slots:
void char16Type();
void writeBackOnFunctionCall();
void valueTypeConversions();
+ void readReferenceOnGetOwnProperty();
private:
QQmlEngine engine;
@@ -1984,6 +1985,35 @@ void tst_qqmlvaluetypes::valueTypeConversions()
QCOMPARE(resultB.b, a.a);
}
+class Chose : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QRectF f READ ff CONSTANT)
+public:
+ Chose(QObject *parent = nullptr) : QObject(parent) {}
+ QRectF ff() const { return QRectF(); }
+ Q_INVOKABLE bool g(QJSValue v) { return v.hasProperty("x"); }
+};
+
+void tst_qqmlvaluetypes::readReferenceOnGetOwnProperty()
+{
+ Chose chose;
+ QQmlEngine engine;
+ engine.rootContext()->setContextProperty(QStringLiteral("chose"), &chose);
+ QQmlComponent c(&engine);
+ c.setData(R"fin(
+ import QtQml
+ QtObject {
+ property bool allo: chose.g(chose.f)
+ }
+ )fin", QUrl());
+
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QVERIFY(o->property("allo").toBool());
+}
+
#undef CHECK_TYPE_IS_NOT_VALUETYPE
QTEST_MAIN(tst_qqmlvaluetypes)
diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
index 9eea229fa6..fe2e267cfd 100644
--- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp
+++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
@@ -50,7 +50,7 @@ private slots:
void gcStats();
void multiWrappedQObjects();
void accessParentOnDestruction();
- void clearICParent();
+ void cleanInternalClasses();
void createObjectsOnDestruction();
};
@@ -119,16 +119,41 @@ void tst_qv4mm::accessParentOnDestruction()
QCOMPARE(obj->property("destructions").toInt(), 100);
}
-void tst_qv4mm::clearICParent()
+void tst_qv4mm::cleanInternalClasses()
{
QV4::ExecutionEngine engine;
QV4::Scope scope(engine.rootContext());
QV4::ScopedObject object(scope, engine.newObject());
+ QV4::ScopedObject prototype(scope, engine.newObject());
+
+ // Set a prototype so that we get a unique IC.
+ object->setPrototypeOf(prototype);
+
+ QV4::Scoped<QV4::InternalClass> prevIC(scope, object->internalClass());
+ QVERIFY(prevIC->d()->transitions.empty());
+
+ uint prevIcChainLength = 0;
+ for (QV4::Heap::InternalClass *ic = object->internalClass(); ic; ic = ic->parent)
+ ++prevIcChainLength;
+
+ const auto checkICCHainLength = [&]() {
+ uint icChainLength = 0;
+ for (QV4::Heap::InternalClass *ic = object->internalClass(); ic; ic = ic->parent)
+ ++icChainLength;
+
+ const uint redundant = object->internalClass()->numRedundantTransitions;
+ QVERIFY(redundant <= QV4::Heap::InternalClass::MaxRedundantTransitions);
+
+ // A removal makes two transitions redundant.
+ QVERIFY(icChainLength <= prevIcChainLength + 2 * redundant);
+ };
+
+ const uint numTransitions = 16 * 1024;
// Keep identifiers in a separate array so that we don't have to allocate them in the loop that
// should test the GC on InternalClass allocations.
QV4::ScopedArrayObject identifiers(scope, engine.newArrayObject());
- for (uint i = 0; i < 16 * 1024; ++i) {
+ for (uint i = 0; i < numTransitions; ++i) {
QV4::Scope scope(&engine);
QV4::ScopedString s(scope);
s = engine.newIdentifier(QString::fromLatin1("key%1").arg(i));
@@ -139,22 +164,60 @@ void tst_qv4mm::clearICParent()
object->insertMember(s, v);
}
- // When allocating the InternalClass objects required for deleting properties, the GC should
- // eventually run and remove all but the last two.
- // If we ever manage to avoid allocating the InternalClasses in the first place we will need
- // to change this test.
- for (uint i = 0; i < 16 * 1024; ++i) {
+ // There is a chain of ICs originating from the original class.
+ QCOMPARE(prevIC->d()->transitions.size(), 1u);
+ QVERIFY(prevIC->d()->transitions.front().lookup != nullptr);
+
+ // When allocating the InternalClass objects required for deleting properties, eventually
+ // the IC chain gets truncated, dropping all the removed properties.
+ for (uint i = 0; i < numTransitions; ++i) {
QV4::Scope scope(&engine);
QV4::ScopedString s(scope, identifiers->get(i));
QV4::Scoped<QV4::InternalClass> ic(scope, object->internalClass());
QVERIFY(ic->d()->parent != nullptr);
- object->deleteProperty(s->toPropertyKey());
+ QV4::ScopedValue val(scope, object->get(s->toPropertyKey()));
+ QCOMPARE(val->toNumber(), double(i));
+ QVERIFY(object->deleteProperty(s->toPropertyKey()));
+ QVERIFY(!object->hasProperty(s->toPropertyKey()));
QVERIFY(object->internalClass() != ic->d());
- QCOMPARE(object->internalClass()->parent, ic->d());
- if (ic->d()->parent == nullptr)
- return;
}
- QFAIL("Garbage collector was not triggered by large amount of InternalClasses");
+
+ // None of the properties we've added are left
+ for (uint i = 0; i < numTransitions; ++i) {
+ QV4::ScopedString s(scope, identifiers->get(i));
+ QVERIFY(!object->hasProperty(s->toPropertyKey()));
+ }
+
+ // Also no other properties have appeared
+ QScopedPointer<QV4::OwnPropertyKeyIterator> iterator(object->ownPropertyKeys(object));
+ QVERIFY(!iterator->next(object).isValid());
+
+ checkICCHainLength();
+
+ // Add and remove properties until it clears all remaining redundant ones
+ uint i = 0;
+ while (object->internalClass()->numRedundantTransitions > 0) {
+ i = (i + 1) % numTransitions;
+ QV4::ScopedString s(scope, identifiers->get(i));
+ QV4::ScopedValue v(scope);
+ v->setDouble(i);
+ object->insertMember(s, v);
+ QVERIFY(object->deleteProperty(s->toPropertyKey()));
+ }
+
+ // Make sure that all dangling ICs are actually gone.
+ scope.engine->memoryManager->runGC();
+
+ // Now the GC has removed the ICs we originally added by adding properties.
+ QVERIFY(prevIC->d()->transitions.empty() || prevIC->d()->transitions.front().lookup == nullptr);
+
+ // Same thing with redundant prototypes
+ for (uint i = 0; i < numTransitions; ++i) {
+ QV4::ScopedObject prototype(scope, engine.newObject());
+ object->setPrototypeOf(prototype); // Makes previous prototype redundant
+ }
+
+ checkICCHainLength();
}
void tst_qv4mm::createObjectsOnDestruction()
diff --git a/tests/auto/qmlls/qmlls/tst_qmlls.cpp b/tests/auto/qmlls/qmlls/tst_qmlls.cpp
index 2d0d38f352..a7276fcedf 100644
--- a/tests/auto/qmlls/qmlls/tst_qmlls.cpp
+++ b/tests/auto/qmlls/qmlls/tst_qmlls.cpp
@@ -108,7 +108,8 @@ tst_Qmlls::tst_Qmlls()
connect(&m_server, &QProcess::readyReadStandardError, this,
[this]() { qWarning() << "LSPerr" << m_server.readAllStandardError(); });
- m_qmllsPath = QLibraryInfo::path(QLibraryInfo::BinariesPath) + QLatin1String("/qmlls");
+ m_qmllsPath =
+ QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath) + QLatin1String("/qmlls");
#ifdef Q_OS_WIN
m_qmllsPath += QLatin1String(".exe");
#endif
diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragHandlerInMouseAreaGrandparent.qml b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragHandlerInMouseAreaGrandparent.qml
new file mode 100644
index 0000000000..7adb205a1f
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragHandlerInMouseAreaGrandparent.qml
@@ -0,0 +1,20 @@
+import QtQuick 2.12
+
+Rectangle {
+ width: 200; height: 200
+
+ DragHandler { }
+
+ Rectangle {
+ objectName: "button"
+ width: 100; height: 40; x: 10; y: 10
+ border.color: "orange"
+ color: ma.pressed ? "lightsteelblue" : "beige"
+
+ MouseArea {
+ id: ma
+ anchors.fill: parent
+ onDoubleClicked: console.log("__")
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp
index ffc0cc333d..ccc1364705 100644
--- a/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp
+++ b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp
@@ -55,6 +55,7 @@ private slots:
void dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch_data();
void dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch();
void hoverHandlerDoesntHoverOnPress();
+ void doubleClickInMouseAreaWithDragHandlerInGrandparent();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName);
@@ -209,6 +210,24 @@ void tst_MouseAreaInterop::hoverHandlerDoesntHoverOnPress() // QTBUG-72843
QCOMPARE(hoveredChangedSpy.count(), 0);
}
+void tst_MouseAreaInterop::doubleClickInMouseAreaWithDragHandlerInGrandparent()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("dragHandlerInMouseAreaGrandparent.qml")));
+
+ QQuickDragHandler *handler = window.rootObject()->findChild<QQuickDragHandler*>();
+ QVERIFY(handler);
+ QSignalSpy dragActiveSpy(handler, &QQuickDragHandler::activeChanged);
+ QQuickMouseArea *ma = window.rootObject()->findChild<QQuickMouseArea*>();
+ QVERIFY(ma);
+ QSignalSpy dClickSpy(ma, &QQuickMouseArea::doubleClicked);
+ QPoint p = ma->mapToScene(ma->boundingRect().center()).toPoint();
+
+ QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, p);
+ QCOMPARE(dClickSpy.count(), 1);
+ QCOMPARE(dragActiveSpy.count(), 0);
+}
+
QTEST_MAIN(tst_MouseAreaInterop)
#include "tst_mousearea_interop.moc"
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
index 9b11f8170b..c0192a81d9 100644
--- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
@@ -62,6 +62,7 @@ private slots:
void hoverHandlerAndUnderlyingHoverHandler();
void mouseAreaAndUnderlyingHoverHandler();
void hoverHandlerAndUnderlyingMouseArea();
+ void disabledHoverHandlerAndUnderlyingMouseArea();
void movingItemWithHoverHandler();
void margin();
void window();
@@ -299,6 +300,52 @@ void tst_HoverHandler::hoverHandlerAndUnderlyingMouseArea()
#endif
}
+void tst_HoverHandler::disabledHoverHandlerAndUnderlyingMouseArea()
+{
+ // Check that if a disabled HoverHandler is installed on an item, it
+ // will not participate in hover event delivery, and as such, also
+ // not block propagation to siblings.
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "lesHoverables.qml");
+ QQuickView * window = windowPtr.data();
+ QQuickItem * bottomSidebar = window->rootObject()->findChild<QQuickItem *>("bottomSidebar");
+ QVERIFY(bottomSidebar);
+ QQuickMouseArea *bottomSidebarMA = bottomSidebar->findChild<QQuickMouseArea *>("bottomSidebarMA");
+ QVERIFY(bottomSidebarMA);
+ QQuickItem * button = bottomSidebar->findChild<QQuickItem *>("buttonWithHH");
+ QVERIFY(button);
+ QQuickHoverHandler *buttonHH = button->findChild<QQuickHoverHandler *>("buttonHH");
+ QVERIFY(buttonHH);
+
+ // By disabling the HoverHandler, it should no longer
+ // block the sibling MouseArea underneath from receiving hover events.
+ buttonHH->setEnabled(false);
+
+ QPoint buttonCenter(button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint());
+ QPoint rightOfButton(button->mapToScene(QPointF(button->width() + 2, button->height() / 2)).toPoint());
+ QPoint outOfSidebar(bottomSidebar->mapToScene(QPointF(bottomSidebar->width() + 2, bottomSidebar->height() / 2)).toPoint());
+ QSignalSpy sidebarHoveredSpy(bottomSidebarMA, SIGNAL(hoveredChanged()));
+ QSignalSpy buttonHoveredSpy(buttonHH, SIGNAL(hoveredChanged()));
+
+ QTest::mouseMove(window, outOfSidebar);
+ QCOMPARE(bottomSidebarMA->hovered(), false);
+ QCOMPARE(sidebarHoveredSpy.count(), 0);
+ QCOMPARE(buttonHH->isHovered(), false);
+ QCOMPARE(buttonHoveredSpy.count(), 0);
+
+ QTest::mouseMove(window, buttonCenter);
+ QCOMPARE(bottomSidebarMA->hovered(), true);
+ QCOMPARE(sidebarHoveredSpy.count(), 1);
+ QCOMPARE(buttonHH->isHovered(), false);
+ QCOMPARE(buttonHoveredSpy.count(), 0);
+
+ QTest::mouseMove(window, rightOfButton);
+ QCOMPARE(bottomSidebarMA->hovered(), true);
+ QCOMPARE(sidebarHoveredSpy.count(), 1);
+ QCOMPARE(buttonHH->isHovered(), false);
+ QCOMPARE(buttonHoveredSpy.count(), 0);
+}
+
void tst_HoverHandler::movingItemWithHoverHandler()
{
if (isPlatformWayland())
diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml
index 3cabde5f59..425494ced6 100644
--- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml
+++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml
@@ -56,7 +56,7 @@ Rectangle {
PinchHandler {
id: pincharea
objectName: "pinchHandler"
- minimumScale: 1.0
+ minimumScale: 0.5
maximumScale: 4.0
minimumRotation: 0.0
maximumRotation: 90.0
diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
index da84bb0a63..b064ffcf13 100644
--- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
@@ -154,19 +154,19 @@ void tst_QQuickPinchHandler::pinchProperties()
QSignalSpy scaleMinSpy(pinchHandler, SIGNAL(minimumScaleChanged()));
QSignalSpy scaleMaxSpy(pinchHandler, SIGNAL(maximumScaleChanged()));
- QCOMPARE(pinchHandler->minimumScale(), 1.0);
+ QCOMPARE(pinchHandler->minimumScale(), 0.5);
QCOMPARE(pinchHandler->maximumScale(), 4.0);
- pinchHandler->setMinimumScale(0.5);
+ pinchHandler->setMinimumScale(0.25);
pinchHandler->setMaximumScale(1.5);
- QCOMPARE(pinchHandler->minimumScale(), 0.5);
+ QCOMPARE(pinchHandler->minimumScale(), 0.25);
QCOMPARE(pinchHandler->maximumScale(), 1.5);
QCOMPARE(scaleMinSpy.count(),1);
QCOMPARE(scaleMaxSpy.count(),1);
- pinchHandler->setMinimumScale(0.5);
+ pinchHandler->setMinimumScale(0.25);
pinchHandler->setMaximumScale(1.5);
QCOMPARE(scaleMinSpy.count(),1);
@@ -373,14 +373,18 @@ void tst_QQuickPinchHandler::scaleThreeFingers()
void tst_QQuickPinchHandler::scaleNativeGesture_data()
{
QTest::addColumn<QString>("qmlfile");
+ QTest::addColumn<qreal>("scale");
- QTest::newRow("just pinch") << "pinchproperties.qml";
- QTest::newRow("pinch & drag") << "pinchAndDrag.qml";
+ QTest::newRow("just pinch") << "pinchproperties.qml" << 1.1;
+ QTest::newRow("pinch & drag") << "pinchAndDrag.qml" << 1.1;
+ QTest::newRow("bigger than limit") << "pinchproperties.qml" << 5.0;
+ QTest::newRow("smaller than limit") << "pinchproperties.qml" << 0.25;
}
void tst_QQuickPinchHandler::scaleNativeGesture()
{
QFETCH(QString, qmlfile);
+ QFETCH(qreal, scale);
QQuickView *window = QQuickViewTestUtils::createView();
QScopedPointer<QQuickView> scope(window);
@@ -400,8 +404,8 @@ void tst_QQuickPinchHandler::scaleNativeGesture()
QPointF targetPos = target->position();
ulong ts = 1;
- // first pinch: scale it up
- const qreal expectedScale = 1.1;
+ // first pinch: scale it
+ const qreal expectedScale = qBound(qreal(0.5), scale, qreal(4));
QPointF pinchPos(75, 75);
QPointF pinchLocalPos = target->mapFromScene(pinchPos);
// target position is adjusted in QQuickItemPrivate::adjustedPosForTransform()
@@ -411,11 +415,11 @@ void tst_QQuickPinchHandler::scaleNativeGesture()
QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad,
Qt::BeginNativeGesture, pinchPos, pinchPos);
QWindowSystemInterface::handleGestureEventWithRealValue(window, ts++, touchpad,
- Qt::ZoomNativeGesture, expectedScale - 1, pinchPos, pinchPos);
+ Qt::ZoomNativeGesture, scale - 1, pinchPos, pinchPos);
QTRY_COMPARE(target->scale(), expectedScale);
QCOMPARE(pinchHandler->active(), true);
- QCOMPARE(pinchHandler->centroid().position(), pinchLocalPos);
- QCOMPARE(pinchHandler->centroid().scenePosition(), pinchPos);
+ QCOMPARE(pinchHandler->centroid().position().toPoint(), pinchLocalPos.toPoint());
+ QCOMPARE(pinchHandler->centroid().scenePosition().toPoint(), pinchPos.toPoint());
QVERIFY(qAbs(target->position().x() - expectedPos.x()) < 0.001);
QVERIFY(qAbs(target->position().y() - expectedPos.y()) < 0.001);
QCOMPARE(pinchHandler->scale(), expectedScale);
@@ -431,9 +435,12 @@ void tst_QQuickPinchHandler::scaleNativeGesture()
QCOMPARE(pinchHandler->translation(), QVector2D());
QCOMPARE(pinchHandler->rotation(), 0);
- // second pinch at a different position: scale it down to original size again
+ // second pinch at a different position: scale it back to original size again
+ // but remove the limits first, so that we can scale arbitrarily
+ pinchHandler->setMaximumScale(qInf());
+ pinchHandler->setMinimumScale(-qInf());
const qreal reverseScale = (1 / expectedScale);
- pinchPos = QPointF(125, 125);
+ pinchPos = QPointF(110, 110);
pinchLocalPos = target->mapFromScene(pinchPos);
QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad,
Qt::BeginNativeGesture, pinchPos, pinchPos);
@@ -441,8 +448,8 @@ void tst_QQuickPinchHandler::scaleNativeGesture()
Qt::ZoomNativeGesture, reverseScale - 1, pinchPos, pinchPos);
QTRY_COMPARE(target->scale(), 1);
QCOMPARE(pinchHandler->active(), true);
- QCOMPARE(pinchHandler->centroid().position(), pinchLocalPos);
- QCOMPARE(pinchHandler->centroid().scenePosition(), pinchPos);
+ QCOMPARE(pinchHandler->centroid().position().toPoint(), pinchLocalPos.toPoint());
+ QCOMPARE(pinchHandler->centroid().scenePosition().toPoint(), pinchPos.toPoint());
QCOMPARE(pinchHandler->scale(), 1);
QCOMPARE(pinchHandler->activeScale(), reverseScale);
QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad,
@@ -680,7 +687,7 @@ void tst_QQuickPinchHandler::retouch()
// accept some slack
QVERIFY(withinBounds(1.4, root->property("scale").toReal(), 1.6));
- QCOMPARE(pinchHandler->centroid().position(), QPointF(40, 40)); // blackrect is at 50,50
+ QCOMPARE(pinchHandler->centroid().position().toPoint(), QPoint(40, 40)); // blackrect is at 50,50
QVERIFY(withinBounds(1.4, blackRect->scale(), 1.6));
QCOMPARE(root->property("activeCount").toInt(), 1);
@@ -763,7 +770,7 @@ void tst_QQuickPinchHandler::cancel()
QQuickTouchUtils::flush(window);
QVERIFY(withinBounds(1.4, root->property("scale").toReal(), 1.6));
- QCOMPARE(pinchHandler->centroid().position(), QPointF(40, 40)); // blackrect is at 50,50
+ QCOMPARE(pinchHandler->centroid().position().toPoint(), QPoint(40, 40)); // blackrect is at 50,50
QVERIFY(withinBounds(1.4, blackRect->scale(), 1.6));
QSKIP("cancel is not supported atm");
@@ -773,7 +780,7 @@ void tst_QQuickPinchHandler::cancel()
QQuickTouchUtils::flush(window);
QCOMPARE(root->property("scale").toReal(), 1.0);
- QCOMPARE(root->property("center").toPointF(), QPointF(40, 40)); // blackrect is at 50,50
+ QCOMPARE(root->property("center").toPoint(), QPoint(40, 40)); // blackrect is at 50,50
QCOMPARE(blackRect->scale(), 1.0);
QVERIFY(!root->property("pinchActive").toBool());
}
diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
index 6296eb4da8..e68f9b127c 100644
--- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
@@ -341,11 +341,11 @@ void tst_PointerHandlers::touchEventDelivery()
QTest::touchEvent(window, touchDevice).move(0, p1, window);
QQuickTouchUtils::flush(window);
qCDebug(lcPointerTests) << "events after touch move" << eventItem1->eventList;
- QCOMPARE(eventItem1->eventList.size(), 2); // no grabs -> no updates
+ QCOMPARE(eventItem1->eventList.size(), 3); // no grabs -> only the handler gets the update
QTest::touchEvent(window, touchDevice).release(0, p1, window);
QQuickTouchUtils::flush(window);
qCDebug(lcPointerTests) << "events after touch release" << eventItem1->eventList;
- QCOMPARE(eventItem1->eventList.size(), 3);
+ QCOMPARE(eventItem1->eventList.size(), 4);
QCOMPARE_EVENT(eventItem1->eventList.size() - 1, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Released, NoGrab);
eventItem1->eventList.clear();
@@ -396,12 +396,12 @@ void tst_PointerHandlers::touchEventDelivery()
QTest::touchEvent(window, touchDevice).move(0, p1, window);
QQuickTouchUtils::flush(window);
qCDebug(lcPointerTests) << "events after touch move" << eventItem1->eventList;
- QCOMPARE(eventItem1->eventList.size(), 2);
+ QCOMPARE(eventItem1->eventList.size(), 3);
QTest::touchEvent(window, touchDevice).release(0, p1, window);
QQuickTouchUtils::flush(window);
qCDebug(lcPointerTests) << "events after touch release" << eventItem1->eventList;
- QCOMPARE(eventItem1->eventList.size(), 3);
- QCOMPARE_EVENT(2, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Released, NoGrab);
+ QCOMPARE(eventItem1->eventList.size(), 4);
+ QCOMPARE_EVENT(3, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Released, NoGrab);
eventItem1->eventList.clear();
// wait to avoid getting a double click event
@@ -423,11 +423,11 @@ void tst_PointerHandlers::touchEventDelivery()
QTest::touchEvent(window, touchDevice).move(0, p1, window);
QQuickTouchUtils::flush(window);
qCDebug(lcPointerTests) << "events after touch move" << eventItem1->eventList;
- QCOMPARE(eventItem1->eventList.size(), 2);
+ QCOMPARE(eventItem1->eventList.size(), 3);
QTest::touchEvent(window, touchDevice).release(0, p1, window);
QQuickTouchUtils::flush(window);
qCDebug(lcPointerTests) << "events after touch release" << eventItem1->eventList;
- QCOMPARE(eventItem1->eventList.size(), 3);
+ QCOMPARE(eventItem1->eventList.size(), 4);
eventItem1->eventList.clear();
// wait to avoid getting a double click event
@@ -501,14 +501,14 @@ void tst_PointerHandlers::mouseEventDelivery()
QPoint p1 = QPoint(20, 20);
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
qCDebug(lcPointerTests) << "events after mouse press" << eventItem1->eventList;
- QCOMPARE(eventItem1->eventList.size(), 2);
+ QCOMPARE(eventItem1->eventList.size(), 3); // handler: hover; handler: press; item: press
p1 += QPoint(10, 0);
QTest::mouseMove(window, p1);
qCDebug(lcPointerTests) << "events after mouse move" << eventItem1->eventList;
- QCOMPARE(eventItem1->eventList.size(), 2);
+ QCOMPARE(eventItem1->eventList.size(), 4); // handler: hover
QTest::mouseRelease(window, Qt::LeftButton);
qCDebug(lcPointerTests) << "events after mouse release" << eventItem1->eventList;
- QCOMPARE(eventItem1->eventList.size(), 2);
+ QCOMPARE(eventItem1->eventList.size(), 4);
eventItem1->eventList.clear();
// wait to avoid getting a double click event
@@ -521,9 +521,9 @@ void tst_PointerHandlers::mouseEventDelivery()
p1 = QPoint(20, 20);
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
qCDebug(lcPointerTests) << "events after mouse press" << eventItem1->eventList;
- QCOMPARE(eventItem1->eventList.size(), 2);
- QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Pressed, NoGrab);
- QCOMPARE_EVENT(1, Event::MouseDestination, QEvent::MouseButtonPress, QEventPoint::State::Pressed, QPointingDevice::GrabExclusive);
+ QCOMPARE(eventItem1->eventList.size(), 3);
+ QCOMPARE_EVENT(1, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Pressed, NoGrab);
+ QCOMPARE_EVENT(2, Event::MouseDestination, QEvent::MouseButtonPress, QEventPoint::State::Pressed, QPointingDevice::GrabExclusive);
QCOMPARE(window->mouseGrabberItem(), eventItem1);
QPointF localPos = eventItem1->mapFromScene(p1);
@@ -536,11 +536,11 @@ void tst_PointerHandlers::mouseEventDelivery()
p1 += QPoint(10, 0);
QTest::mouseMove(window, p1);
qCDebug(lcPointerTests) << "events after mouse move" << eventItem1->eventList;
- QCOMPARE(eventItem1->eventList.size(), 4);
- QCOMPARE_EVENT(3, Event::MouseDestination, QEvent::MouseMove, QEventPoint::State::Updated, QPointingDevice::GrabExclusive);
+ QCOMPARE(eventItem1->eventList.size(), 5);
+ QCOMPARE_EVENT(4, Event::MouseDestination, QEvent::MouseMove, QEventPoint::State::Updated, QPointingDevice::GrabExclusive);
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
qCDebug(lcPointerTests) << "events after mouse release" << eventItem1->eventList;
- QCOMPARE(eventItem1->eventList.size(), 7);
+ QCOMPARE(eventItem1->eventList.size(), 8);
QCOMPARE_EVENT(eventItem1->eventList.size() - 2, Event::MouseDestination, QEvent::MouseButtonRelease, QEventPoint::State::Released, NoGrab);
QCOMPARE_EVENT(eventItem1->eventList.size() - 1, Event::MouseDestination, QEvent::UngrabMouse, QEventPoint::State::Released, QPointingDevice::UngrabExclusive);
eventItem1->eventList.clear();
@@ -647,9 +647,10 @@ void tst_PointerHandlers::dynamicCreation()
QPoint p1(20, 20);
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
- QTRY_COMPARE(eventItem1->eventList.size(), 2);
- QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Pressed, NoGrab);
- QCOMPARE_EVENT(1, Event::MouseDestination, QEvent::MouseButtonPress, QEventPoint::State::Pressed, NoGrab);
+ QTRY_COMPARE(eventItem1->eventList.size(), 3);
+ QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Updated, NoGrab);
+ QCOMPARE_EVENT(1, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Pressed, NoGrab);
+ QCOMPARE_EVENT(2, Event::MouseDestination, QEvent::MouseButtonPress, QEventPoint::State::Pressed, NoGrab);
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
}
diff --git a/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp
index d22b3d86aa..f9f3216fbe 100644
--- a/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp
@@ -149,6 +149,8 @@ void tst_PointHandler::tabletStylus()
QQuickPointHandler *handler = window->rootObject()->findChild<QQuickPointHandler *>("pointHandler");
QVERIFY(handler);
handler->setAcceptedDevices(QInputDevice::DeviceType::Stylus);
+ // avoid generating a double click
+ QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
QSignalSpy activeSpy(handler, SIGNAL(activeChanged()));
QSignalSpy pointSpy(handler, SIGNAL(pointChanged()));
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/tapHandlersOverlapped.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml
index 8d2e36d921..8d2e36d921 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/tapHandlersOverlapped.qml
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
index fb817ca045..c17fb5777a 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
@@ -75,6 +75,8 @@ private slots:
void rightLongPressIgnoreWheel();
void negativeZStackingOrder();
void nonTopLevelParentWindow();
+ void nestedDoubleTap_data();
+ void nestedDoubleTap();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName,
@@ -837,7 +839,7 @@ void tst_TapHandler::rightLongPressIgnoreWheel()
void tst_TapHandler::negativeZStackingOrder() // QTBUG-83114
{
QScopedPointer<QQuickView> windowPtr;
- createView(windowPtr, "tapHandlersOverlapped.qml");
+ createView(windowPtr, "nested.qml");
QQuickView *window = windowPtr.data();
QQuickItem *root = window->rootObject();
@@ -891,6 +893,42 @@ void tst_TapHandler::nonTopLevelParentWindow() // QTBUG-91716
QCOMPARE(root->property("tapCount").toInt(), 2);
}
+void tst_TapHandler::nestedDoubleTap_data()
+{
+ QTest::addColumn<QQuickTapHandler::GesturePolicy>("childGesturePolicy");
+
+ QTest::newRow("DragThreshold") << QQuickTapHandler::GesturePolicy::DragThreshold;
+ QTest::newRow("WithinBounds") << QQuickTapHandler::GesturePolicy::WithinBounds;
+ QTest::newRow("ReleaseWithinBounds") << QQuickTapHandler::GesturePolicy::ReleaseWithinBounds;
+ QTest::newRow("DragWithinBounds") << QQuickTapHandler::GesturePolicy::DragWithinBounds;
+}
+
+void tst_TapHandler::nestedDoubleTap() // QTBUG-102625
+{
+ QFETCH(QQuickTapHandler::GesturePolicy, childGesturePolicy);
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("nested.qml")));
+ QQuickItem *root = window.rootObject();
+ QQuickTapHandler *parentTapHandler = root->findChild<QQuickTapHandler*>("parentTapHandler");
+ QVERIFY(parentTapHandler);
+ QSignalSpy parentSpy(parentTapHandler, &QQuickTapHandler::doubleTapped);
+ QQuickTapHandler *childTapHandler = root->findChild<QQuickTapHandler*>("childTapHandler");
+ QVERIFY(childTapHandler);
+ QSignalSpy childSpy(childTapHandler, &QQuickTapHandler::doubleTapped);
+ childTapHandler->setGesturePolicy(childGesturePolicy);
+
+ QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, QPoint(150, 100));
+
+ QCOMPARE(childSpy.count(), 1);
+ // If the child gets by with a passive grab, both handlers see tap and double-tap.
+ // If the child takes an exclusive grab and stops event propagation, the parent doesn't see them.
+ QCOMPARE(parentSpy.count(),
+ childGesturePolicy == QQuickTapHandler::GesturePolicy::DragThreshold ? 1 : 0);
+ QCOMPARE(root->property("taps").toList().count(),
+ childGesturePolicy == QQuickTapHandler::GesturePolicy::DragThreshold ? 4 : 2);
+}
+
QTEST_MAIN(tst_TapHandler)
#include "tst_qquicktaphandler.moc"
diff --git a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
index d48c7ed163..31032ff64b 100644
--- a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
+++ b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
@@ -69,7 +69,7 @@ public:
public slots:
void initTestCase() override;
void cleanupTestCase();
- void init();
+ void init() override;
void cleanup();
private slots:
@@ -111,6 +111,7 @@ void tst_QQuickAccessible::cleanupTestCase()
void tst_QQuickAccessible::init()
{
+ QQmlDataTest::init();
QTestAccessibility::clearEvents();
}
diff --git a/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp b/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp
index 159fb1398e..bc1c16de6d 100644
--- a/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp
+++ b/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp
@@ -46,7 +46,7 @@ public:
tst_qquickbehaviors() : QQmlDataTest(QT_QMLTEST_DATADIR) {}
private slots:
- void init() { qApp->processEvents(); } //work around animation timer bug (QTBUG-22865)
+ void init() override;
void simpleBehavior();
void scriptTriggered();
void cppTriggered();
@@ -81,6 +81,13 @@ private slots:
void defaultQProperty();
};
+void tst_qquickbehaviors::init()
+{
+ QQmlDataTest::init();
+ //work around animation timer bug (QTBUG-22865)
+ qApp->processEvents();
+}
+
void tst_qquickbehaviors::simpleBehavior()
{
QQmlEngine engine;
diff --git a/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp b/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp
index ee9321a236..d346e09d40 100644
--- a/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp
+++ b/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp
@@ -41,10 +41,17 @@ public:
tst_qquickboundaryrule() : QQmlDataTest(QT_QMLTEST_DATADIR) {}
private slots:
- void init() { qApp->processEvents(); } //work around animation timer bug (QTBUG-22865)
+ void init() override;
void dragHandler();
};
+void tst_qquickboundaryrule::init()
+{
+ QQmlDataTest::init();
+ //work around animation timer bug (QTBUG-22865)
+ qApp->processEvents();
+}
+
void tst_qquickboundaryrule::dragHandler()
{
QQuickView window;
diff --git a/tests/auto/quick/qquickdroparea/data/ignoreRetriggerEvent.qml b/tests/auto/quick/qquickdroparea/data/ignoreRetriggerEvent.qml
new file mode 100644
index 0000000000..af25a04ee7
--- /dev/null
+++ b/tests/auto/quick/qquickdroparea/data/ignoreRetriggerEvent.qml
@@ -0,0 +1,15 @@
+import QtQuick 2.0
+
+DropArea {
+ property int enterEvents: 0
+ property int exitEvents: 0
+ width: 100; height: 100
+ objectName: "dropArea"
+ onEntered: function (drag) { ++enterEvents; drag.accepted = false }
+ onExited: {++exitEvents}
+ Item {
+ objectName: "dragItem"
+ x: 50; y: 50
+ width: 10; height: 10
+ }
+}
diff --git a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp
index de7e3f1629..dcc05f548b 100644
--- a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp
+++ b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp
@@ -67,6 +67,9 @@ public:
private slots:
void containsDrag_internal();
void containsDrag_external();
+
+ void ignoreRetriggerEvent();
+
void keys_internal();
void keys_external();
void source_internal();
@@ -829,6 +832,32 @@ void tst_QQuickDropArea::competingDrags()
QCOMPARE(evaluate<QString>(dropArea1, "statuslol"), QStringLiteral("parent"));
}
+void tst_QQuickDropArea::ignoreRetriggerEvent()
+{
+ QQuickView window;
+ QByteArray errorMessage;
+ QVERIFY2(QQuickTest::initView(window, testFileUrl("ignoreRetriggerEvent.qml"), true, &errorMessage), errorMessage.constData());
+
+ QQuickItem *dropArea = window.rootObject();
+ QVERIFY(dropArea);
+ QQuickItem *dragItem = dropArea->findChild<QQuickItem *>("dragItem");
+ QVERIFY(dragItem);
+
+ evaluate<void>(dragItem, "Drag.active = true");
+ // Drag the item within the drop area
+ dragItem->setPosition(QPointF(25, 25));
+ QCoreApplication::processEvents();
+ dragItem->setPosition(QPointF(50, 50));
+ QCoreApplication::processEvents();
+ dragItem->setPosition(QPointF(75, 75));
+ QCoreApplication::processEvents();
+
+ QCOMPARE(evaluate<bool>(dropArea, "containsDrag"), false);
+ QCOMPARE(evaluate<int>(dropArea, "enterEvents"), 1);
+ QCOMPARE(evaluate<int>(dropArea, "exitEvents"), 0);
+}
+
+
void tst_QQuickDropArea::simultaneousDrags()
{
QQuickWindow window;
diff --git a/tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml b/tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml
new file mode 100644
index 0000000000..b42fbc1adb
--- /dev/null
+++ b/tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml
@@ -0,0 +1,24 @@
+import QtQuick
+import QtQuick.Shapes
+import QtQuick.Controls
+
+Item {
+ id: root
+ width: 500
+ height: 500
+ Flickable {
+ anchors.centerIn: parent
+ width: 100
+ height: 100
+ clip: true
+ contentWidth: content.width
+ contentHeight: content.height
+ Rectangle {
+ id: content
+ width: 320
+ height: width
+ color: "#41cd52"
+ radius: width/2
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickflickable/data/fractionalExtent.qml b/tests/auto/quick/qquickflickable/data/fractionalExtent.qml
new file mode 100644
index 0000000000..2675e8c85a
--- /dev/null
+++ b/tests/auto/quick/qquickflickable/data/fractionalExtent.qml
@@ -0,0 +1,14 @@
+import QtQuick
+
+Flickable {
+ width: 300
+ height: 300
+ contentWidth: content.width; contentHeight: content.height
+ Rectangle {
+ id: content
+ width: 350
+ height: 350
+ color: "darkkhaki"
+ }
+ boundsBehavior: Flickable.StopAtBounds
+}
diff --git a/tests/auto/quick/qquickflickable/data/nestedmouseareapce.qml b/tests/auto/quick/qquickflickable/data/nestedmouseareapce.qml
new file mode 100644
index 0000000000..b36df04d45
--- /dev/null
+++ b/tests/auto/quick/qquickflickable/data/nestedmouseareapce.qml
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+
+Flickable {
+ id: outer
+ objectName: "flickable"
+ width: 400
+ height: 400
+ contentX: 50
+ contentY: 50
+ contentWidth: 500
+ contentHeight: 500
+
+ Rectangle {
+ objectName: "nested"
+ x: 100
+ y: 100
+ width: 300
+ height: 300
+
+ color: "yellow"
+ MouseArea {
+ anchors.fill: parent
+ propagateComposedEvents: true
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
index 9682079e84..ac8e7550bc 100644
--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
@@ -27,10 +27,11 @@
****************************************************************************/
#include <qtest.h>
#include <QtTest/QSignalSpy>
+#include <QtQuick/qquickview.h>
+#include <QtQuickTest/QtQuickTest>
#include <QtGui/QStyleHints>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
-#include <QtQuick/qquickview.h>
#include <private/qquickflickable_p.h>
#include <private/qquickflickable_p_p.h>
#include <private/qquickmousearea_p.h>
@@ -217,6 +218,7 @@ private slots:
void stopAtBounds();
void stopAtBounds_data();
void nestedMouseAreaUsingTouch();
+ void nestedMouseAreaPropagateComposedEvents();
void nestedSliderUsingTouch();
void nestedSliderUsingTouch_data();
void pressDelayWithLoader();
@@ -238,6 +240,10 @@ private slots:
void receiveTapOutsideContentItem();
void flickWhenRotated_data();
void flickWhenRotated();
+ void scrollingWithFractionalExtentSize_data();
+ void scrollingWithFractionalExtentSize();
+ void setContentPositionWhileDragging_data();
+ void setContentPositionWhileDragging();
private:
void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to);
@@ -2123,6 +2129,27 @@ void tst_qquickflickable::nestedMouseAreaUsingTouch()
QVERIFY(nested->y() < 100.0);
}
+void tst_qquickflickable::nestedMouseAreaPropagateComposedEvents()
+{
+ QScopedPointer<QQuickView> window(new QQuickView);
+ window->setSource(testFileUrl("nestedmouseareapce.qml"));
+ QTRY_COMPARE(window->status(), QQuickView::Ready);
+ QQuickViewTestUtils::centerOnScreen(window.data());
+ QQuickViewTestUtils::moveMouseAway(window.data());
+ window->show();
+ QVERIFY(window->rootObject() != nullptr);
+ QVERIFY(QTest::qWaitForWindowActive(window.data()));
+
+ QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject());
+ QVERIFY(flickable != nullptr);
+
+ QCOMPARE(flickable->contentY(), 50.0f);
+ flickWithTouch(window.data(), QPoint(100, 300), QPoint(100, 200));
+
+ // flickable should have moved
+ QVERIFY(!qFuzzyCompare(flickable->contentY(), 50.0));
+}
+
void tst_qquickflickable::nestedSliderUsingTouch_data()
{
QTest::addColumn<bool>("keepMouseGrab");
@@ -2815,7 +2842,7 @@ void tst_qquickflickable::flickWhenRotated_data()
for (const auto fr : rotations) {
if (pr <= 90) {
for (const auto s : scales)
- QTest::addRow("parent: %g, flickable: %g, scale: %g", pr, fr) << pr << fr << s;
+ QTest::addRow("parent: %g, flickable: %g, scale: %g", pr, fr, s) << pr << fr << s;
} else {
// don't bother trying every scale with every set of rotations, to save time
QTest::addRow("parent: %g, flickable: %g, scale: 1", pr, fr) << pr << fr << qreal(1);
@@ -2853,6 +2880,194 @@ void tst_qquickflickable::flickWhenRotated() // QTBUG-99639
QVERIFY(!flickable->isAtYBeginning());
}
+
+void tst_qquickflickable::scrollingWithFractionalExtentSize_data()
+{
+ QTest::addColumn<QByteArray>("property");
+ QTest::addColumn<bool>("isYAxis");
+ QTest::addColumn<bool>("atBeginning");
+ QTest::addColumn<QQuickFlickable::BoundsBehaviorFlag>("boundsBehaviour");
+
+ QTest::newRow("minyextent") << QByteArray("topMargin") << true << true << QQuickFlickable::StopAtBounds;
+ QTest::newRow("maxyextent") << QByteArray("bottomMargin") << true << false << QQuickFlickable::StopAtBounds;
+ QTest::newRow("minxextent") << QByteArray("leftMargin") << false << true << QQuickFlickable::StopAtBounds;
+ QTest::newRow("maxxextent") << QByteArray("rightMargin") << false << false << QQuickFlickable::StopAtBounds;
+
+ QTest::newRow("minyextent") << QByteArray("topMargin") << true << true << QQuickFlickable::DragAndOvershootBounds;
+ QTest::newRow("maxyextent") << QByteArray("bottomMargin") << true << false << QQuickFlickable::DragAndOvershootBounds;
+ QTest::newRow("minxextent") << QByteArray("leftMargin") << false << true << QQuickFlickable::DragAndOvershootBounds;
+ QTest::newRow("maxxextent") << QByteArray("rightMargin") << false << false << QQuickFlickable::DragAndOvershootBounds;
+}
+
+void tst_qquickflickable::scrollingWithFractionalExtentSize() // QTBUG-101268
+{
+ QFETCH(QByteArray, property);
+ QFETCH(bool, isYAxis);
+ QFETCH(bool, atBeginning);
+ QFETCH(QQuickFlickable::BoundsBehaviorFlag, boundsBehaviour);
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("fractionalExtent.qml")));
+ QQuickItem *rootItem = window.rootObject();
+ QVERIFY(rootItem);
+ QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(rootItem);
+ QVERIFY(flickable);
+ flickable->setBoundsBehavior(boundsBehaviour);
+
+ qreal value = 100.5;
+ flickable->setProperty(property.constData(), 100.5);
+ if (isYAxis)
+ flickable->setContentY(atBeginning ? -value : value + qAbs(flickable->height() - flickable->contentHeight()));
+ else
+ flickable->setContentX(atBeginning ? -value : value + qAbs(flickable->width() - flickable->contentWidth()));
+
+ if (isYAxis) {
+ QVERIFY(atBeginning ? flickable->isAtYBeginning() : flickable->isAtYEnd());
+ QVERIFY(!flickable->isMovingVertically());
+ } else {
+ QVERIFY(atBeginning ? flickable->isAtXBeginning() : flickable->isAtXEnd());
+ QVERIFY(!flickable->isMovingHorizontally());
+ }
+
+ QPointF pos(flickable->x() + flickable->width() / 2, flickable->y() + flickable->height() / 2);
+ QPoint angleDelta(isYAxis ? 0 : (atBeginning ? -50 : 50), isYAxis ? (atBeginning ? -50 : 50) : 0);
+
+ QWheelEvent wheelEvent1(pos, window.mapToGlobal(pos), QPoint(), angleDelta,
+ Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
+
+ QGuiApplication::sendEvent(&window, &wheelEvent1);
+ qApp->processEvents();
+ if (isYAxis) {
+ QVERIFY(flickable->isMovingVertically());
+ QTRY_VERIFY(!flickable->isMovingVertically());
+ QVERIFY(!(atBeginning ? flickable->isAtYBeginning() : flickable->isAtYEnd()));
+ } else {
+ QVERIFY(flickable->isMovingHorizontally());
+ QTRY_VERIFY(!flickable->isMovingHorizontally());
+ QVERIFY(!(atBeginning ? flickable->isAtXBeginning() : flickable->isAtXEnd()));
+ }
+
+ QWheelEvent wheelEvent2(pos, window.mapToGlobal(pos), QPoint(), -angleDelta,
+ Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
+ wheelEvent2.setTimestamp(wheelEvent1.timestamp() + 10);
+
+ QGuiApplication::sendEvent(&window, &wheelEvent2);
+ qApp->processEvents();
+
+ if (isYAxis) {
+ QVERIFY(flickable->isMovingVertically());
+ QTRY_VERIFY(!flickable->isMovingVertically());
+ QVERIFY(atBeginning ? flickable->isAtYBeginning() : flickable->isAtYEnd());
+ } else {
+ QVERIFY(flickable->isMovingHorizontally());
+ QTRY_VERIFY(!flickable->isMovingHorizontally());
+ QVERIFY(atBeginning ? flickable->isAtXBeginning() : flickable->isAtXEnd());
+ }
+}
+
+void tst_qquickflickable::setContentPositionWhileDragging_data()
+{
+ QTest::addColumn<bool>("isHorizontal");
+ QTest::addColumn<int>("newPos");
+ QTest::addColumn<int>("newExtent");
+ QTest::newRow("horizontal, setContentX") << true << 0 << -1;
+ QTest::newRow("vertical, setContentY") << false << 0 << -1;
+ QTest::newRow("horizontal, setContentWidth") << true << -1 << 200;
+ QTest::newRow("vertical, setContentHeight") << false << -1 << 200;
+}
+
+void tst_qquickflickable::setContentPositionWhileDragging() // QTBUG-104966
+{
+ QFETCH(bool, isHorizontal);
+ QFETCH(int, newPos);
+ QFETCH(int, newExtent);
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("contentPosWhileDragging.qml")));
+ QQuickViewTestUtils::centerOnScreen(&window);
+ QVERIFY(window.isVisible());
+ QQuickItem *rootItem = window.rootObject();
+ QVERIFY(rootItem);
+ QQuickFlickable *flickable = rootItem->findChild<QQuickFlickable *>();
+ QVERIFY(flickable);
+
+ const auto contentPos = [flickable]() -> QPoint {
+ return QPoint(flickable->contentX(), flickable->contentY());
+ };
+ const qreal threshold =
+ qApp->styleHints()->startDragDistance() * flickable->parentItem()->scale();
+ const QPoint thresholdPnt(qRound(threshold), qRound(threshold));
+ const auto flickableCenterPos = flickable->mapToScene({flickable->width() / 2, flickable->height() / 2}).toPoint();
+
+ // Drag the mouse until we have surpassed the mouse drag threshold and a drag is initiated
+ // by checking for flickable->isDragging()
+ QPoint pos = flickableCenterPos;
+ QQuickViewTestUtils::moveAndPress(&window, pos);
+ int j = 1;
+ QVERIFY(!flickable->isDragging());
+ while (!flickable->isDragging()) {
+ pos = flickableCenterPos - QPoint(j, j);
+ QTest::mouseMove(&window, pos);
+ j++;
+ }
+
+ // Now we have entered the drag state
+ QVERIFY(flickable->isDragging());
+ QCOMPARE(flickable->contentX(), 0);
+ QCOMPARE(flickable->contentY(), 0);
+ QVERIFY(flickable->width() > 0);
+ QVERIFY(flickable->height() > 0);
+
+
+ const int moveLength = 50;
+ const QPoint unitDelta(isHorizontal ? 1 : 0, isHorizontal ? 0 : 1);
+ const QPoint moveDelta = unitDelta * moveLength;
+
+ pos -= 3*moveDelta;
+ QTest::mouseMove(&window, pos);
+ // Should be positive because we drag in the opposite direction
+ QCOMPARE(contentPos(), 3 * moveDelta);
+ QPoint expectedContentPos;
+
+ // Set the content item position back to zero *while dragging* (!!)
+ if (newPos >= 0) {
+ if (isHorizontal) {
+ flickable->setContentX(newPos);
+ } else {
+ flickable->setContentY(newPos);
+ }
+ // Continue dragging
+ pos -= moveDelta;
+ expectedContentPos = moveDelta;
+ } else if (newExtent >= 0) {
+ // ...or reduce the content size be be less than current (contentX, contentY) position
+ // This forces the content item to move.
+ // contentY: 150
+ // 320 - 150 = 170 pixels down to bottom
+ // Now reduce contentHeight to 200
+ // since we are at the bottom, and the flickable is 100 pixels tall, contentY must land
+ // at newExtent - 100.
+
+ if (isHorizontal) {
+ flickable->setContentWidth(newExtent);
+ } else {
+ flickable->setContentHeight(newExtent);
+ }
+ // Assumption is that the contentItem is aligned to the bottom of the flickable
+ // We therefore cannot scroll/flick it further down. Drag it up towards the top instead
+ // (by moving mouse down).
+ pos += moveDelta;
+ expectedContentPos = unitDelta * (newExtent - (isHorizontal ? flickable->width() : flickable->height()));
+ }
+
+ QTest::mouseMove(&window, pos);
+
+ // Make sure that the contentItem was only dragged the delta in mouse movement since the last
+ // setContentX/Y() call.
+ QCOMPARE(contentPos(), expectedContentPos);
+ QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, pos);
+ QVERIFY(!flickable->isDragging());
+}
+
QTEST_MAIN(tst_qquickflickable)
#include "tst_qquickflickable.moc"
diff --git a/tests/auto/quick/qquickfontloader/tst_qquickfontloader.cpp b/tests/auto/quick/qquickfontloader/tst_qquickfontloader.cpp
index 9983997dc1..8bfeb7314a 100644
--- a/tests/auto/quick/qquickfontloader/tst_qquickfontloader.cpp
+++ b/tests/auto/quick/qquickfontloader/tst_qquickfontloader.cpp
@@ -200,9 +200,7 @@ void tst_qquickfontloader::changeFontSourceViaState()
{
QQuickView window(testFileUrl("qtbug-20268.qml"));
window.show();
- window.requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(&window));
- QCOMPARE(&window, qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
QQuickFontLoader *fontObject = qobject_cast<QQuickFontLoader*>(qvariant_cast<QObject *>(window.rootObject()->property("fontloader")));
QVERIFY(fontObject != nullptr);
diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
index 9532aa546c..942c7cfd93 100644
--- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
+++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
@@ -67,7 +67,7 @@ public:
tst_QQuickGridView();
private slots:
- void init();
+ void init() override;
void cleanupTestCase();
void items();
void changed();
@@ -328,6 +328,8 @@ tst_QQuickGridView::tst_QQuickGridView()
void tst_QQuickGridView::init()
{
+ QQmlDataTest::init();
+
#ifdef SHARE_VIEWS
if (m_view && QString(QTest::currentTestFunction()) != testForView) {
testForView = QString();
diff --git a/tests/auto/quick/qquickitemlayer/data/itemImageLayer.qml b/tests/auto/quick/qquickitemlayer/data/itemImageLayer.qml
new file mode 100644
index 0000000000..1f05aa882f
--- /dev/null
+++ b/tests/auto/quick/qquickitemlayer/data/itemImageLayer.qml
@@ -0,0 +1,14 @@
+import QtQuick
+
+Item {
+ width: 300
+ height: 300
+
+ Image {
+ objectName: "image"
+ anchors.fill: parent
+ fillMode: Image.PreserveAspectFit
+ source: "qt-logo.png"
+ layer.enabled: true
+ }
+}
diff --git a/tests/auto/quick/qquickitemlayer/data/qt-logo.png b/tests/auto/quick/qquickitemlayer/data/qt-logo.png
new file mode 100644
index 0000000000..dff7729515
--- /dev/null
+++ b/tests/auto/quick/qquickitemlayer/data/qt-logo.png
Binary files differ
diff --git a/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp b/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp
index ae6947f01b..90e1ab0a66 100644
--- a/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp
+++ b/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp
@@ -31,10 +31,12 @@
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/qsgrendererinterface.h>
+#include <QtQuick/private/qquickitem_p.h>
#include <qopenglcontext.h>
#include <qopenglfunctions.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQuickTestUtils/private/viewtestutils_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformintegration.h>
@@ -85,6 +87,8 @@ private slots:
void textureMirroring_data();
void textureMirroring();
+ void effectSourceResizeToItem();
+
private:
void mirroringCheck(int mirroring, int x, bool shouldMirror, const QImage &fb);
};
@@ -468,6 +472,25 @@ void tst_QQuickItemLayer::textureMirroring()
mirroringCheck(mirroring, 200, true, fb);
}
+void tst_QQuickItemLayer::effectSourceResizeToItem() // QTBUG-104442
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("itemImageLayer.qml")));
+ window.setResizeMode(QQuickView::SizeRootObjectToView);
+ QQuickItem *image = window.rootObject()->findChild<QQuickItem*>("image");
+ QVERIFY(image);
+ QCOMPARE(image->size(), window.rootObject()->size());
+ QQuickItemLayer *layer = QQuickItemPrivate::get(image)->layer();
+ QVERIFY(layer);
+ auto *effectSource = layer->effectSource();
+ QVERIFY(effectSource);
+ QCOMPARE(effectSource->size(), image->size());
+
+ window.resize(200, 200); // shrink it a bit
+ QTRY_COMPARE(image->size().toSize(), QSize(200, 200)); // wait for the window system
+ QCOMPARE(effectSource->size(), image->size());
+}
+
void tst_QQuickItemLayer::mirroringCheck(int mirroring, int x, bool shouldMirror, const QImage &fb)
{
int offset = 10;
diff --git a/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml
index 1df04a11e8..52ad3b478d 100644
--- a/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml
+++ b/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml
@@ -586,6 +586,56 @@ Item {
tryCompare(layout.children[4], "y", 60);
}
+ Component {
+ id: layout_alignBaseline_Component
+ GridLayout {
+ columns: 2
+ columnSpacing: 0
+ rowSpacing: 0
+ TextInput {
+ property var itemRect: [x, y, width, height]
+ text: "red"
+ baselineOffset: 7
+ color: "red"
+ verticalAlignment: TextInput.AlignVCenter
+ Layout.preferredWidth: 50
+ Layout.preferredHeight: 10
+ Layout.fillHeight: true
+ }
+ TextInput {
+ property var itemRect: [x, y, width, height]
+ text: "green"
+ baselineOffset: 7
+ color: "green"
+ verticalAlignment: TextInput.AlignVCenter
+ Layout.preferredWidth: 50
+ Layout.preferredHeight: 10
+ Layout.fillHeight: true
+ }
+
+ }
+ }
+
+ function test_alignBaseline_dont_always_invalidate()
+ {
+ var layout = createTemporaryObject(layout_alignBaseline_Component, container);
+ waitForItemPolished(layout)
+ layout.height = 20
+ // Adjusting height on an item that uses Qt.AlignBaseline might adjust the baseline
+ // Test if we don't get excessive number of polish() events because of baseline changes
+ // (In this case, we don't want to align by the baseline)
+ compare(isPolishScheduled(layout), false)
+ waitForItemPolished(layout)
+ var c0 = layout.children[0]
+ c0.Layout.alignment = Qt.AlignBaseline
+ var c1 = layout.children[1]
+ c1.Layout.alignment = Qt.AlignBaseline
+
+ // We want to align by baseline => expect a polish event
+ compare(isPolishScheduled(layout), true)
+ waitForItemPolished(layout)
+ }
+
Component {
id: layout_rightToLeft_Component
diff --git a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml
index c567b31db3..552bfb6065 100644
--- a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml
+++ b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml
@@ -272,5 +272,55 @@ Item {
compare(layout.item2.isCurrentItem, false)
compare(layout.item2.layout, layout)
}
+
+ Component {
+ id: layout_setCurrentIndex_Component
+
+ StackLayout {
+ width: 200
+ height: 200
+
+ property alias firstItem : rect
+ property alias secondItem: rowLayout
+
+ Rectangle {
+ id: rect
+ color: "red"
+ implicitWidth: 10
+ implicitHeight: 10
+ }
+ RowLayout {
+ id: rowLayout
+ spacing: 0
+ Rectangle {
+ color: "green"
+ implicitWidth: 10
+ implicitHeight: 10
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ Rectangle {
+ color: "blue"
+ implicitWidth: 10
+ implicitHeight: 10
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+ }
+ }
+
+ function test_setCurrentIndex()
+ {
+ var layout = layout_setCurrentIndex_Component.createObject(container)
+ compare(layout.firstItem.width, 200)
+
+ // Invalidate the StackLayout (and its cached size hints)
+ layout.firstItem.implicitWidth = 42
+
+ layout.currentIndex = 1
+ compare(layout.secondItem.width, 200) // width should not be -1
+ layout.destroy()
+ }
}
}
diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST
index 3c5d5ee25b..9b30dd2bc0 100644
--- a/tests/auto/quick/qquicklistview/BLACKLIST
+++ b/tests/auto/quick/qquicklistview/BLACKLIST
@@ -4,12 +4,6 @@ opensuse-leap
#QTBUG-53863
[populateTransitions]
opensuse-42.1
-#QTBUG-75960
-#QTBUG-76652
-[currentIndex]
-opensuse-leap
-ubuntu-18.04
-ubuntu-20.04
# QTBUG-75202
[contentHeightWithDelayRemove]
macos ci
diff --git a/tests/auto/quick/qquicklistview/data/appendDuringScrollDown.qml b/tests/auto/quick/qquicklistview/data/appendDuringScrollDown.qml
index af35c29143..6ba6480297 100644
--- a/tests/auto/quick/qquicklistview/data/appendDuringScrollDown.qml
+++ b/tests/auto/quick/qquicklistview/data/appendDuringScrollDown.qml
@@ -1,10 +1,11 @@
import QtQuick 2.6
ListView {
+ id: listView
width: 320; height: 240
focus: true
delegate: Text {
- height: 40; width: parent.width
+ height: 40; width: listView.width
text: model.text
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
diff --git a/tests/auto/quick/qquicklistview/data/displayMargin.qml b/tests/auto/quick/qquicklistview/data/displayMargin.qml
index aafbb4235f..e6ea695b49 100644
--- a/tests/auto/quick/qquicklistview/data/displayMargin.qml
+++ b/tests/auto/quick/qquicklistview/data/displayMargin.qml
@@ -44,7 +44,7 @@ Item {
model: 100
delegate: Rectangle {
objectName: "delegate"
- width: parent.width
+ width: view.width
height: 25
color: index % 2 ? "steelblue" : "lightsteelblue"
Text {
diff --git a/tests/auto/quick/qquicklistview/data/headerCrash.qml b/tests/auto/quick/qquicklistview/data/headerCrash.qml
index 124fa894f2..972ecb2906 100644
--- a/tests/auto/quick/qquicklistview/data/headerCrash.qml
+++ b/tests/auto/quick/qquicklistview/data/headerCrash.qml
@@ -12,7 +12,7 @@ ListView {
}
delegate: Rectangle {
- width: parent.width; height: 20
+ width: myList.width; height: 20
color: index % 2 ? "green" : "red"
}
diff --git a/tests/auto/quick/qquicklistview/data/listview-itematindex.qml b/tests/auto/quick/qquicklistview/data/listview-itematindex.qml
index fba8b11933..2194f1edff 100644
--- a/tests/auto/quick/qquicklistview/data/listview-itematindex.qml
+++ b/tests/auto/quick/qquicklistview/data/listview-itematindex.qml
@@ -1,13 +1,14 @@
import QtQuick 2.0
ListView {
+ id: listView
width: 400
height: 400
focus: true
model: 3
delegate: Text {
- width: parent.width
+ width: listView.width
height: 10
property int idx: index
text: index
diff --git a/tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml b/tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml
index e0acaf49e4..ef959b10b4 100644
--- a/tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml
+++ b/tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml
@@ -72,7 +72,7 @@ Item {
ListElement { size: 300; }
}
delegate: Rectangle {
- width: parent.width
+ width: list.width
color: index % 2 == 0 ? "red" : "blue"
height: size
Text { anchors.centerIn: parent; text: index }
diff --git a/tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml b/tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml
index 851d8f9a0c..f5be95b7cc 100644
--- a/tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml
+++ b/tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml
@@ -56,7 +56,7 @@ ListView {
anchors.fill: parent
model: 10
delegate: Rectangle {
- width: parent.width
+ width: listView.width
height: 40
border.color: "lightsteelblue"
Text {
diff --git a/tests/auto/quick/qquicklistview/data/sectionSnapping.qml b/tests/auto/quick/qquicklistview/data/sectionSnapping.qml
index 2583cc0377..48a893f88c 100644
--- a/tests/auto/quick/qquicklistview/data/sectionSnapping.qml
+++ b/tests/auto/quick/qquicklistview/data/sectionSnapping.qml
@@ -1,6 +1,7 @@
import QtQuick 2.0
ListView {
+ id: listView
width: 400
height: 400
preferredHighlightBegin: 100
@@ -17,7 +18,7 @@ ListView {
}
delegate: Rectangle {
- width: parent.width
+ width: listView.width
height: 50
color: index % 2 ? "lightsteelblue" : "steelblue"
Text {
diff --git a/tests/auto/quick/qquicklistview/data/snapOneItemWrongDirection.qml b/tests/auto/quick/qquicklistview/data/snapOneItemWrongDirection.qml
index f5b7b35d0c..1dfbfe0feb 100644
--- a/tests/auto/quick/qquicklistview/data/snapOneItemWrongDirection.qml
+++ b/tests/auto/quick/qquicklistview/data/snapOneItemWrongDirection.qml
@@ -1,13 +1,14 @@
import QtQuick 2.0
ListView {
+ id: listView
width: 400
height: 400
focus: true
model: 10
delegate: Rectangle {
- width: parent.width
+ width: listView.width
height: 50
color: index % 2 ? "blue" : "green"
}
diff --git a/tests/auto/quick/qquicklistview/data/strictlyenforcerange-resize.qml b/tests/auto/quick/qquicklistview/data/strictlyenforcerange-resize.qml
index 338af38475..16b9c72b16 100644
--- a/tests/auto/quick/qquicklistview/data/strictlyenforcerange-resize.qml
+++ b/tests/auto/quick/qquicklistview/data/strictlyenforcerange-resize.qml
@@ -1,6 +1,7 @@
import QtQuick 2.0
ListView {
+ id: listView
width: 400
height: 400
focus: true
@@ -15,7 +16,7 @@ ListView {
model: 10
delegate: Item {
- width: parent.width
+ width: listView.width
height: ListView.isCurrentItem ? 100 : 50
Text {
diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
index 6975fa2dbd..23738f13c7 100644
--- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
@@ -77,7 +77,7 @@ public:
private slots:
// WARNING: please add new tests to tst_qquicklistview2; this file is too slow to work with.
- void init();
+ void init() override;
void cleanupTestCase();
// Test QAbstractItemModel model types
void qAbstractItemModel_package_items();
@@ -410,6 +410,8 @@ tst_QQuickListView::tst_QQuickListView()
void tst_QQuickListView::init()
{
+ QQmlDataTest::init();
+
#ifdef SHARE_VIEWS
if (m_view && QString(QTest::currentTestFunction()) != testForView) {
testForView = QString();
@@ -434,6 +436,9 @@ void tst_QQuickListView::cleanupTestCase()
template <class T>
void tst_QQuickListView::items(const QUrl &source)
{
+ // Make sure we outlive the view, or the context property will become null.
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
T model;
@@ -444,7 +449,6 @@ void tst_QQuickListView::items(const QUrl &source)
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(source);
@@ -515,6 +519,8 @@ void tst_QQuickListView::items(const QUrl &source)
template <class T>
void tst_QQuickListView::changed(const QUrl &source)
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
T model;
@@ -525,7 +531,6 @@ void tst_QQuickListView::changed(const QUrl &source)
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(source);
@@ -553,6 +558,8 @@ void tst_QQuickListView::changed(const QUrl &source)
template <class T>
void tst_QQuickListView::inserted(const QUrl &source)
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
@@ -565,7 +572,6 @@ void tst_QQuickListView::inserted(const QUrl &source)
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(source);
@@ -657,11 +663,12 @@ void tst_QQuickListView::inserted_more(QQuickItemView::VerticalLayoutDirection v
for (int i = 0; i < 30; i++)
model.addItem("Item" + QString::number(i), "");
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -958,6 +965,8 @@ void tst_QQuickListView::insertBeforeVisible_data()
template <class T>
void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
T model;
@@ -967,7 +976,6 @@ void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(source);
@@ -1321,6 +1329,8 @@ void tst_QQuickListView::removed_more_data()
template <class T>
void tst_QQuickListView::clear(const QUrl &source, QQuickItemView::VerticalLayoutDirection verticalLayoutDirection)
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
T model;
@@ -1330,7 +1340,6 @@ void tst_QQuickListView::clear(const QUrl &source, QQuickItemView::VerticalLayou
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(source);
@@ -1853,6 +1862,8 @@ void tst_QQuickListView::multipleChanges_data()
void tst_QQuickListView::swapWithFirstItem()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
QaimModel model;
@@ -1862,7 +1873,6 @@ void tst_QQuickListView::swapWithFirstItem()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -1881,6 +1891,8 @@ void tst_QQuickListView::swapWithFirstItem()
void tst_QQuickListView::checkCountForMultiColumnModels()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
// Check that a list view will only load items for the first
// column, even if the model reports that it got several columns.
// We test this since QQmlDelegateModel has been changed to
@@ -1898,7 +1910,6 @@ void tst_QQuickListView::checkCountForMultiColumnModels()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -2035,6 +2046,8 @@ void tst_QQuickListView::enforceRange_withoutHighlight()
void tst_QQuickListView::spacing()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
QaimModel model;
@@ -2044,7 +2057,6 @@ void tst_QQuickListView::spacing()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -2965,8 +2977,8 @@ void tst_QQuickListView::keyNavigation()
for (int i = 0; i < 30; i++)
model.addItem("Item" + QString::number(i), "");
- QQuickView *window = getView();
QScopedPointer<TestObject> testObject(new TestObject);
+ QQuickView *window = getView();
window->rootContext()->setContextProperty("testModel", &model);
window->rootContext()->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -3170,6 +3182,8 @@ void tst_QQuickListView::itemListFlicker()
void tst_QQuickListView::cacheBuffer()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
QaimModel model;
@@ -3179,7 +3193,6 @@ void tst_QQuickListView::cacheBuffer()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -3272,6 +3285,8 @@ void tst_QQuickListView::cacheBuffer()
void tst_QQuickListView::positionViewAtBeginningEnd()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
QaimModel model;
@@ -3281,7 +3296,6 @@ void tst_QQuickListView::positionViewAtBeginningEnd()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->show();
window->setSource(testFileUrl("listviewtest.qml"));
@@ -3330,6 +3344,8 @@ void tst_QQuickListView::positionViewAtIndex()
QFETCH(QQuickListView::PositionMode, mode);
QFETCH(qreal, contentY);
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QQuickView *window = getView();
QaimModel model;
@@ -3339,7 +3355,6 @@ void tst_QQuickListView::positionViewAtIndex()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->show();
window->setSource(testFileUrl("listviewtest.qml"));
@@ -3679,6 +3694,8 @@ void tst_QQuickListView::manualHighlight()
void tst_QQuickListView::QTBUG_11105()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
QaimModel model;
for (int i = 0; i < 30; i++)
@@ -3687,7 +3704,6 @@ void tst_QQuickListView::QTBUG_11105()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -4349,6 +4365,8 @@ void tst_QQuickListView::resetModel_headerFooter()
void tst_QQuickListView::resizeView()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
QaimModel model;
for (int i = 0; i < 40; i++)
@@ -4357,7 +4375,6 @@ void tst_QQuickListView::resizeView()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -4458,6 +4475,8 @@ void tst_QQuickListView::resizeViewAndRepaint()
void tst_QQuickListView::sizeLessThan1()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
QaimModel model;
@@ -4467,7 +4486,6 @@ void tst_QQuickListView::sizeLessThan1()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("sizelessthan1.qml"));
@@ -4588,6 +4606,8 @@ void tst_QQuickListView::resizeFirstDelegate()
{
// QTBUG-20712: Content Y jumps constantly if first delegate height == 0
// and other delegates have height > 0
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
// bug only occurs when all items in the model are visible
@@ -4598,7 +4618,6 @@ void tst_QQuickListView::resizeFirstDelegate()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -4796,6 +4815,8 @@ void tst_QQuickListView::indexAt_itemAt()
QFETCH(qreal, y);
QFETCH(int, index);
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QQuickView *window = getView();
QaimModel model;
@@ -4805,7 +4826,6 @@ void tst_QQuickListView::indexAt_itemAt()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -6673,8 +6693,8 @@ void tst_QQuickListView::populateTransitions()
model.addItem("item" + QString::number(i), "");
}
+ QScopedPointer<TestObject> testObject(new TestObject());
QQuickView *window = getView();
- QScopedPointer<TestObject> testObject(new TestObject(window->rootContext()));
window->rootContext()->setContextProperty("testModel", &model);
window->rootContext()->setContextProperty("testObject", testObject.data());
window->rootContext()->setContextProperty("usePopulateTransition", usePopulateTransition);
@@ -6797,12 +6817,12 @@ void tst_QQuickListView::populateTransitions_data()
void tst_QQuickListView::sizeTransitions()
{
QFETCH(bool, topToBottom);
+ QScopedPointer<TestObject> testObject(new TestObject);
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
QaimModel model;
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("topToBottom", topToBottom);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", &model);
window->setSource(testFileUrl("sizeTransitions.qml"));
window->show();
@@ -6857,9 +6877,9 @@ void tst_QQuickListView::addTransitions()
QaimModel model_targetItems_transitionFrom;
QaimModel model_displacedItems_transitionVia;
+ QScopedPointer<TestObject> testObject(new TestObject);
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("model_targetItems_transitionFrom", &model_targetItems_transitionFrom);
ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
@@ -7052,9 +7072,9 @@ void tst_QQuickListView::moveTransitions()
QaimModel model_targetItems_transitionVia;
QaimModel model_displacedItems_transitionVia;
+ QScopedPointer<TestObject> testObject(new TestObject);
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("model_targetItems_transitionVia", &model_targetItems_transitionVia);
ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
@@ -7254,9 +7274,9 @@ void tst_QQuickListView::removeTransitions()
QaimModel model_targetItems_transitionTo;
QaimModel model_displacedItems_transitionVia;
+ QScopedPointer<TestObject> testObject(new TestObject);
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("model_targetItems_transitionTo", &model_targetItems_transitionTo);
ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
@@ -7452,9 +7472,9 @@ void tst_QQuickListView::displacedTransitions()
QPointF moveDisplaced_transitionVia(50, -100);
QPointF removeDisplaced_transitionVia(150, 100);
+ QScopedPointer<TestObject> testObject(new TestObject());
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
- QScopedPointer<TestObject> testObject(new TestObject(window));
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("testObject", testObject.data());
ctxt->setContextProperty("model_displaced_transitionVia", &model_displaced_transitionVia);
@@ -7678,9 +7698,9 @@ void tst_QQuickListView::multipleTransitions()
for (int i = 0; i < initialCount; i++)
model.addItem("Original item" + QString::number(i), "");
+ QScopedPointer<TestObject> testObject(new TestObject);
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("testObject", testObject.data());
ctxt->setContextProperty("addTargets_transitionFrom", addTargets_transitionFrom);
@@ -7845,9 +7865,9 @@ void tst_QQuickListView::multipleDisplaced()
for (int i = 0; i < 30; i++)
model.addItem("Original item" + QString::number(i), "");
+ QScopedPointer<TestObject> testObject(new TestObject());
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
- QScopedPointer<TestObject> testObject(new TestObject(window));
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("multipleDisplaced.qml"));
@@ -9466,7 +9486,7 @@ void tst_QQuickListView::QTBUG_66163_setModelViewPortSizeChange()
delegate: Rectangle {
color: index % 2 ? "green" : "orange"
- width: parent.width
+ width: view.width
height: 50
}
@@ -9518,8 +9538,8 @@ void tst_QQuickListView::itemFiltered()
QScopedPointer<QQuickView> window(createView());
window->engine()->rootContext()->setContextProperty("_model", &proxy2);
QQmlComponent component(window->engine());
- component.setData("import QtQuick 2.4; ListView { "
- "anchors.fill: parent; model: _model; delegate: Text { width: parent.width;"
+ component.setData("import QtQuick 2.4; ListView { id: listView; "
+ "anchors.fill: parent; model: _model; delegate: Text { width: listView.width;"
"text: model.display; } }",
QUrl());
window->setContent(QUrl(), &component, component.create());
@@ -9802,12 +9822,24 @@ void tst_QQuickListView::delegateWithRequiredProperties()
void tst_QQuickListView::reuse_reuseIsOffByDefault()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
// Check that delegate recycling is off by default. The reason is that
// ListView needs to be backwards compatible with legacy applications. And
// when using delegate recycling, there are certain differences, like that
// a delegates Component.onCompleted will just be called the first time the
// item is created, and not when it's reused.
QScopedPointer<QQuickView> window(createView());
+
+ QaimModel model;
+ for (int i = 0; i < 40; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QQmlContext *ctxt = window->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ ctxt->setContextProperty("testObject", testObject.data());
+
window->setSource(testFileUrl("listviewtest.qml"));
window->resize(640, 480);
window->show();
diff --git a/tests/auto/quick/qquicklistview2/BLACKLIST b/tests/auto/quick/qquicklistview2/BLACKLIST
new file mode 100644
index 0000000000..0162bbc852
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/BLACKLIST
@@ -0,0 +1,5 @@
+[tapDelegateDuringFlicking]
+android # QTBUG-104471
+[flickDuringFlicking]
+android # QTBUG-104471
+macos ci # QTBUG-105190
diff --git a/tests/auto/quick/qquicklistview2/data/buttonDelegate.qml b/tests/auto/quick/qquicklistview2/data/buttonDelegate.qml
new file mode 100644
index 0000000000..a40ba1cd7e
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/buttonDelegate.qml
@@ -0,0 +1,27 @@
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+
+ListView {
+ id: root
+ width: 320
+ height: 480
+ model: 100
+
+ property var pressedDelegates: []
+ property var releasedDelegates: []
+ property var tappedDelegates: []
+ property var canceledDelegates: []
+
+ delegate: Button {
+ required property int index
+ objectName: text
+ text: "button " + index
+ height: 100
+ width: 320
+
+ onPressed: root.pressedDelegates.push(index)
+ onReleased: root.releasedDelegates.push(index)
+ onClicked: root.tappedDelegates.push(index)
+ onCanceled: root.canceledDelegates.push(index)
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/data/mouseAreaDelegate.qml b/tests/auto/quick/qquicklistview2/data/mouseAreaDelegate.qml
new file mode 100644
index 0000000000..ad556913a5
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/mouseAreaDelegate.qml
@@ -0,0 +1,30 @@
+import QtQuick 2.15
+
+ListView {
+ id: root
+ width: 320
+ height: 480
+ model: 100
+
+ property var pressedDelegates: []
+ property var releasedDelegates: []
+ property var tappedDelegates: []
+ property var canceledDelegates: []
+
+ delegate: MouseArea {
+ height: 100
+ width: 320
+
+ onPressed: root.pressedDelegates.push(index)
+ onReleased: root.releasedDelegates.push(index)
+ onClicked: root.tappedDelegates.push(index)
+ onCanceled: root.canceledDelegates.push(index)
+
+ Rectangle {
+ id: buttonArea
+ anchors.fill: parent
+ border.color: "#41cd52"
+ color: parent.pressed ? "lightsteelblue" : "beige"
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
index 27bf389891..1905c0f660 100644
--- a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
+++ b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
@@ -38,6 +38,8 @@
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
+
using namespace QQuickViewTestUtils;
using namespace QQuickVisualTestUtils;
@@ -60,6 +62,14 @@ private slots:
void sectionsNoOverlap();
void metaSequenceAsModel();
void noCrashOnIndexChange();
+ void tapDelegateDuringFlicking_data();
+ void tapDelegateDuringFlicking();
+ void flickDuringFlicking_data();
+ void flickDuringFlicking();
+
+private:
+ void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to);
+ QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
};
tst_QQuickListView2::tst_QQuickListView2()
@@ -307,6 +317,149 @@ void tst_QQuickListView2::noCrashOnIndexChange()
QCOMPARE(items->property("count").toInt(), 4);
}
+void tst_QQuickListView2::tapDelegateDuringFlicking_data()
+{
+ QTest::addColumn<QByteArray>("qmlFile");
+ QTest::addColumn<QQuickFlickable::BoundsBehavior>("boundsBehavior");
+
+ QTest::newRow("Button StopAtBounds") << QByteArray("buttonDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds);
+ QTest::newRow("MouseArea StopAtBounds") << QByteArray("mouseAreaDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds);
+ QTest::newRow("Button DragOverBounds") << QByteArray("buttonDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds);
+ QTest::newRow("MouseArea DragOverBounds") << QByteArray("mouseAreaDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds);
+ QTest::newRow("Button OvershootBounds") << QByteArray("buttonDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds);
+ QTest::newRow("MouseArea OvershootBounds") << QByteArray("mouseAreaDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds);
+ QTest::newRow("Button DragAndOvershootBounds") << QByteArray("buttonDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds);
+ QTest::newRow("MouseArea DragAndOvershootBounds") << QByteArray("mouseAreaDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds);
+}
+
+void tst_QQuickListView2::tapDelegateDuringFlicking() // QTBUG-103832
+{
+ QFETCH(QByteArray, qmlFile);
+ QFETCH(QQuickFlickable::BoundsBehavior, boundsBehavior);
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl(qmlFile.constData())));
+ QQuickListView *listView = qobject_cast<QQuickListView*>(window.rootObject());
+ QVERIFY(listView);
+ listView->setBoundsBehavior(boundsBehavior);
+
+ flickWithTouch(&window, {100, 400}, {100, 100});
+ QTRY_VERIFY(listView->contentY() > 501); // let it flick some distance
+ QVERIFY(listView->isFlicking()); // we want to test the case when it's still moving while we tap
+ // @y = 400 we pressed the 4th delegate; started flicking, and the press was canceled
+ QCOMPARE(listView->property("pressedDelegates").toList().first(), 4);
+ QCOMPARE(listView->property("canceledDelegates").toList().first(), 4);
+
+ // press a delegate during flicking (at y > 501 + 100, so likely delegate 6)
+ QTest::touchEvent(&window, touchDevice.data()).press(0, {100, 100});
+ QQuickTouchUtils::flush(&window);
+ QTest::touchEvent(&window, touchDevice.data()).release(0, {100, 100});
+ QQuickTouchUtils::flush(&window);
+
+ const QVariantList pressedDelegates = listView->property("pressedDelegates").toList();
+ const QVariantList releasedDelegates = listView->property("releasedDelegates").toList();
+ const QVariantList tappedDelegates = listView->property("tappedDelegates").toList();
+ const QVariantList canceledDelegates = listView->property("canceledDelegates").toList();
+
+ qCDebug(lcTests) << "pressed" << pressedDelegates; // usually [4, 6]
+ qCDebug(lcTests) << "released" << releasedDelegates;
+ qCDebug(lcTests) << "tapped" << tappedDelegates;
+ qCDebug(lcTests) << "canceled" << canceledDelegates;
+
+ // which delegate received the second press, during flicking?
+ const int lastPressed = pressedDelegates.last().toInt();
+ QVERIFY(lastPressed > 5);
+ QCOMPARE(releasedDelegates.last(), lastPressed);
+ QCOMPARE(tappedDelegates.last(), lastPressed);
+ QCOMPARE(canceledDelegates.count(), 1); // only the first press was canceled, not the second
+}
+
+void tst_QQuickListView2::flickDuringFlicking_data()
+{
+ QTest::addColumn<QByteArray>("qmlFile");
+ QTest::addColumn<QQuickFlickable::BoundsBehavior>("boundsBehavior");
+
+ QTest::newRow("Button StopAtBounds") << QByteArray("buttonDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds);
+ QTest::newRow("MouseArea StopAtBounds") << QByteArray("mouseAreaDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds);
+ QTest::newRow("Button DragOverBounds") << QByteArray("buttonDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds);
+ QTest::newRow("MouseArea DragOverBounds") << QByteArray("mouseAreaDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds);
+ QTest::newRow("Button OvershootBounds") << QByteArray("buttonDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds);
+ QTest::newRow("MouseArea OvershootBounds") << QByteArray("mouseAreaDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds);
+ QTest::newRow("Button DragAndOvershootBounds") << QByteArray("buttonDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds);
+ QTest::newRow("MouseArea DragAndOvershootBounds") << QByteArray("mouseAreaDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds);
+}
+
+void tst_QQuickListView2::flickDuringFlicking() // QTBUG-103832
+{
+ QFETCH(QByteArray, qmlFile);
+ QFETCH(QQuickFlickable::BoundsBehavior, boundsBehavior);
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl(qmlFile.constData())));
+ QQuickListView *listView = qobject_cast<QQuickListView*>(window.rootObject());
+ QVERIFY(listView);
+ listView->setBoundsBehavior(boundsBehavior);
+
+ flickWithTouch(&window, {100, 400}, {100, 100});
+ // let it flick some distance
+ QTRY_VERIFY2(listView->contentY() > 1000, qPrintable(QString::fromLatin1(
+ "Expected ListView's contentY to be greater than 1000, but it's %1").arg(listView->contentY())));
+ QVERIFY(listView->isFlicking()); // we want to test the case when it's moving and then we flick again
+ const qreal posBeforeSecondFlick = listView->contentY();
+
+ // flick again during flicking, and make sure that it doesn't jump back to the first delegate,
+ // but flicks incrementally further from the position at that time
+ QTest::touchEvent(&window, touchDevice.data()).press(0, {100, 400});
+ QQuickTouchUtils::flush(&window);
+ qCDebug(lcTests) << "second press: contentY" << posBeforeSecondFlick << "->" << listView->contentY();
+ qCDebug(lcTests) << "pressed delegates" << listView->property("pressedDelegates").toList();
+ QVERIFY(listView->contentY() >= posBeforeSecondFlick);
+
+ QTest::qWait(20);
+ QTest::touchEvent(&window, touchDevice.data()).move(0, {100, 300});
+ QQuickTouchUtils::flush(&window);
+ qCDebug(lcTests) << "first move after second press: contentY" << posBeforeSecondFlick << "->" << listView->contentY();
+ QVERIFY(listView->contentY() >= posBeforeSecondFlick);
+
+ QTest::qWait(20);
+ QTest::touchEvent(&window, touchDevice.data()).move(0, {100, 200});
+ QQuickTouchUtils::flush(&window);
+ qCDebug(lcTests) << "second move after second press: contentY" << posBeforeSecondFlick << "->" << listView->contentY();
+ QVERIFY(listView->contentY() >= posBeforeSecondFlick + 100);
+
+ QTest::touchEvent(&window, touchDevice.data()).release(0, {100, 100});
+}
+
+void tst_QQuickListView2::flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to)
+{
+ QTest::touchEvent(window, touchDevice.data()).press(0, from, window);
+ QQuickTouchUtils::flush(window);
+
+ QPoint diff = to - from;
+ for (int i = 1; i <= 8; ++i) {
+ QTest::touchEvent(window, touchDevice.data()).move(0, from + i * diff / 8, window);
+ QQuickTouchUtils::flush(window);
+ }
+ QTest::touchEvent(window, touchDevice.data()).release(0, to, window);
+ QQuickTouchUtils::flush(window);
+}
+
class SingletonModel : public QStringListModel
{
Q_OBJECT
diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
index e990e94bc3..4bb98db003 100644
--- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp
+++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
@@ -711,7 +711,7 @@ void tst_QQuickLoader::initialPropertyValues_data()
<< (QVariantList() << 12);
QTest::newRow("initial property errors get reported") << testFileUrl("initialPropertyTriggerException.qml")
- << (QStringList() << "^.*: Error: Cannot assign JavaScript function to int")
+ << (QStringList() << "^.*:10: Error: Cannot assign JavaScript function to int")
<< QStringList()
<< QVariantList();
}
diff --git a/tests/auto/quick/qquickmousearea/data/simple.qml b/tests/auto/quick/qquickmousearea/data/simple.qml
index 56d561e5af..2b8a2af118 100644
--- a/tests/auto/quick/qquickmousearea/data/simple.qml
+++ b/tests/auto/quick/qquickmousearea/data/simple.qml
@@ -4,8 +4,9 @@ Rectangle {
id: whiteRect
width: 200
height: 200
- color: "white"
+ color: ma.pressed ? "lightsteelblue" : "white"
MouseArea {
+ id: ma
objectName: "mousearea"
anchors.fill: parent
}
diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
index f6080d94cd..ecdb57955f 100644
--- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
+++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
@@ -42,6 +42,7 @@
#include <QtGui/QCursor>
#include <QtGui/QScreen>
#include <qpa/qwindowsysteminterface.h>
+#include <qpa/qwindowsysteminterface_p.h>
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
@@ -159,6 +160,9 @@ private slots:
void containsMouseAndVisibility();
void doubleClickToHide();
void releaseFirstTouchAfterSecond();
+#if QT_CONFIG(tabletevent)
+ void tabletStylusTap();
+#endif
private:
int startDragDistance() const {
@@ -2420,6 +2424,37 @@ void tst_QQuickMouseArea::releaseFirstTouchAfterSecond() // QTBUG-103766
QTRY_COMPARE(releaseSpy.count(), 1);
}
+#if QT_CONFIG(tabletevent)
+void tst_QQuickMouseArea::tabletStylusTap()
+{
+ QVERIFY(qApp->testAttribute(Qt::AA_SynthesizeMouseForUnhandledTabletEvents)); // MouseArea depends on it
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("simple.qml")));
+ QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>();
+ QVERIFY(mouseArea);
+ QSignalSpy pressSpy(mouseArea, SIGNAL(pressed(QQuickMouseEvent*)));
+ QSignalSpy releaseSpy(mouseArea, &QQuickMouseArea::released);
+ QSignalSpy clickSpy(mouseArea, &QQuickMouseArea::clicked);
+ const qint64 stylusId = 1234567890;
+
+ const QPoint point(100,100);
+ QWindowSystemInterface::handleTabletEvent(&window, point, window.mapToGlobal(point),
+ int(QInputDevice::DeviceType::Stylus), int(QPointingDevice::PointerType::Pen),
+ Qt::LeftButton, 0.5, 0, 0, 0, 0, 0, stylusId, Qt::NoModifier);
+ if (QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse)
+ QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, point); // simulate what the platform does
+ QTRY_COMPARE(pressSpy.count(), 1);
+ QWindowSystemInterface::handleTabletEvent(&window, point, window.mapToGlobal(point),
+ int(QInputDevice::DeviceType::Stylus), int(QPointingDevice::PointerType::Pen),
+ Qt::NoButton, 0.5, 0, 0, 0, 0, 0, stylusId, Qt::NoModifier);
+ if (QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse)
+ QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, point);
+ QTRY_COMPARE(releaseSpy.count(), 1);
+ QCOMPARE(clickSpy.count(), 1);
+ QCOMPARE(pressSpy.count(), 1);
+}
+#endif
+
QTEST_MAIN(tst_QQuickMouseArea)
#include "tst_qquickmousearea.moc"
diff --git a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
index abe240f048..6d53fa2274 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
+++ b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
@@ -1,7 +1,9 @@
[nonOverlapping]
ubuntu-20.04
+ubuntu-22.04
[nested]
ubuntu-20.04
+ubuntu-22.04
# QTBUG-103200
[inFlickable2]
diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
index 8f45368f1c..2b7aca72d0 100644
--- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
+++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
@@ -153,6 +153,10 @@ private slots:
void objectModelMove();
void requiredPropertiesInDelegate();
void requiredPropertiesInDelegatePreventUnrelated();
+ void touchMove();
+
+private:
+ QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
};
class TestObject : public QObject
@@ -1390,8 +1394,7 @@ void tst_QQuickPathView::package()
QVERIFY(window);
window->setSource(testFileUrl("pathview_package.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathView = window->rootObject()->findChild<QQuickPathView*>("photoPathView");
QVERIFY(pathView);
@@ -1519,9 +1522,7 @@ void tst_QQuickPathView::mouseDrag()
QQuickVisualTestUtils::moveMouseAway(window.data());
window->setSource(testFileUrl("dragpath.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
QVERIFY(pathview != nullptr);
@@ -1589,10 +1590,7 @@ void tst_QQuickPathView::nestedMouseAreaDrag()
QQuickVisualTestUtils::moveMouseAway(window.data());
window->setSource(testFileUrl("nestedmousearea.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
-
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
QVERIFY(pathview != nullptr);
@@ -1612,9 +1610,7 @@ void tst_QQuickPathView::flickNClick() // QTBUG-77173
QQuickVisualTestUtils::moveMouseAway(window.data());
window->setSource(testFileUrl("nestedmousearea2.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
QVERIFY(pathview != nullptr);
@@ -1703,9 +1699,7 @@ void tst_QQuickPathView::changePreferredHighlight()
window->setGeometry(0,0,400,200);
window->setSource(testFileUrl("dragpath.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
QVERIFY(pathview != nullptr);
@@ -1906,9 +1900,7 @@ void tst_QQuickPathView::cancelDrag()
window->setSource(testFileUrl("dragpath.qml"));
QQuickVisualTestUtils::moveMouseAway(window.data());
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
QVERIFY(pathview != nullptr);
@@ -1953,9 +1945,7 @@ void tst_QQuickPathView::maximumFlickVelocity()
window->setSource(testFileUrl("dragpath.qml"));
QQuickVisualTestUtils::moveMouseAway(window.data());
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
QVERIFY(pathview != nullptr);
@@ -2000,9 +1990,7 @@ void tst_QQuickPathView::snapToItem()
QQuickVisualTestUtils::moveMouseAway(window.data());
window->setSource(testFileUrl("panels.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathview = window->rootObject()->findChild<QQuickPathView*>("view");
QVERIFY(pathview != nullptr);
@@ -2044,9 +2032,7 @@ void tst_QQuickPathView::snapOneItem()
QQuickVisualTestUtils::moveMouseAway(window.data());
window->setSource(testFileUrl("panels.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathview = window->rootObject()->findChild<QQuickPathView*>("view");
QVERIFY(pathview != nullptr);
@@ -2097,9 +2083,7 @@ void tst_QQuickPathView::positionViewAtIndex()
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("pathview3.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
QVERIFY(pathview != nullptr);
@@ -2160,9 +2144,7 @@ void tst_QQuickPathView::indexAt_itemAt()
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("pathview3.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
QVERIFY(pathview != nullptr);
@@ -2286,8 +2268,7 @@ void tst_QQuickPathView::changePathDuringRefill()
window->setSource(testFileUrl("changePathDuringRefill.qml"));
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathView = qobject_cast<QQuickPathView*>(window->rootObject());
QVERIFY(pathView != nullptr);
@@ -2313,9 +2294,7 @@ void tst_QQuickPathView::nestedinFlickable()
QQuickVisualTestUtils::moveMouseAway(window.data());
window->setSource(testFileUrl("nestedInFlickable.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "pathView");
QVERIFY(pathview != nullptr);
@@ -2421,9 +2400,7 @@ void tst_QQuickPathView::ungrabNestedinFlickable()
QQuickVisualTestUtils::moveMouseAway(window.data());
window->setSource(testFileUrl("ungrabNestedinFlickable.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "pathView");
QVERIFY(pathview != nullptr);
@@ -2455,9 +2432,7 @@ void tst_QQuickPathView::flickableDelegate()
QQuickVisualTestUtils::moveMouseAway(window.data());
window->setSource(testFileUrl("flickableDelegate.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
QVERIFY(pathview != nullptr);
@@ -2537,8 +2512,7 @@ void tst_QQuickPathView::qtbug37815()
window->setSource(testFileUrl("qtbug37815.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
// cache items will be created async. Let's wait...
QTest::qWait(1000);
@@ -2568,8 +2542,7 @@ void tst_QQuickPathView::qtbug42716()
window->setSource(testFileUrl("qtbug42716.qml"));
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathView = findItem<QQuickPathView>(window->rootObject(), "pathView");
QVERIFY(pathView != nullptr);
@@ -2610,8 +2583,7 @@ void tst_QQuickPathView::qtbug53464()
window->setSource(testFileUrl("qtbug53464.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathView = findItem<QQuickPathView>(window->rootObject(), "pathView");
QVERIFY(pathView != nullptr);
@@ -2688,9 +2660,7 @@ void tst_QQuickPathView::movementDirection()
QQuickVisualTestUtils::moveMouseAway(window.data());
window->setSource(testFileUrl("movementDirection.qml"));
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
- QCOMPARE(window.data(), qGuiApp->focusWindow());
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickPathView *pathview = window->rootObject()->findChild<QQuickPathView*>("view");
QVERIFY(pathview != nullptr);
@@ -2797,6 +2767,112 @@ void tst_QQuickPathView::requiredPropertiesInDelegatePreventUnrelated()
window->show();
}
+void tst_QQuickPathView::touchMove()
+{
+ QScopedPointer<QQuickView> window(createView());
+ QQuickVisualTestUtils::moveMouseAway(window.data());
+ window->setSource(testFileUrl("dragpath.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
+ QVERIFY(pathview != nullptr);
+
+ QSignalSpy movingSpy(pathview, SIGNAL(movingChanged()));
+ QSignalSpy moveStartedSpy(pathview, SIGNAL(movementStarted()));
+ QSignalSpy moveEndedSpy(pathview, SIGNAL(movementEnded()));
+ QSignalSpy draggingSpy(pathview, SIGNAL(draggingChanged()));
+ QSignalSpy dragStartedSpy(pathview, SIGNAL(dragStarted()));
+ QSignalSpy dragEndedSpy(pathview, SIGNAL(dragEnded()));
+ QSignalSpy flickStartedSpy(pathview, SIGNAL(flickStarted()));
+ QSignalSpy flickEndedSpy(pathview, SIGNAL(flickEnded()));
+
+ int current = pathview->currentIndex();
+
+ // touch move from left to right
+ QPoint from(250, 100);
+ QPoint to(10, 100);
+
+ QTest::touchEvent(window.data(), touchDevice.data()).press(0, from, window.data());
+ QQuickTouchUtils::flush(window.data());
+
+ QVERIFY(!pathview->isMoving());
+ QVERIFY(!pathview->isDragging());
+ QCOMPARE(movingSpy.count(), 0);
+ QCOMPARE(moveStartedSpy.count(), 0);
+ QCOMPARE(moveEndedSpy.count(), 0);
+ QCOMPARE(draggingSpy.count(), 0);
+ QCOMPARE(dragStartedSpy.count(), 0);
+ QCOMPARE(dragEndedSpy.count(), 0);
+ QCOMPARE(flickStartedSpy.count(), 0);
+ QCOMPARE(flickEndedSpy.count(), 0);
+
+ from -= QPoint(QGuiApplication::styleHints()->startDragDistance() + 1, 0);
+ QTest::touchEvent(window.data(), touchDevice.data()).move(0, from, window.data());
+ QQuickTouchUtils::flush(window.data());
+
+ // first move does not trigger move/drag
+ QVERIFY(!pathview->isMoving());
+ QVERIFY(!pathview->isDragging());
+ QCOMPARE(movingSpy.count(), 0);
+ QCOMPARE(moveStartedSpy.count(), 0);
+ QCOMPARE(moveEndedSpy.count(), 0);
+ QCOMPARE(draggingSpy.count(), 0);
+ QCOMPARE(dragStartedSpy.count(), 0);
+ QCOMPARE(dragEndedSpy.count(), 0);
+ QCOMPARE(flickStartedSpy.count(), 0);
+ QCOMPARE(flickEndedSpy.count(), 0);
+
+ QPoint diff = from - to;
+ int moveCount = 4;
+ for (int i = 1; i <= moveCount; ++i) {
+ QTest::touchEvent(window.data(), touchDevice.data()).move(0, from - i * diff / moveCount, window.data());
+ QQuickTouchUtils::flush(window.data());
+
+ QVERIFY(pathview->isMoving());
+ QVERIFY(pathview->isDragging());
+ QCOMPARE(movingSpy.count(), 1);
+ QCOMPARE(moveStartedSpy.count(), 1);
+ QCOMPARE(moveEndedSpy.count(), 0);
+ QCOMPARE(draggingSpy.count(), 1);
+ QCOMPARE(dragStartedSpy.count(), 1);
+ QCOMPARE(dragEndedSpy.count(), 0);
+ QCOMPARE(flickStartedSpy.count(), 0);
+ QCOMPARE(flickEndedSpy.count(), 0);
+ }
+ QVERIFY(pathview->currentIndex() != current);
+
+ QTest::touchEvent(window.data(), touchDevice.data()).release(0, to, window.data());
+ QQuickTouchUtils::flush(window.data());
+
+ QVERIFY(pathview->isMoving());
+ QVERIFY(!pathview->isDragging());
+ QCOMPARE(movingSpy.count(), 1);
+ QCOMPARE(moveStartedSpy.count(), 1);
+ QCOMPARE(moveEndedSpy.count(), 0);
+ QCOMPARE(draggingSpy.count(), 2);
+ QCOMPARE(dragStartedSpy.count(), 1);
+ QCOMPARE(dragEndedSpy.count(), 1);
+ QCOMPARE(flickStartedSpy.count(), 1);
+ QCOMPARE(flickEndedSpy.count(), 0);
+
+ // Wait for the flick to finish
+ QVERIFY(QTest::qWaitFor([&]()
+ { return !pathview->isFlicking(); }
+ ));
+ QVERIFY(!pathview->isMoving());
+ QVERIFY(!pathview->isDragging());
+ QCOMPARE(movingSpy.count(), 2);
+ QCOMPARE(moveStartedSpy.count(), 1);
+ QCOMPARE(moveEndedSpy.count(), 1);
+ QCOMPARE(draggingSpy.count(), 2);
+ QCOMPARE(dragStartedSpy.count(), 1);
+ QCOMPARE(dragEndedSpy.count(), 1);
+ QCOMPARE(flickStartedSpy.count(), 1);
+ QCOMPARE(flickEndedSpy.count(), 1);
+
+}
+
QTEST_MAIN(tst_QQuickPathView)
#include "tst_qquickpathview.moc"
diff --git a/tests/auto/quick/qquickpincharea/data/pinchAreaInPathView.qml b/tests/auto/quick/qquickpincharea/data/pinchAreaInPathView.qml
new file mode 100644
index 0000000000..dc909c2c7c
--- /dev/null
+++ b/tests/auto/quick/qquickpincharea/data/pinchAreaInPathView.qml
@@ -0,0 +1,48 @@
+import QtQuick
+
+PathView {
+ width: 600
+ height: 200
+
+ model: 3
+ delegate: Rectangle {
+ width: 200
+ height: 200
+ color: "salmon"
+ opacity: PathView.isCurrentItem ? 1 : 0.5
+
+ property alias pinchArea: pinchArea
+
+ Text {
+ text: "Test"
+ font.pixelSize: 100
+ anchors.fill: parent
+ fontSizeMode: Text.Fit
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ PinchArea {
+ id: pinchArea
+ anchors.fill: parent
+ pinch.target: parent
+ pinch.dragAxis: Pinch.XAndYAxis
+ pinch.minimumScale: 1.0
+ pinch.maximumScale: 5.0
+
+ onPinchFinished: (pinch) => {
+ parent.scale = 1
+ parent.x = 0
+ parent.y = 0
+ }
+ }
+ }
+ path: Path {
+ startX: 100
+ startY: 100
+ PathLine {
+ x: 700
+ y: 100
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickpincharea/data/pinchproperties.qml b/tests/auto/quick/qquickpincharea/data/pinchproperties.qml
index 899c6380da..42096d0b77 100644
--- a/tests/auto/quick/qquickpincharea/data/pinchproperties.qml
+++ b/tests/auto/quick/qquickpincharea/data/pinchproperties.qml
@@ -31,18 +31,18 @@ Rectangle {
pinch.maximumScale: 2.0
pinch.minimumRotation: 0.0
pinch.maximumRotation: 90.0
- onPinchStarted: {
+ onPinchStarted: (pinch) => {
whiteRect.center = pinch.center
whiteRect.scale = pinch.scale
whiteRect.pointCount = pinch.pointCount;
whiteRect.pinchActive = true;
}
- onPinchUpdated: {
+ onPinchUpdated: (pinch) => {
whiteRect.center = pinch.center
whiteRect.scale = pinch.scale
whiteRect.pointCount = pinch.pointCount;
}
- onPinchFinished: {
+ onPinchFinished: (pinch) => {
whiteRect.center = pinch.center
whiteRect.scale = pinch.scale
whiteRect.pointCount = pinch.pointCount;
diff --git a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
index 85c186b2d8..17e225f1e2 100644
--- a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
+++ b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
@@ -26,6 +26,7 @@
**
****************************************************************************/
+#include <QtCore/private/qvariantanimation_p.h>
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
#include <QtGui/QStyleHints>
@@ -33,6 +34,7 @@
#include <QtGui/private/qeventpoint_p.h>
#include <qpa/qwindowsysteminterface.h>
#include <private/qquickpincharea_p.h>
+#include <QtQuick/private/qquickpathview_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/qquickview.h>
#include <QtQml/qqmlcontext.h>
@@ -55,6 +57,8 @@ private slots:
void transformedPinchArea();
void dragTransformedPinchArea_data();
void dragTransformedPinchArea();
+ void pinchAreaKeepsDragInView();
+ void pinchInPathView();
private:
QQuickView *createView();
@@ -643,6 +647,147 @@ void tst_QQuickPinchArea::dragTransformedPinchArea() // QTBUG-63673
QCOMPARE(pinchArea->pinch()->active(), false);
}
+template<typename F>
+void forEachLerpStep(int steps, F &&func)
+{
+ for (int i = 0; i < steps; ++i) {
+ const qreal t = qreal(i) / steps;
+ func(t);
+ }
+}
+
+QPoint lerpPoints(const QPoint &point1, const QPoint &point2, qreal t)
+{
+ return QPoint(_q_interpolate(point1.x(), point2.x(), t), _q_interpolate(point1.y(), point2.y(), t));
+};
+
+// QTBUG-105058
+void tst_QQuickPinchArea::pinchAreaKeepsDragInView()
+{
+ QQuickView view;
+ view.setSource(testFileUrl("pinchAreaInPathView.qml"));
+ QVERIFY(view.rootObject());
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ QQuickPathView *pathView = qobject_cast<QQuickPathView*>(view.rootObject());
+ QVERIFY(pathView);
+ QCOMPARE(pathView->count(), 3);
+
+ const QQuickItem *pinchDelegateItem = pathView->itemAtIndex(0);
+ QQuickPinchArea *pinchArea = pinchDelegateItem->property("pinchArea").value<QQuickPinchArea*>();
+ QVERIFY(pinchArea);
+
+ // Press.
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(&view, device);
+ QPoint point1Start = { 80, 120 };
+ QPoint point2Start = { 120, 80 };
+ const int dragThreshold = qApp->styleHints()->startDragDistance();
+ pinchSequence.press(1, pinchArea->mapToScene(point1Start).toPoint(), &view)
+ .press(2, pinchArea->mapToScene(point2Start).toPoint(), &view).commit();
+ QQuickTouchUtils::flush(&view);
+
+ // Move past the drag threshold to begin the pinch.
+ const int steps = 30;
+ QPoint point1End = point1Start + QPoint(-dragThreshold, dragThreshold);
+ QPoint point2End = point2Start + QPoint(dragThreshold, -dragThreshold);
+ forEachLerpStep(steps, [&](qreal t) {
+ pinchSequence.move(1, lerpPoints(point1Start, point1End, t), &view)
+ .move(2, lerpPoints(point2Start, point2End, t), &view).commit();
+ QQuickTouchUtils::flush(&view);
+ QTest::qWait(5);
+ });
+ QCOMPARE(pinchArea->pinch()->active(), true);
+ QVERIFY2(pinchDelegateItem->scale() > 1.0, qPrintable(QString::number(pinchDelegateItem->scale())));
+ // The PathView contents shouldn't have moved.
+ QCOMPARE(pathView->offset(), 0);
+
+ // Release a touch point.
+ pinchSequence.stationary(1).release(2, point2End, &view).commit();
+ QQuickTouchUtils::flush(&view);
+
+ // Press it again.
+ pinchSequence.stationary(1).press(2, point2End, &view).commit();
+ QQuickTouchUtils::flush(&view);
+ QCOMPARE(pinchArea->pinch()->active(), true);
+
+ // Drag to the right; the PathView still shouldn't move.
+ point1Start = point1End;
+ point2Start = point2End;
+ point1End = point1Start + QPoint(100, 0);
+ point2End = point2Start + QPoint(100, 0);
+ forEachLerpStep(steps, [&](qreal t) {
+ pinchSequence.move(1, lerpPoints(point1Start, point1End, t), &view)
+ .move(2, lerpPoints(point2Start, point2End, t), &view).commit();
+ QQuickTouchUtils::flush(&view);
+ QTest::qWait(5);
+ });
+ QCOMPARE(pinchArea->pinch()->active(), true);
+ QVERIFY2(pinchDelegateItem->scale() > 1.0, qPrintable(QString::number(pinchDelegateItem->scale())));
+ QCOMPARE(pathView->offset(), 0);
+
+ // Release pinch.
+ pinchSequence.release(1, point1End, &view).release(2, point2End, &view).commit();
+ QQuickTouchUtils::flush(&view);
+ QCOMPARE(pinchArea->pinch()->active(), false);
+ QCOMPARE(pathView->offset(), 0);
+}
+
+void tst_QQuickPinchArea::pinchInPathView()
+{
+ QQuickView view;
+ view.setSource(testFileUrl("pinchAreaInPathView.qml"));
+ QVERIFY(view.rootObject());
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ QQuickPathView *pathView = qobject_cast<QQuickPathView*>(view.rootObject());
+ QVERIFY(pathView);
+ QCOMPARE(pathView->count(), 3);
+
+ const QQuickItem *pinchDelegateItem = pathView->itemAtIndex(0);
+ QQuickPinchArea *pinchArea = pinchDelegateItem->property("pinchArea").value<QQuickPinchArea*>();
+ QVERIFY(pinchArea);
+
+ // press
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(&view, device);
+ QPoint point1Start = { 10, 10 };
+ QPoint point2Start = { 100, 100 };
+ const int dragThreshold = qApp->styleHints()->startDragDistance();
+ pinchSequence.press(0, point1Start, &view)
+ .press(1, point2Start, &view)
+ .commit();
+ QQuickTouchUtils::flush(&view);
+ QTest::qWait(20);
+
+ // move
+ QPoint moveDistance = QPoint(dragThreshold * 3, dragThreshold * 3);
+ QPoint point2End = point2Start + moveDistance;
+ pinchSequence.stationary(0)
+ .move(1, point2End, &view)
+ .commit();
+ QQuickTouchUtils::flush(&view);
+ QTest::qWait(20);
+
+ point2End += moveDistance;
+ pinchSequence.stationary(0)
+ .move(1, point2End, &view)
+ .commit();
+ QQuickTouchUtils::flush(&view);
+ QTest::qWait(20);
+
+ QCOMPARE(pinchArea->pinch()->active(), true);
+ QVERIFY2(pinchDelegateItem->scale() > 1.0, qPrintable(QString::number(pinchDelegateItem->scale())));
+ // PathView shouldn't have moved.
+ QCOMPARE(pathView->offset(), 0);
+
+ // release pinch.
+ pinchSequence.release(0, point1Start, &view).release(1, point2End, &view).commit();
+ QQuickTouchUtils::flush(&view);
+ QCOMPARE(pinchArea->pinch()->active(), false);
+ QCOMPARE(pathView->offset(), 0);
+}
+
QQuickView *tst_QQuickPinchArea::createView()
{
QQuickView *window = new QQuickView(nullptr);
diff --git a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
index c7619ab73c..788323ecf4 100644
--- a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
+++ b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
@@ -32,9 +32,11 @@
#include <QByteArray>
#include <private/qquickshadereffect_p.h>
#include <QMatrix4x4>
-#include <QtQuick/QQuickView>
#include <QtQml/QQmlEngine>
+#include <QtQuick/QQuickView>
+#include <QtQuick/private/qquickshadereffectsource_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQuickTestUtils/private/viewtestutils_p.h>
class TestShaderEffect : public QQuickShaderEffect
{
@@ -120,17 +122,17 @@ void tst_qquickshadereffect::cleanupTestCase()
void tst_qquickshadereffect::testConnection()
{
// verify that the property notify signal is connected
- QQuickView *view = new QQuickView(nullptr);
- view->setSource(QUrl(QStringLiteral("qrc:/data/connections.qml")));
+ QQuickView view;
+ QVERIFY(QQuickTest::initView(view, QStringLiteral("qrc:/data/connections.qml")));
- auto *shaderEffectItem = qobject_cast<TestShaderEffect*>(view->rootObject());
+ auto *shaderEffectItem = qobject_cast<TestShaderEffect*>(view.rootObject());
QVERIFY(shaderEffectItem);
QCOMPARE(shaderEffectItem->signalsConnected, 0);
- view->show();
- QVERIFY(QTest::qWaitForWindowExposed(view));
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
- QSGRendererInterface *rif = view->rendererInterface();
+ QSGRendererInterface *rif = view.rendererInterface();
if (rif && rif->graphicsApi() != QSGRendererInterface::Software)
QCOMPARE(shaderEffectItem->signalsConnected, 1);
}
@@ -138,76 +140,62 @@ void tst_qquickshadereffect::testConnection()
void tst_qquickshadereffect::deleteSourceItem()
{
// purely to ensure that deleting the sourceItem of a shader doesn't cause a crash
- QQuickView *view = new QQuickView(nullptr);
- view->setSource(QUrl(QStringLiteral("qrc:/data/deleteSourceItem.qml")));
- view->show();
- QVERIFY(QTest::qWaitForWindowExposed(view));
- QVERIFY(view);
- QObject *obj = view->rootObject();
+ QQuickView view;
+ QVERIFY(QQuickTest::showView(view, QStringLiteral("qrc:/data/deleteSourceItem.qml")));
+ QObject *obj = view.rootObject();
QVERIFY(obj);
QMetaObject::invokeMethod(obj, "setDeletedSourceItem");
- QTest::qWait(50);
- delete view;
+ const auto shaderEffectSource = obj->findChild<QQuickShaderEffectSource*>();
+ QVERIFY(shaderEffectSource);
+ QTRY_VERIFY(!shaderEffectSource->sourceItem());
}
void tst_qquickshadereffect::deleteShaderEffectSource()
{
- // purely to ensure that deleting the sourceItem of a shader doesn't cause a crash
- QQuickView *view = new QQuickView(nullptr);
- view->setSource(QUrl(QStringLiteral("qrc:/data/deleteShaderEffectSource.qml")));
- view->show();
- QVERIFY(QTest::qWaitForWindowExposed(view));
- QVERIFY(view);
- QObject *obj = view->rootObject();
+ // purely to ensure that deleting the ShaderEffectSource doesn't cause a crash
+ QQuickView view;
+ QVERIFY(QQuickTest::showView(view, QStringLiteral("qrc:/data/deleteShaderEffectSource.qml")));
+ QObject *obj = view.rootObject();
QVERIFY(obj);
+ const QPointer<QQuickShaderEffectSource> shaderEffectSource = obj->findChild<QQuickShaderEffectSource*>();
+ QVERIFY(shaderEffectSource);
QMetaObject::invokeMethod(obj, "setDeletedShaderEffectSource");
- QTest::qWait(50);
- delete view;
+ QTRY_VERIFY(shaderEffectSource);
}
void tst_qquickshadereffect::twoImagesOneShaderEffect()
{
// Don't crash when having a ShaderEffect and an Image sharing the texture via supportsAtlasTextures
- QQuickView *view = new QQuickView(nullptr);
- view->setSource(QUrl(QStringLiteral("qrc:/data/twoImagesOneShaderEffect.qml")));
- view->show();
- QVERIFY(QTest::qWaitForWindowExposed(view));
- QVERIFY(view);
- QObject *obj = view->rootObject();
+ QQuickView view;
+ QVERIFY(QQuickTest::showView(view, QStringLiteral("qrc:/data/twoImagesOneShaderEffect.qml")));
+ QObject *obj = view.rootObject();
QVERIFY(obj);
- delete view;
}
void tst_qquickshadereffect::withoutQmlEngine()
{
// using a shader without QML engine used to crash
- auto window = new QQuickWindow;
- auto shaderEffect = new TestShaderEffect(window->contentItem());
+ const QQuickWindow window;
+ auto shaderEffect = new TestShaderEffect(window.contentItem());
shaderEffect->setVertexShader(QUrl());
QVERIFY(shaderEffect->isComponentComplete());
- delete window;
}
// QTBUG-86402: hiding the parent of an item that uses an effect should not cause a crash.
void tst_qquickshadereffect::hideParent()
{
- QScopedPointer<QQuickView> view(new QQuickView);
- view->setSource(QUrl(QStringLiteral("qrc:/data/hideParent.qml")));
- QCOMPARE(view->status(), QQuickView::Ready);
- view->show();
- QVERIFY(QTest::qWaitForWindowExposed(view.data()));
+ QQuickView view;
+ view.setSource(QUrl(QStringLiteral("qrc:/data/hideParent.qml")));
+ QVERIFY(QQuickTest::showView(view, QStringLiteral("qrc:/data/hideParent.qml")));
// Should finish without crashing.
- QTRY_VERIFY(view->rootObject()->property("finished").toBool());
+ QTRY_VERIFY(view.rootObject()->property("finished").toBool());
}
void tst_qquickshadereffect::testPropertyMappings()
{
- QScopedPointer<QQuickView> view(new QQuickView);
- view->setSource(QUrl(QStringLiteral("qrc:/data/testProperties.qml")));
- QCOMPARE(view->status(), QQuickView::Ready);
- view->show();
- QVERIFY(QTest::qWaitForWindowExposed(view.data()));
- QTRY_VERIFY(view->rootObject()->property("finished").toBool());
+ QQuickView view;
+ view.setSource(QUrl(QStringLiteral("qrc:/data/testProperties.qml")));
+ QTRY_VERIFY(view.rootObject()->property("finished").toBool());
}
QTEST_MAIN(tst_qquickshadereffect)
diff --git a/tests/auto/quick/qquickstates/data/removeBindingWithTransition.qml b/tests/auto/quick/qquickstates/data/removeBindingWithTransition.qml
new file mode 100644
index 0000000000..ed40e18374
--- /dev/null
+++ b/tests/auto/quick/qquickstates/data/removeBindingWithTransition.qml
@@ -0,0 +1,23 @@
+import QtQuick
+
+Item {
+ id: root
+ property bool toggle: true
+ property int state1Width: 500
+
+ states: [
+ State {
+ when: root.toggle
+ PropertyChanges { root.width: root.state1Width }
+ },
+ State {
+ when: !root.toggle
+ PropertyChanges { root.width: 300 }
+ }
+ ]
+
+ transitions: Transition {
+ id: transition
+ SmoothedAnimation { target: root; property: "width"; velocity: 200 }
+ }
+}
diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
index d6814bd057..35d6deee58 100644
--- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp
+++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
@@ -207,6 +207,7 @@ private slots:
void parentChangeInvolvingBindings();
void deferredProperties();
void rewindAnchorChange();
+ void bindingProperlyRemovedWithTransition();
};
void tst_qquickstates::initTestCase()
@@ -1978,6 +1979,26 @@ void tst_qquickstates::rewindAnchorChange()
QTRY_COMPARE(innerRect->height(), 200);
}
+void tst_qquickstates::bindingProperlyRemovedWithTransition()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("removeBindingWithTransition.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> root(c.create());
+ QVERIFY(root);
+ QQuickItem *item = qobject_cast<QQuickItem *>(root.get());
+ QVERIFY(item);
+
+ item->setProperty("toggle", false);
+ QTRY_COMPARE(item->width(), 300);
+
+ item->setProperty("state1Width", 100);
+ QCOMPARE(item->width(), 300);
+
+ item->setProperty("toggle", true);
+ QTRY_COMPARE(item->width(), 100);
+}
+
QTEST_MAIN(tst_qquickstates)
#include "tst_qquickstates.moc"
diff --git a/tests/auto/quick/qquicktext/BLACKLIST b/tests/auto/quick/qquicktext/BLACKLIST
index e7ad2cdada..a4e9c44eab 100644
--- a/tests/auto/quick/qquicktext/BLACKLIST
+++ b/tests/auto/quick/qquicktext/BLACKLIST
@@ -21,7 +21,5 @@ android
# QTBUG-103096
[largeTextObservesViewport]
android
-[imgTagsAlign]
-android
[baselineOffset]
android
diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
index 1f7d000bda..8fcdcca5a2 100644
--- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp
+++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
@@ -3278,12 +3278,12 @@ void tst_qquicktext::imgTagsAlign_data()
QTest::addColumn<QString>("src");
QTest::addColumn<int>("imgHeight");
QTest::addColumn<QString>("align");
- QTest::newRow("heart-bottom") << "data/images/heart200.png" << 181 << "bottom";
- QTest::newRow("heart-middle") << "data/images/heart200.png" << 181 << "middle";
- QTest::newRow("heart-top") << "data/images/heart200.png" << 181 << "top";
- QTest::newRow("starfish-bottom") << "data/images/starfish_2.png" << 217 << "bottom";
- QTest::newRow("starfish-middle") << "data/images/starfish_2.png" << 217 << "middle";
- QTest::newRow("starfish-top") << "data/images/starfish_2.png" << 217 << "top";
+ QTest::newRow("heart-bottom") << "images/heart200.png" << 181 << "bottom";
+ QTest::newRow("heart-middle") << "images/heart200.png" << 181 << "middle";
+ QTest::newRow("heart-top") << "images/heart200.png" << 181 << "top";
+ QTest::newRow("starfish-bottom") << "images/starfish_2.png" << 217 << "bottom";
+ QTest::newRow("starfish-middle") << "images/starfish_2.png" << 217 << "middle";
+ QTest::newRow("starfish-top") << "images/starfish_2.png" << 217 << "top";
}
void tst_qquicktext::imgTagsAlign()
@@ -3293,7 +3293,7 @@ void tst_qquicktext::imgTagsAlign()
QFETCH(QString, align);
QString componentStr = "import QtQuick 2.0\nText { text: \"This is a test <img src=\\\"" + src + "\\\" align=\\\"" + align + "\\\"> of image.\" }";
QQmlComponent textComponent(&engine);
- textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("."));
+ textComponent.setData(componentStr.toLatin1(), testFileUrl("."));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
QVERIFY(textObject != nullptr);
@@ -3315,10 +3315,10 @@ void tst_qquicktext::imgTagsAlign()
void tst_qquicktext::imgTagsMultipleImages()
{
- QString componentStr = "import QtQuick 2.0\nText { text: \"This is a starfish<img src=\\\"data/images/starfish_2.png\\\" width=\\\"60\\\" height=\\\"60\\\" > and another one<img src=\\\"data/images/heart200.png\\\" width=\\\"85\\\" height=\\\"85\\\">.\" }";
+ QString componentStr = "import QtQuick 2.0\nText { text: \"This is a starfish<img src=\\\"images/starfish_2.png\\\" width=\\\"60\\\" height=\\\"60\\\" > and another one<img src=\\\"images/heart200.png\\\" width=\\\"85\\\" height=\\\"85\\\">.\" }";
QQmlComponent textComponent(&engine);
- textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("."));
+ textComponent.setData(componentStr.toLatin1(), testFileUrl("."));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
QVERIFY(textObject != nullptr);
@@ -3374,11 +3374,15 @@ void tst_qquicktext::imgTagsUpdates()
void tst_qquicktext::imgTagsError()
{
- QString componentStr = "import QtQuick 2.0\nText { text: \"This is a starfish<img src=\\\"data/images/starfish_2.pn\\\" width=\\\"60\\\" height=\\\"60\\\">.\" }";
+ QString componentStr = "import QtQuick 2.0\nText { text: \"This is a starfish<img src=\\\"images/starfish_2.pn\\\" width=\\\"60\\\" height=\\\"60\\\">.\" }";
QQmlComponent textComponent(&engine);
- QTest::ignoreMessage(QtWarningMsg, "<Unknown File>:2:1: QML Text: Cannot open: file:data/images/starfish_2.pn");
- textComponent.setData(componentStr.toLatin1(), QUrl("file:"));
+ const QString expectedMessage(
+ testFileUrl(".").toString()
+ + ":2:1: QML Text: Cannot open: "
+ + testFileUrl("images/starfish_2.pn").toString());
+ QTest::ignoreMessage(QtWarningMsg, expectedMessage.toLatin1());
+ textComponent.setData(componentStr.toLatin1(), testFileUrl("."));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
QVERIFY(textObject != nullptr);
@@ -3626,6 +3630,46 @@ void tst_qquicktext::fontSizeMode()
myText->setElideMode(QQuickText::ElideNone);
QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+
+ // Growing height needs to update the baselineOffset when AlignBottom is used
+ // and text is NOT wrapped
+ myText->setVAlign(QQuickText::AlignBottom);
+ myText->setFontSizeMode(QQuickText::Fit);
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+
+ int baselineOffset = myText->baselineOffset();
+ myText->setHeight(myText->height() * 2);
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+ QVERIFY(myText->baselineOffset() > baselineOffset);
+
+ // Growing height needs to update the baselineOffset when AlignBottom is used
+ // and the text is wrapped
+ myText->setVAlign(QQuickText::AlignBottom);
+ myText->setFontSizeMode(QQuickText::Fit);
+ myText->setWrapMode(QQuickText::NoWrap);
+ myText->resetMaximumLineCount();
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+
+ baselineOffset = myText->baselineOffset();
+ myText->setHeight(myText->height() * 2);
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+ QVERIFY(myText->baselineOffset() > baselineOffset);
+
+ // Check baselineOffset for the HorizontalFit case
+ myText->setVAlign(QQuickText::AlignBottom);
+ myText->setFontSizeMode(QQuickText::HorizontalFit);
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+ QSignalSpy baselineOffsetSpy(myText, SIGNAL(baselineOffsetChanged(qreal)));
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+ const qreal oldBaselineOffset = myText->baselineOffset();
+ myText->setHeight(myText->height() + 42);
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+ QCOMPARE(baselineOffsetSpy.count(), 1);
+ QCOMPARE(myText->baselineOffset(), oldBaselineOffset + 42);
+ myText->setHeight(myText->height() - 42);
+ QVERIFY(QQuickTest::qWaitForItemPolished(myText));
+ QCOMPARE(baselineOffsetSpy.count(), 2);
+ QCOMPARE(myText->baselineOffset(), oldBaselineOffset);
}
void tst_qquicktext::fontSizeModeMultiline_data()
diff --git a/tests/auto/quick/qquicktextedit/data/threeLines.qml b/tests/auto/quick/qquicktextedit/data/threeLines.qml
new file mode 100644
index 0000000000..cee03bfa15
--- /dev/null
+++ b/tests/auto/quick/qquicktextedit/data/threeLines.qml
@@ -0,0 +1,7 @@
+import QtQuick
+import Qt.test 1.0
+
+NodeCheckerTextEdit {
+ width: 200; height: 100
+ text: "Line 1\nLine 2\nLine 3\n"
+}
diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
index 7cdaf6819e..66d1d29ae6 100644
--- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
+++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
@@ -101,6 +101,8 @@ public:
tst_qquicktextedit();
private slots:
+ void initTestCase() override;
+
void cleanup();
void text();
void width();
@@ -182,6 +184,7 @@ private slots:
void implicitSizeBinding();
void largeTextObservesViewport_data();
void largeTextObservesViewport();
+ void renderingAroundSelection();
void signal_editingfinished();
@@ -369,6 +372,56 @@ tst_qquicktextedit::tst_qquicktextedit()
//
}
+class NodeCheckerTextEdit : public QQuickTextEdit
+{
+public:
+ NodeCheckerTextEdit(QQuickItem *parent = nullptr) : QQuickTextEdit(parent) {}
+
+ void populateLinePositions(QSGNode *node)
+ {
+ linePositions.clear();
+ lastLinePosition = 0;
+ QSGNode *ch = node->firstChild();
+ while (ch != node->lastChild()) {
+ QCOMPARE(ch->type(), QSGNode::TransformNodeType);
+ QSGTransformNode *tn = static_cast<QSGTransformNode *>(ch);
+ int y = 0;
+ if (!tn->matrix().isIdentity())
+ y = tn->matrix().column(3).y();
+ if (tn->childCount() == 0) {
+ // A TransformNode with no children is a waste of memory.
+ // So far, QQuickTextEdit still creates a couple of extras.
+ qCDebug(lcTests) << "ignoring leaf TransformNode" << tn << "@ y" << y;
+ } else {
+ qCDebug(lcTests) << "child" << tn << "@ y" << y << "has children" << tn->childCount();
+ if (!linePositions.contains(y)) {
+ linePositions.append(y);
+ lastLinePosition = qMax(lastLinePosition, y);
+ }
+ }
+ ch = ch->nextSibling();
+ }
+ std::sort(linePositions.begin(), linePositions.end());
+ }
+
+ QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data) override
+ {
+ QSGNode *ret = QQuickTextEdit::updatePaintNode(node, data);
+ qCDebug(lcTests) << "updated root node" << ret;
+ populateLinePositions(ret);
+ return ret;
+ }
+
+ QList<int> linePositions;
+ int lastLinePosition;
+};
+
+void tst_qquicktextedit::initTestCase()
+{
+ QQmlDataTest::initTestCase();
+ qmlRegisterType<NodeCheckerTextEdit>("Qt.test", 1, 0, "NodeCheckerTextEdit");
+}
+
void tst_qquicktextedit::cleanup()
{
// ensure not even skipped tests with custom input context leave it dangling
@@ -935,7 +988,7 @@ void tst_qquicktextedit::hAlignVisual()
// Try to check whether alignment works by checking the number of black
// pixels in the thirds of the grabbed image.
- const int windowWidth = 200;
+ const int windowWidth = view.width();
const int textWidth = qCeil(text->implicitWidth());
QVERIFY2(textWidth < windowWidth, "System font too large.");
const int sectionWidth = textWidth / 3;
@@ -983,7 +1036,7 @@ void tst_qquicktextedit::hAlignVisual()
{
// Left Align
QImage image = view.grabWindow();
- int x = qCeil(text->implicitWidth());
+ int x = qCeil(text->implicitWidth() * view.devicePixelRatio());
int left = numberOfNonWhitePixels(0, x, image);
int right = numberOfNonWhitePixels(x, image.width() - x, image);
QVERIFY2(left > 0, msgNotGreaterThan(left, 0).constData());
@@ -993,7 +1046,7 @@ void tst_qquicktextedit::hAlignVisual()
// HCenter Align
text->setHAlign(QQuickText::AlignHCenter);
QImage image = view.grabWindow();
- int x1 = qFloor(image.width() - text->implicitWidth()) / 2;
+ int x1 = qFloor(image.width() - text->implicitWidth() * view.devicePixelRatio()) / 2;
int x2 = image.width() - x1;
int left = numberOfNonWhitePixels(0, x1, image);
int mid = numberOfNonWhitePixels(x1, x2 - x1, image);
@@ -1006,7 +1059,7 @@ void tst_qquicktextedit::hAlignVisual()
// Right Align
text->setHAlign(QQuickText::AlignRight);
QImage image = view.grabWindow();
- int x = image.width() - qCeil(text->implicitWidth());
+ int x = image.width() - qCeil(text->implicitWidth() * view.devicePixelRatio());
int left = numberOfNonWhitePixels(0, x, image);
int right = numberOfNonWhitePixels(x, image.width() - x, image);
QCOMPARE(left, 0);
@@ -2612,8 +2665,6 @@ void tst_qquicktextedit::linkHover()
QCOMPARE(window.cursor().shape(), Qt::IBeamCursor);
QCOMPARE(hover.last()[0].toString(), QString());
- texteditObject->setCursor(Qt::OpenHandCursor);
-
QCursor::setPos(linkPos);
QTRY_COMPARE(hover.count(), 3);
QCOMPARE(window.cursor().shape(), Qt::PointingHandCursor);
@@ -2621,7 +2672,7 @@ void tst_qquicktextedit::linkHover()
QCursor::setPos(textPos);
QTRY_COMPARE(hover.count(), 4);
- QCOMPARE(window.cursor().shape(), Qt::OpenHandCursor);
+ QCOMPARE(window.cursor().shape(), Qt::IBeamCursor);
QCOMPARE(hover.last()[0].toString(), QString());
}
#endif
@@ -3826,6 +3877,40 @@ void tst_qquicktextedit::largeTextObservesViewport()
QCOMPARE(textPriv->cursorItem->isVisible(), textPriv->renderedRegion.intersects(textItem->cursorRectangle()));
}
+void tst_qquicktextedit::renderingAroundSelection()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("threeLines.qml")));
+ NodeCheckerTextEdit *textItem = qmlobject_cast<NodeCheckerTextEdit*>(window.rootObject());
+ QVERIFY(textItem);
+ QTRY_VERIFY(textItem->linePositions.count() > 0);
+ const auto linePositions = textItem->linePositions;
+ const int lastLinePosition = textItem->lastLinePosition;
+ QQuickTextEditPrivate *textPriv = QQuickTextEditPrivate::get(textItem);
+ QSignalSpy renderSpy(&window, &QQuickWindow::afterRendering);
+
+ if (lcTests().isDebugEnabled())
+ QTest::qWait(500); // for visual check; not needed in CI
+
+ const int renderCount = renderSpy.count();
+ QPoint p1 = textItem->mapToScene(textItem->positionToRectangle(8).center()).toPoint();
+ QPoint p2 = textItem->mapToScene(textItem->positionToRectangle(10).center()).toPoint();
+ qCDebug(lcTests) << "drag from" << p1 << "to" << p2;
+ QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p1);
+ QTest::mouseMove(&window, p2);
+ QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p2);
+ // ensure that QQuickTextEdit::updatePaintNode() has a chance to run
+ QTRY_VERIFY(renderSpy.count() > renderCount);
+
+ if (lcTests().isDebugEnabled())
+ QTest::qWait(500); // for visual check; not needed in CI
+
+ qCDebug(lcTests) << "TextEdit's nodes" << textPriv->textNodeMap;
+ qCDebug(lcTests) << "font" << textItem->font() << "line positions" << textItem->linePositions << "should be" << linePositions;
+ QCOMPARE(textItem->lastLinePosition, lastLinePosition);
+ QTRY_COMPARE(textItem->linePositions, linePositions);
+}
+
void tst_qquicktextedit::signal_editingfinished()
{
QQuickView *window = new QQuickView(nullptr);
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index fdaaaaca5e..438e6ede02 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -1247,6 +1247,7 @@ void tst_qquickwindow::mouseFromTouch_basic()
QCOMPARE(item->lastVelocityFromMouseMove, velocity);
// QVERIFY(item->lastMouseCapabilityFlags.testFlag(QInputDevice::Capability::Velocity)); // TODO
+ QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); // avoid generating a double-click
// Now the same with a transformation.
item->setRotation(90); // clockwise
QMutableEventPoint::setState(points[0], QEventPoint::State::Pressed);
diff --git a/tests/auto/quickcontrols2/controls/data/tst_container.qml b/tests/auto/quickcontrols2/controls/data/tst_container.qml
index be2b9a12ba..5fa3137ade 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_container.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_container.qml
@@ -169,7 +169,7 @@ TestCase {
// don't crash (QTBUG-61310)
function test_repeater(data) {
- var control = createTemporaryObject(data.component)
+ var control = createTemporaryObject(data.component, testCase)
verify(control)
compare(control.itemAt(0).objectName, "0")
@@ -213,4 +213,86 @@ TestCase {
wait(1)
verify(item3)
}
+
+ Component {
+ id: contentItemDeletionOrder1
+
+ Item {
+ objectName: "parentItem"
+
+ Item {
+ id: item
+ objectName: "contentItem"
+ }
+ Container {
+ objectName: "control"
+ contentItem: item
+ }
+ }
+ }
+
+ Component {
+ id: contentItemDeletionOrder2
+
+ Item {
+ objectName: "parentItem"
+
+ Container {
+ objectName: "control"
+ contentItem: item
+ }
+ Item {
+ id: item
+ objectName: "contentItem"
+ }
+ }
+ }
+
+ function test_contentItemDeletionOrder() {
+ var control1 = createTemporaryObject(contentItemDeletionOrder1, testCase)
+ verify(control1)
+ var control2 = createTemporaryObject(contentItemDeletionOrder2, testCase)
+ verify(control2)
+ }
+
+ Component {
+ id: backgroundDeletionOrder1
+
+ Item {
+ objectName: "parentItem"
+
+ Item {
+ id: item
+ objectName: "backgroundItem"
+ }
+ Container {
+ objectName: "control"
+ background: item
+ }
+ }
+ }
+
+ Component {
+ id: backgroundDeletionOrder2
+
+ Item {
+ objectName: "parentItem"
+
+ Container {
+ objectName: "control"
+ background: item
+ }
+ Item {
+ id: item
+ objectName: "backgroundItem"
+ }
+ }
+ }
+
+ function test_backgroundDeletionOrder() {
+ var control1 = createTemporaryObject(backgroundDeletionOrder1, testCase)
+ verify(control1)
+ var control2 = createTemporaryObject(backgroundDeletionOrder2, testCase)
+ verify(control2)
+ }
}
diff --git a/tests/auto/quickcontrols2/controls/data/tst_control.qml b/tests/auto/quickcontrols2/controls/data/tst_control.qml
index 4afa719ef6..e12cbbf4ef 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_control.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_control.qml
@@ -1414,4 +1414,86 @@ TestCase {
compare(control.background.width, 100)
compare(control.background.height, 100)
}
+
+ Component {
+ id: contentItemDeletionOrder1
+
+ Item {
+ objectName: "parentItem"
+
+ Item {
+ id: item
+ objectName: "contentItem"
+ }
+ Control {
+ objectName: "control"
+ contentItem: item
+ }
+ }
+ }
+
+ Component {
+ id: contentItemDeletionOrder2
+
+ Item {
+ objectName: "parentItem"
+
+ Control {
+ objectName: "control"
+ contentItem: item
+ }
+ Item {
+ id: item
+ objectName: "contentItem"
+ }
+ }
+ }
+
+ function test_contentItemDeletionOrder() {
+ var control1 = createTemporaryObject(contentItemDeletionOrder1, testCase)
+ verify(control1)
+ var control2 = createTemporaryObject(contentItemDeletionOrder2, testCase)
+ verify(control2)
+ }
+
+ Component {
+ id: backgroundDeletionOrder1
+
+ Item {
+ objectName: "parentItem"
+
+ Item {
+ id: item
+ objectName: "backgroundItem"
+ }
+ Control {
+ objectName: "control"
+ background: item
+ }
+ }
+ }
+
+ Component {
+ id: backgroundDeletionOrder2
+
+ Item {
+ objectName: "parentItem"
+
+ Control {
+ objectName: "control"
+ background: item
+ }
+ Item {
+ id: item
+ objectName: "backgroundItem"
+ }
+ }
+ }
+
+ function test_backgroundDeletionOrder() {
+ var control1 = createTemporaryObject(backgroundDeletionOrder1, testCase)
+ verify(control1)
+ var control2 = createTemporaryObject(backgroundDeletionOrder2, testCase)
+ verify(control2)
+ }
}
diff --git a/tests/auto/quickcontrols2/controls/data/tst_dialogbuttonbox.qml b/tests/auto/quickcontrols2/controls/data/tst_dialogbuttonbox.qml
index 7d4c5d9e4d..57216ed11c 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_dialogbuttonbox.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_dialogbuttonbox.qml
@@ -528,4 +528,86 @@ TestCase {
control.destroy()
}
}
+
+ Component {
+ id: contentItemDeletionOrder1
+
+ Item {
+ objectName: "parentItem"
+
+ Item {
+ id: item
+ objectName: "contentItem"
+ }
+ DialogButtonBox {
+ objectName: "control"
+ contentItem: item
+ }
+ }
+ }
+
+ Component {
+ id: contentItemDeletionOrder2
+
+ Item {
+ objectName: "parentItem"
+
+ DialogButtonBox {
+ objectName: "control"
+ contentItem: item
+ }
+ Item {
+ id: item
+ objectName: "contentItem"
+ }
+ }
+ }
+
+ function test_contentItemDeletionOrder() {
+ var control1 = createTemporaryObject(contentItemDeletionOrder1, testCase)
+ verify(control1)
+ var control2 = createTemporaryObject(contentItemDeletionOrder2, testCase)
+ verify(control2)
+ }
+
+ Component {
+ id: backgroundDeletionOrder1
+
+ Item {
+ objectName: "parentItem"
+
+ Item {
+ id: item
+ objectName: "backgroundItem"
+ }
+ DialogButtonBox {
+ objectName: "control"
+ background: item
+ }
+ }
+ }
+
+ Component {
+ id: backgroundDeletionOrder2
+
+ Item {
+ objectName: "parentItem"
+
+ DialogButtonBox {
+ objectName: "control"
+ background: item
+ }
+ Item {
+ id: item
+ objectName: "backgroundItem"
+ }
+ }
+ }
+
+ function test_backgroundDeletionOrder() {
+ var control1 = createTemporaryObject(backgroundDeletionOrder1, testCase)
+ verify(control1)
+ var control2 = createTemporaryObject(backgroundDeletionOrder2, testCase)
+ verify(control2)
+ }
}
diff --git a/tests/auto/quickcontrols2/controls/data/tst_popup.qml b/tests/auto/quickcontrols2/controls/data/tst_popup.qml
index 0d2ec72ee2..ddf3150877 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_popup.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_popup.qml
@@ -1004,45 +1004,45 @@ TestCase {
control.contentItem.implicitWidth = 10
compare(control.implicitWidth, 10 + control.leftPadding + control.rightPadding)
compare(control.width, control.implicitWidth)
- compare(control.contentItem.width, control.width - control.leftPadding - control.rightPadding)
+ compare(control.contentItem.width, control.availableWidth)
control.contentItem.implicitHeight = 20
compare(control.implicitHeight, 20 + control.topPadding + control.bottomPadding)
compare(control.height, control.implicitHeight)
- compare(control.contentItem.height, control.height - control.topPadding - control.bottomPadding)
+ compare(control.contentItem.height, control.availableHeight)
// implicit size of the popup
control.implicitWidth = 30
compare(control.implicitWidth, 30)
compare(control.width, 30)
- compare(control.contentItem.width, control.width - control.leftPadding - control.rightPadding)
+ compare(control.contentItem.width, control.availableWidth)
control.implicitHeight = 40
compare(control.implicitHeight, 40)
compare(control.height, 40)
- compare(control.contentItem.height, control.height - control.topPadding - control.bottomPadding)
+ compare(control.contentItem.height, control.availableHeight)
// set explicit size
control.width = 50
compare(control.implicitWidth, 30)
compare(control.width, 50)
- compare(control.contentItem.width, control.width - control.leftPadding - control.rightPadding)
+ compare(control.contentItem.width, control.availableWidth)
control.height = 60
compare(control.implicitHeight, 40)
compare(control.height, 60)
- compare(control.contentItem.height, control.height - control.topPadding - control.bottomPadding)
+ compare(control.contentItem.height, control.availableHeight)
// reset explicit size
control.width = undefined
compare(control.implicitWidth, 30)
compare(control.width, 30)
- compare(control.contentItem.width, control.width - control.leftPadding - control.rightPadding)
+ compare(control.contentItem.width, control.availableWidth)
control.height = undefined
compare(control.implicitHeight, 40)
compare(control.height, 40)
- compare(control.contentItem.height, control.height - control.topPadding - control.bottomPadding)
+ compare(control.contentItem.height, control.availableHeight)
}
function test_visible() {
diff --git a/tests/auto/quickcontrols2/controls/data/tst_scrollview.qml b/tests/auto/quickcontrols2/controls/data/tst_scrollview.qml
index 6e09c65e70..dc9eff0456 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_scrollview.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_scrollview.qml
@@ -61,7 +61,7 @@ TestCase {
name: "ScrollView"
Component {
- id: signalSpy
+ id: signalSpyComponent
SignalSpy { }
}
@@ -581,4 +581,95 @@ TestCase {
verify(newHorizontalScrollBar.visible)
verify(!oldHorizontalScrollBar.visible)
}
+
+ Component {
+ id: mouseAreaWheelComponent
+
+ MouseArea {
+ anchors.fill: parent
+
+ property alias scrollView: scrollView
+ property alias flickable: flickable
+
+ ScrollView {
+ id: scrollView
+ anchors.fill: parent
+ wheelEnabled: false
+
+ Flickable {
+ id: flickable
+ contentHeight: 1000
+
+ Text {
+ text: "Test"
+ width: 500
+ height: 1000
+ }
+ }
+ }
+ }
+ }
+
+ // If a ScrollView containing a Flickable sets wheelEnabled to false,
+ // neither item should consume wheel events.
+ function test_wheelEnabled() {
+ let mouseArea = createTemporaryObject(mouseAreaWheelComponent, testCase)
+ verify(mouseArea)
+
+ let mouseWheelSpy = signalSpyComponent.createObject(mouseArea,
+ { target: mouseArea, signalName: "wheel" })
+ verify(mouseWheelSpy.valid)
+
+ let scrollView = mouseArea.scrollView
+ mouseWheel(scrollView, scrollView.width / 2, scrollView.height / 2, 0, 120)
+ compare(mouseWheelSpy.count, 1)
+ compare(mouseArea.flickable.contentY, 0)
+ }
+
+ Component {
+ id: bindingToContentItemAndStandaloneFlickable
+
+ Item {
+ width: 200
+ height: 200
+
+ property alias scrollView: scrollView
+
+ ScrollView {
+ id: scrollView
+ anchors.fill: parent
+ contentItem: listView
+
+ property Item someBinding: contentItem
+ }
+ ListView {
+ id: listView
+ model: 10
+ delegate: ItemDelegate {
+ text: modelData
+ width: listView.width
+ }
+ }
+ }
+ }
+
+ // Tests that scroll bars show up for a ScrollView where
+ // - its contentItem is declared as a standalone, separate item
+ // - there is a binding to contentItem (which causes a default Flickable to be created)
+ function test_bindingToContentItemAndStandaloneFlickable() {
+ let root = createTemporaryObject(bindingToContentItemAndStandaloneFlickable, testCase)
+ verify(root)
+
+ let control = root.scrollView
+ let verticalScrollBar = control.ScrollBar.vertical
+ let horizontalScrollBar = control.ScrollBar.horizontal
+ compare(verticalScrollBar.parent, control)
+ compare(horizontalScrollBar.parent, control)
+ verify(verticalScrollBar.visible)
+ verify(horizontalScrollBar.visible)
+
+ mouseDrag(verticalScrollBar, verticalScrollBar.width / 2, verticalScrollBar.height / 2, 0, 50)
+ verify(verticalScrollBar.active)
+ verify(horizontalScrollBar.active)
+ }
}
diff --git a/tests/auto/quickcontrols2/controls/data/tst_selectionrectangle.qml b/tests/auto/quickcontrols2/controls/data/tst_selectionrectangle.qml
index 437e8ec7f2..210c6594cd 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_selectionrectangle.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_selectionrectangle.qml
@@ -337,7 +337,7 @@ TestCase {
}
// Drag on the bottom right handle, so that the selection shrinks to cell 1, 1
- mouseDrag(tableView, cellWidth * 2, cellHeight * 2, -cellWidth / 2, -cellHeight / 2, Qt.LeftButton)
+ mouseDrag(tableView, (cellWidth * 3) - 1, (cellHeight * 3) - 1, -cellWidth, -cellHeight, Qt.LeftButton)
compare(tableView.selectionModel.selectedIndexes.length, 1)
verify(tableView.selectionModel.isSelected(tableView.model.index(1, 1)))
}
diff --git a/tests/auto/quickcontrols2/controls/data/tst_splitview.qml b/tests/auto/quickcontrols2/controls/data/tst_splitview.qml
index b9ace27ee3..6004f9eb44 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_splitview.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_splitview.qml
@@ -1883,6 +1883,436 @@ TestCase {
verify(firstItem.height > firstItemOriginalHeight)
}
+ Component {
+ id: splitViewHandleContainmentMaskComponent
+
+ MouseArea {
+ property alias mouseArea1: mouseArea1
+ property alias mouseArea2: mouseArea2
+ property alias splitView: splitView
+
+ anchors.fill: parent
+ hoverEnabled: true
+ acceptedButtons: Qt.LeftButton
+
+ Rectangle {
+ anchors.fill: parent
+ color: 'green'
+ opacity: 0.3
+ }
+
+ SplitView {
+ id: splitView
+
+ anchors {
+ fill: parent
+ margins: 100
+ }
+
+ handle: Rectangle {
+ id: handleRoot
+
+ readonly property bool containsMouse: SplitHandle.hovered
+ readonly property int defaultSize: 2
+
+ implicitWidth: splitView.orientation === Qt.Horizontal ? handleRoot.defaultSize : splitView.width
+ implicitHeight: splitView.orientation === Qt.Vertical ? handleRoot.defaultSize : splitView.height
+
+ color: 'red'
+ objectName: "handle"
+
+ Text {
+ objectName: "handleText_" + text
+ text: parent.x + "," + parent.y + " " + parent.width + "x" + parent.height
+ color: "black"
+ anchors.centerIn: parent
+ rotation: 90
+ }
+
+ containmentMask: Item {
+ readonly property real extraOverflow: 20
+
+ x: splitView.orientation === Qt.Horizontal ? -extraOverflow : 0
+ y: splitView.orientation === Qt.Horizontal ? 0 : -extraOverflow
+ width: splitView.orientation === Qt.Horizontal ? handleRoot.defaultSize + (extraOverflow * 2): handleRoot.width
+ height: splitView.orientation === Qt.Horizontal ? handleRoot.height : handleRoot.defaultSize + (extraOverflow * 2)
+ }
+ }
+
+ MouseArea {
+ id: mouseArea1
+
+ SplitView.fillHeight: splitView.orientation === Qt.Horizontal
+ SplitView.fillWidth: splitView.orientation === Qt.Vertical
+ SplitView.preferredWidth: splitView.orientation === Qt.Horizontal ? parent.width / 2 : undefined
+ SplitView.preferredHeight: splitView.orientation === Qt.Vertical ? parent.height / 2 : undefined
+ hoverEnabled: true
+
+ Rectangle {
+ anchors.fill: parent
+ color: 'cyan'
+ opacity: 0.3
+ }
+ }
+
+ MouseArea {
+ id: mouseArea2
+
+ SplitView.fillHeight: splitView.orientation === Qt.Horizontal
+ SplitView.fillWidth: splitView.orientation === Qt.Vertical
+ SplitView.preferredWidth: splitView.orientation === Qt.Horizontal ? parent.width / 2 : undefined
+ SplitView.preferredHeight: splitView.orientation === Qt.Vertical ? parent.height / 2 : undefined
+ hoverEnabled: true
+
+ Rectangle {
+ anchors.fill: parent
+ color: 'cyan'
+ opacity: 0.3
+ }
+ }
+ }
+ }
+ }
+
+ function test_handleContainmentMask_data() {
+ const data = [
+ {
+ tag: "handleContainmentMaskHorizontalLeftEdgeDragRight",
+ orientation: Qt.Horizontal,
+ press: {
+ x: (handle) => handle.containmentMask.x,
+ y: (handle) => handle.height / 2
+ },
+ dx: 25,
+ dy: 0,
+ },
+ {
+ tag: "handleContainmentMaskHorizontalRightEdgeDragLeft",
+ orientation: Qt.Horizontal,
+ press: {
+ x: (handle) => handle.containmentMask.x,
+ y: (handle) => handle.height / 2
+ },
+ dx: -50,
+ dy: 0
+ },
+ {
+ tag: "handleContainmentMaskHorizontalTopEdgeDragRight",
+ orientation: Qt.Horizontal,
+ press: {
+ x: (handle) => handle.containmentMask.x + handle.containmentMask.width - 1,
+ y: (handle) => handle.height / 2
+ },
+ dx: 25,
+ dy: 0,
+ },
+ {
+ tag: "handleContainmentMaskHorizontalBottomEdgeDragLeft",
+ orientation: Qt.Horizontal,
+ press: {
+ x: (handle) => handle.containmentMask.x + handle.containmentMask.width - 1,
+ y: (handle) => handle.containmentMask.y
+ },
+ dx: -50,
+ dy: 0
+ },
+ {
+ tag: "handleContainmentMaskVerticalTopEdgeDragUp",
+ orientation: Qt.Vertical,
+ press: {
+ x: (handle) => handle.width / 2,
+ y: (handle) => handle.containmentMask.y
+ },
+ dx: 0,
+ dy: -40,
+ },
+ {
+ tag: "handleContainmentMaskVerticalTopEdgeDragDown",
+ orientation: Qt.Vertical,
+ press: {
+ x: (handle) => handle.width / 2,
+ y: (handle) => handle.containmentMask.y
+ },
+ dx: 0,
+ dy: 70
+ },
+ {
+ tag: "handleContainmentMaskVerticalBottomEdgeDragUp",
+ orientation: Qt.Vertical,
+ press: {
+ x: (handle) => handle.width / 2,
+ y: (handle) => handle.containmentMask.y + handle.containmentMask.height - 1
+ },
+ dx: 0,
+ dy: -40,
+ },
+ {
+ tag: "handleContainmentMaskVerticalBottomEdgeDragDown",
+ orientation: Qt.Vertical,
+ press: {
+ x: (handle) => handle.width / 2,
+ y: (handle) => handle.containmentMask.y + handle.containmentMask.height - 1
+ },
+ dx: 0,
+ dy: 70
+ }
+ ]
+
+ return data
+ }
+
+ function test_handleContainmentMask(data) {
+ const control = createTemporaryObject(splitViewHandleContainmentMaskComponent, testCase)
+ verify(control)
+ const splitView = control.splitView
+ splitView.orientation = data.orientation
+
+ const handle = findHandles(splitView)[0]
+ if (splitView.orientation === Qt.Vertical)
+ handle.height = handle.defaultHeight
+
+ verify(isPolishScheduled(splitView))
+ verify(waitForItemPolished(splitView))
+
+ const firstItem = control.mouseArea1
+ const secondItem = control.mouseArea2
+
+ const backgroundMouseAreaPress = signalSpyComponent.createObject(control,
+ { target: control, signalName: "onPressed" })
+
+ const mouseArea1Press = signalSpyComponent.createObject(firstItem,
+ { target: firstItem, signalName: "onPressed" })
+
+ const mouseArea2Press = signalSpyComponent.createObject(secondItem,
+ { target: secondItem, signalName: "onPressed" })
+
+ verify(backgroundMouseAreaPress.valid)
+ verify(mouseArea1Press.valid)
+ verify(mouseArea2Press.valid)
+
+ const firstItemWidthBeforeDrag = firstItem.width
+ const secondItemWidthBeforeDrag = secondItem.width
+ const firstItemHeightBeforeDrag = firstItem.height
+ const secondItemHeightBeforeDrag = secondItem.height
+
+ const dx = data.dx
+ const dy = data.dy
+ const pressX = data.press.x(handle)
+ const pressY = data.press.y(handle)
+
+ mousePress(handle, pressX, pressY, Qt.LeftButton)
+ mouseMove(handle, pressX + dx, pressY + dy, -1, Qt.LeftButton)
+ mouseRelease(handle, pressX + dx, pressY + dy, Qt.LeftButton)
+
+ compare(firstItem.width, firstItemWidthBeforeDrag + dx)
+ compare(secondItem.width, secondItemWidthBeforeDrag - dx)
+ compare(firstItem.height, firstItemHeightBeforeDrag + dy)
+ compare(secondItem.height, secondItemHeightBeforeDrag - dy)
+
+ compare(backgroundMouseAreaPress.count, 0)
+ compare(mouseArea1Press.count, 0)
+ compare(mouseArea2Press.count, 0)
+ }
+
+ function test_handleContainmentMaskHovered_data() {
+ const data = [
+ {
+ tag: "firstItemHorizontalHover",
+ orientation: Qt.Horizontal,
+ press: {
+ "x": (item) => item.width / 2,
+ "y": (item) => item.height / 2
+ },
+ hoverItem: "firstItem",
+ expectedHover: {
+ "firstItem": true,
+ "handle": false,
+ "secondItem": false
+ }
+ },
+ {
+ tag: "handleHorizontalHoverOnTheLeft",
+ orientation: Qt.Horizontal,
+ press: {
+ "x": (item) => item.containmentMask.x,
+ "y": (item) => item.height / 2
+ },
+ hoverItem: "handle",
+ expectedHover: {
+ "firstItem": true,
+ "handle": true,
+ "secondItem": false
+ }
+ },
+ {
+ tag: "handleHorizontalHoverOnTheCenter",
+ orientation: Qt.Horizontal,
+ press: {
+ "x": (item) => item.width / 2,
+ "y": (item) => item.height / 2
+ },
+ hoverItem: "handle",
+ expectedHover: {
+ "firstItem": false,
+ "handle": true,
+ "secondItem": false
+ }
+ },
+ {
+ tag: "handleHorizontalHoverOnTheRight",
+ orientation: Qt.Horizontal,
+ press: {
+ "x": (item) => item.containmentMask.x + item.containmentMask.width - 1,
+ "y": (item) => item.height / 2
+ },
+ hoverItem: "handle",
+ expectedHover: {
+ "firstItem": false,
+ "handle": true,
+ "secondItem": true
+ }
+ },
+ {
+ tag: "secondItemHorizontalHover",
+ orientation: Qt.Horizontal,
+ press: {
+ "x": (item) => item.width / 2,
+ "y": (item) => item.height / 2
+ },
+ hoverItem: "secondItem",
+ expectedHover: {
+ "firstItem": false,
+ "handle": false,
+ "secondItem": true
+ }
+ },
+ {
+ tag: "firstItemVerticalHover",
+ orientation: Qt.Vertical,
+ press: {
+ "x": (item) => item.width / 2,
+ "y": (item) => item.height / 2
+ },
+ hoverItem: "firstItem",
+ expectedHover: {
+ "firstItem": true,
+ "handle": false,
+ "secondItem": false
+ }
+ },
+ {
+ tag: "handleVerticalHoverOnTheTop",
+ orientation: Qt.Vertical,
+ press: {
+ "x": (item) => item.width / 2,
+ "y": (item) => item.containmentMask.y
+ },
+ hoverItem: "handle",
+ expectedHover: {
+ "firstItem": true,
+ "handle": true,
+ "secondItem": false
+ }
+ },
+ {
+ tag: "handleVerticalHoverOnTheCenter",
+ orientation: Qt.Vertical,
+ press: {
+ "x": (item) => item.width / 2,
+ "y": (item) => item.height / 2
+ },
+ hoverItem: "handle",
+ expectedHover: {
+ "firstItem": false,
+ "handle": true,
+ "secondItem": false
+ }
+ },
+ {
+ tag: "handleVerticalHoverOnTheBottom",
+ orientation: Qt.Vertical,
+ press: {
+ "x": (item) => item.width / 2,
+ "y": (item) => item.containmentMask.y + item.containmentMask.height - 1
+ },
+ hoverItem: "handle",
+ expectedHover: {
+ "firstItem": false,
+ "handle": true,
+ "secondItem": true
+ }
+ },
+ {
+ tag: "secondItemVerticalHover",
+ orientation: Qt.Vertical,
+ press: {
+ "x": (item) => item.width / 2,
+ "y": (item) => item.height / 2
+ },
+ hoverItem: "secondItem",
+ expectedHover: {
+ "firstItem": false,
+ "handle": false,
+ "secondItem": true
+ }
+ }
+ ]
+
+ return data
+ }
+
+ function test_handleContainmentMaskHovered(data) {
+ if ((Qt.platform.pluginName === "offscreen") || (Qt.platform.pluginName === "minimal"))
+ skip("Mouse hovering not functional on offscreen/minimal platforms")
+
+ const control = createTemporaryObject(splitViewHandleContainmentMaskComponent, testCase)
+ verify(control)
+ const splitView = control.splitView
+ splitView.orientation = data.orientation
+
+ const handle = findHandles(splitView)[0]
+ if (splitView.orientation === Qt.Vertical)
+ handle.height = handle.defaultHeight
+
+ verify(isPolishScheduled(splitView))
+ verify(waitForItemPolished(splitView))
+
+ const firstItem = control.mouseArea1
+ const secondItem = control.mouseArea2
+
+ verify(!firstItem.containsMouse)
+ verify(!secondItem.containsMouse)
+ verify(!handle.containsMouse)
+
+ const actualItem = {
+ "firstItem": firstItem,
+ "secondItem": secondItem,
+ "handle": handle
+ }[data.hoverItem]
+
+ const pressX = data.press.x(actualItem)
+ const pressY = data.press.y(actualItem)
+
+ // Test fails if we don't do two moves for some reason...
+ mouseMove(actualItem, pressX, pressY)
+ mouseMove(actualItem, pressX, pressY)
+
+ compare(firstItem.containsMouse, data.expectedHover.firstItem)
+ compare(secondItem.containsMouse, data.expectedHover.secondItem)
+ compare(handle.containsMouse, data.expectedHover.handle)
+
+ // Hide SplitView, then all children shouldn't be hovered
+ control.visible = false
+
+ verify(isPolishScheduled(splitView))
+ verify(waitForItemPolished(splitView))
+
+ verify(!control.containsMouse)
+ verify(!firstItem.containsMouse)
+ verify(!secondItem.containsMouse)
+ verify(!handle.containsMouse)
+ }
+
function test_hoveredPressed() {
if ((Qt.platform.pluginName === "offscreen") || (Qt.platform.pluginName === "minimal"))
skip("Mouse hovering not functional on offscreen/minimal platforms")
diff --git a/tests/auto/quickcontrols2/controls/data/tst_stackview.qml b/tests/auto/quickcontrols2/controls/data/tst_stackview.qml
index 72450ebb49..e1b8fcb79c 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_stackview.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_stackview.qml
@@ -1558,4 +1558,37 @@ TestCase {
verify(control.currentItem.i === 42)
control.pop(StackView.Immediate)
}
+
+ // QTBUG-104491
+ // Tests that correctly set a busy state when the transition is stolen(canceled)
+ function test_continuousTransition() {
+ let redRect = createTemporaryObject(rectangleComponent, testCase, { color: "red" })
+ verify(redRect)
+ let blueRect = createTemporaryObject(rectangleComponent, testCase, { color: "blue" })
+ verify(blueRect)
+ let greenRect = createTemporaryObject(rectangleComponent, testCase, { color: "green" })
+ verify(greenRect)
+ let yellowRect = createTemporaryObject(rectangleComponent, testCase, { color: "yellow" })
+ verify(yellowRect)
+ let control = createTemporaryObject(qtbug96966_stackViewComponent, testCase,
+ { "anchors.fill": testCase, initialItem: redRect })
+ verify(control)
+
+ control.push(blueRect)
+ control.pop()
+ tryCompare(control, "busy", true)
+ tryCompare(control, "busy", false)
+
+ control.push(blueRect)
+ control.push(greenRect)
+ control.push(yellowRect)
+ tryCompare(control, "busy", true)
+ tryCompare(control, "busy", false)
+
+ control.pop()
+ control.pop()
+ control.pop()
+ tryCompare(control, "busy", true)
+ tryCompare(control, "busy", false)
+ }
}
diff --git a/tests/auto/quickcontrols2/controls/data/tst_tooltip.qml b/tests/auto/quickcontrols2/controls/data/tst_tooltip.qml
index 3b3e0b43c1..89e179575f 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_tooltip.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_tooltip.qml
@@ -296,7 +296,13 @@ TestCase {
function test_activateShortcutWhileToolTipVisible() {
if ((Qt.platform.pluginName === "offscreen")
|| (Qt.platform.pluginName === "minimal"))
- skip("Mouse hoovering not functional on offscreen/minimal platforms")
+ skip("Mouse hovering not functional on offscreen/minimal platforms")
+
+ // Window shortcuts (the default context for Shortcut) require the window to have focus.
+ var window = testCase.Window.window
+ verify(window)
+ window.requestActivate()
+ tryCompare(window, "active", true)
var root = createTemporaryObject(buttonAndShortcutComponent, testCase)
verify(root)
diff --git a/tests/auto/quickcontrols2/cursor/tst_cursor.cpp b/tests/auto/quickcontrols2/cursor/tst_cursor.cpp
index b3a44fa061..afc76a9a68 100644
--- a/tests/auto/quickcontrols2/cursor/tst_cursor.cpp
+++ b/tests/auto/quickcontrols2/cursor/tst_cursor.cpp
@@ -52,12 +52,13 @@ public:
tst_cursor();
private slots:
- void init();
+ void init() override;
void controls_data();
void controls();
void editable();
void pageIndicator();
void scrollBar();
+ void textArea();
};
tst_cursor::tst_cursor()
@@ -68,6 +69,8 @@ tst_cursor::tst_cursor()
void tst_cursor::init()
{
+ QQmlDataTest::init();
+
#if QT_CONFIG(cursor)
// Ensure mouse cursor was not left by previous tests where widgets
// will appear, as it could cause events and interfere with the tests.
@@ -225,6 +228,19 @@ void tst_cursor::scrollBar()
QCOMPARE(window->cursor().shape(), textArea->cursor().shape());
}
+// QTBUG-104089
+void tst_cursor::textArea()
+{
+ QQuickTextArea textArea;
+ QCOMPARE(textArea.cursor().shape(), Qt::IBeamCursor);
+
+ textArea.setReadOnly(true);
+ QCOMPARE(textArea.cursor().shape(), Qt::ArrowCursor);
+
+ textArea.setSelectByMouse(true);
+ QCOMPARE(textArea.cursor().shape(), Qt::IBeamCursor);
+}
+
QTEST_MAIN(tst_cursor)
#include "tst_cursor.moc"
diff --git a/tests/auto/quickcontrols2/customization/tst_customization.cpp b/tests/auto/quickcontrols2/customization/tst_customization.cpp
index b1fa3ea6a3..aa1c97b3e6 100644
--- a/tests/auto/quickcontrols2/customization/tst_customization.cpp
+++ b/tests/auto/quickcontrols2/customization/tst_customization.cpp
@@ -112,7 +112,7 @@ private slots:
void initTestCase() override;
void cleanupTestCase();
- void init();
+ void init() override;
void cleanup();
void creation_data();
@@ -208,8 +208,11 @@ extern "C" Q_DECL_EXPORT void qt_removeQObject(QObject *object)
}
}
+// We don't want to fail on warnings until QTBUG-98964 is fixed,
+// as we deliberately prevent deferred execution in some of the tests here,
+// which causes warnings.
tst_customization::tst_customization()
- : QQmlDataTest(QT_QMLTEST_DATADIR)
+ : QQmlDataTest(QT_QMLTEST_DATADIR, FailOnWarningsPolicy::DoNotFailOnWarnings)
{
}
@@ -228,6 +231,8 @@ void tst_customization::cleanupTestCase()
void tst_customization::init()
{
+ QQmlDataTest::init();
+
engine = new QQmlEngine(this);
engine->addImportPath(testFile("styles"));
diff --git a/tests/auto/quickcontrols2/platform/CMakeLists.txt b/tests/auto/quickcontrols2/platform/CMakeLists.txt
index 6633de5c02..8e6df7d2b4 100644
--- a/tests/auto/quickcontrols2/platform/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/platform/CMakeLists.txt
@@ -17,6 +17,7 @@ qt_internal_add_test(tst_platform
tst_platform.cpp
PUBLIC_LIBRARIES
Qt::Gui
+ Qt::Qml
TESTDATA ${test_data}
)
diff --git a/tests/auto/quickcontrols2/platform/data/tst_menu.qml b/tests/auto/quickcontrols2/platform/data/tst_menu.qml
index 7e38922255..aedf6529d7 100644
--- a/tests/auto/quickcontrols2/platform/data/tst_menu.qml
+++ b/tests/auto/quickcontrols2/platform/data/tst_menu.qml
@@ -51,6 +51,8 @@
import QtQuick
import QtTest
import Qt.labs.platform
+import QtQuick.Controls as Controls
+import org.qtproject.Test
TestCase {
id: testCase
@@ -75,6 +77,11 @@ TestCase {
signalName: "itemsChanged"
}
+ Component {
+ id: signalSpyComponent
+ SignalSpy {}
+ }
+
function init() {
verify(!itemsSpy.target)
compare(itemsSpy.count, 0)
@@ -261,4 +268,68 @@ TestCase {
compare(subMenu.title, "Title")
compare(subMenuItem.text, "Title")
}
+
+ Component {
+ id: disabledMenuItemAndActionComponent
+
+ Item {
+ property alias action: action
+ property alias menu: menu
+
+ Controls.Action {
+ id: action
+ shortcut: StandardKey.Copy
+ }
+
+ Menu {
+ id: menu
+
+ MenuItem {
+ enabled: !action.enabled
+ shortcut: StandardKey.Copy
+ text: "test"
+ }
+ }
+ }
+ }
+
+ function test_shortcuts() {
+ if (!TestHelper.shortcutsSupported)
+ skip("This test requires shortcut support")
+
+ let root = createTemporaryObject(disabledMenuItemAndActionComponent, testCase)
+ verify(root)
+ let menu = root.menu
+ let menuItem = menu.items[0]
+ verify(menuItem)
+ let action = root.action
+
+ let actionTriggeredSpy = signalSpyComponent.createObject(root,
+ { target: action, signalName: "triggered" })
+ verify(actionTriggeredSpy.valid)
+ let menuItemTriggeredSpy = signalSpyComponent.createObject(root,
+ { target: menuItem, signalName: "triggered" })
+ verify(menuItemTriggeredSpy.valid)
+
+ // Perform the shortcut; the Action should be triggered since the MenuItem is disabled.
+ keySequence(StandardKey.Copy)
+ compare(actionTriggeredSpy.count, 1)
+ compare(menuItemTriggeredSpy.count, 0)
+
+ // Disable the Action, enabling the MenuItem in the process.
+ action.enabled = false
+ verify(menuItem.enabled)
+ // Perform the shortcut; the MenuItem should be triggered since the Action is disabled.
+ keySequence(StandardKey.Copy)
+ compare(actionTriggeredSpy.count, 1)
+ compare(menuItemTriggeredSpy.count, 1)
+
+ // Re-enable the Action, disabling the MenuItem in the process.
+ action.enabled = true
+ verify(!menuItem.enabled)
+ // Perform the shortcut; the Action should be triggered since the MenuItem is disabled.
+ keySequence(StandardKey.Copy)
+ compare(actionTriggeredSpy.count, 2)
+ compare(menuItemTriggeredSpy.count, 1)
+ }
}
diff --git a/tests/auto/quickcontrols2/platform/tst_platform.cpp b/tests/auto/quickcontrols2/platform/tst_platform.cpp
index b67042e2f9..bd4db00cd6 100644
--- a/tests/auto/quickcontrols2/platform/tst_platform.cpp
+++ b/tests/auto/quickcontrols2/platform/tst_platform.cpp
@@ -26,5 +26,31 @@
**
****************************************************************************/
+#include <QtQml/qqmlengine.h>
#include <QtQuickTest/quicktest.h>
-QUICK_TEST_MAIN(tst_platform)
+
+class Setup : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool shortcutsSupported READ areShortcutsSupported CONSTANT FINAL)
+
+public:
+ bool areShortcutsSupported() const
+ {
+#if QT_CONFIG(shortcut)
+ return true;
+#else
+ return false;
+#endif
+ }
+
+public slots:
+ void qmlEngineAvailable(QQmlEngine *)
+ {
+ qmlRegisterSingletonInstance("org.qtproject.Test", 1, 0, "TestHelper", this);
+ }
+};
+
+QUICK_TEST_MAIN_WITH_SETUP(tst_platform, Setup)
+
+#include "tst_platform.moc"
diff --git a/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp b/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp
index b3a5b9da33..d2593ef2e4 100644
--- a/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp
+++ b/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp
@@ -34,6 +34,8 @@
#include <QtGui/qstylehints.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qpa/qwindowsysteminterface.h>
+#include <QtGui/qpa/qplatformintegration.h>
+#include <QtGui/private/qguiapplication_p.h>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qquickflickable_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -356,8 +358,7 @@ void tst_QQuickDrawer::position()
QQuickApplicationWindow *window = helper.appWindow;
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickDrawer *drawer = helper.appWindow->property("drawer").value<QQuickDrawer*>();
QVERIFY(drawer);
@@ -404,8 +405,7 @@ void tst_QQuickDrawer::dragMargin()
QQuickApplicationWindow *window = helper.appWindow;
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickDrawer *drawer = helper.appWindow->property("drawer").value<QQuickDrawer*>();
QVERIFY(drawer);
@@ -552,7 +552,7 @@ void tst_QQuickDrawer::dragHandlerInteraction()
auto window = helper.appWindow;;
QVERIFY(window);
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QTest::mousePress(window, Qt::LeftButton, Qt::KeyboardModifiers(), QPoint(250, 250));
QTest::mouseMove(window, QPoint(100, 100));
QTest::mouseRelease(window, Qt::LeftButton, Qt::KeyboardModifiers(), QPoint(100, 100));
@@ -579,8 +579,7 @@ void tst_QQuickDrawer::hover()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickDrawer *drawer = window->property("drawer").value<QQuickDrawer*>();
QVERIFY(drawer);
@@ -906,7 +905,7 @@ void tst_QQuickDrawer::multiTouch()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickOverlay *overlay = QQuickOverlay::overlay(window);
QVERIFY(overlay);
@@ -1060,6 +1059,9 @@ void tst_QQuickDrawer::interactive_data()
void tst_QQuickDrawer::interactive()
{
+ if (!(QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)))
+ QSKIP("Window activation is not supported");
+
QFETCH(QString, source);
QQuickControlsApplicationHelper helper(this, source);
QVERIFY2(helper.ready, helper.failureMessage());
@@ -1185,7 +1187,7 @@ void tst_QQuickDrawer::dragOverModalShadow()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickDrawer *drawer = window->property("drawer").value<QQuickDrawer *>();
QVERIFY(drawer);
@@ -1242,7 +1244,7 @@ void tst_QQuickDrawer::nonModal()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickDrawer *drawer = window->property("drawer").value<QQuickDrawer *>();
QVERIFY(drawer);
@@ -1326,7 +1328,7 @@ void tst_QQuickDrawer::slider()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickDrawer *drawer = window->property("drawer").value<QQuickDrawer *>();
QVERIFY(drawer);
@@ -1374,7 +1376,7 @@ void tst_QQuickDrawer::topEdgeScreenEdge()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickDrawer *drawer = window->property("drawer").value<QQuickDrawer *>();
QVERIFY(drawer);
diff --git a/tests/auto/quickcontrols2/qquickheaderview/tst_qquickheaderview.cpp b/tests/auto/quickcontrols2/qquickheaderview/tst_qquickheaderview.cpp
index 62593f60cc..82187752f6 100644
--- a/tests/auto/quickcontrols2/qquickheaderview/tst_qquickheaderview.cpp
+++ b/tests/auto/quickcontrols2/qquickheaderview/tst_qquickheaderview.cpp
@@ -211,7 +211,7 @@ public:
private slots:
void initTestCase() override;
void cleanupTestCase();
- void init();
+ void init() override;
void cleanup();
void defaults();
@@ -254,6 +254,8 @@ void tst_QQuickHeaderView::cleanupTestCase()
void tst_QQuickHeaderView::init()
{
+ QQmlDataTest::init();
+
engine = new QQmlEngine(this);
}
diff --git a/tests/auto/quickcontrols2/qquickmenu/BLACKLIST b/tests/auto/quickcontrols2/qquickmenu/BLACKLIST
index 8f69450b4f..9e2954b3f5 100644
--- a/tests/auto/quickcontrols2/qquickmenu/BLACKLIST
+++ b/tests/auto/quickcontrols2/qquickmenu/BLACKLIST
@@ -1,10 +1,3 @@
-[popup]
-macos # Can't control cursor (QTBUG-76312)
-
-# QTBUG-87018
-[subMenuDisabledMouse]
-macos
-
# QTBUG-98491
[disableWhenTriggered]
macos
diff --git a/tests/auto/quickcontrols2/qquickmenu/data/customMenuCullItems.qml b/tests/auto/quickcontrols2/qquickmenu/data/customMenuCullItems.qml
new file mode 100644
index 0000000000..0f56ecdd87
--- /dev/null
+++ b/tests/auto/quickcontrols2/qquickmenu/data/customMenuCullItems.qml
@@ -0,0 +1,55 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+ApplicationWindow {
+ width: 200
+ height: 200
+ property alias menu: menu
+
+ Menu {
+ id: menu
+
+ contentItem: FocusScope {
+ implicitHeight: view.implicitHeight
+ Button {
+ anchors {
+ top: parent.top
+ topMargin: 5
+ horizontalCenter: parent.horizontalCenter
+ }
+ z: 1
+ text: "Button Up"
+ visible: view.interactive
+ }
+ ListView {
+ id: view
+ width: parent.width
+ implicitHeight: Math.min(contentHeight, 300)
+ model: menu.contentModel
+
+ clip: true
+ currentIndex: menu.currentIndex
+ ScrollIndicator.vertical: ScrollIndicator {}
+ }
+ Button {
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: 5
+ horizontalCenter: parent.horizontalCenter
+ }
+ z: 1
+ text: "Button Down"
+ visible: view.interactive
+ }
+ }
+
+ Repeater {
+ model: 20
+ MenuItem {
+ objectName: "Item: " + modelData
+ text: objectName
+ }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols2/qquickmenu/data/customMenuUseRepeaterAsTheContentItem.qml b/tests/auto/quickcontrols2/qquickmenu/data/customMenuUseRepeaterAsTheContentItem.qml
new file mode 100644
index 0000000000..bfa8f66be5
--- /dev/null
+++ b/tests/auto/quickcontrols2/qquickmenu/data/customMenuUseRepeaterAsTheContentItem.qml
@@ -0,0 +1,65 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+ApplicationWindow {
+ width: 200
+ height: 200
+ property alias menu: menu
+
+ Menu {
+ id: menu
+ visible: true
+
+ contentItem: FocusScope {
+ implicitHeight: flickable.height
+
+ Button {
+ anchors {
+ top: parent.top
+ topMargin: 5
+ horizontalCenter: parent.horizontalCenter
+ }
+ z: 1
+ text: "Button Up"
+ }
+
+ Flickable {
+ id: flickable
+ width: parent.width
+ height: Math.min(contentHeight, 300)
+ contentHeight: repeaterLayout.implicitHeight
+ clip: true
+
+ ScrollIndicator.vertical: ScrollIndicator {}
+
+ ColumnLayout {
+ id: repeaterLayout
+ width: parent.width
+
+ Repeater {
+ model: menu.contentModel
+ }
+ }
+ }
+
+ Button {
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: 5
+ horizontalCenter: parent.horizontalCenter
+ }
+ z: 1
+ text: "Button Down"
+ }
+ }
+
+ Repeater {
+ model: 20
+ MenuItem {
+ objectName: "Item: " + modelData
+ text: objectName
+ }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols2/qquickmenu/data/subMenus.qml b/tests/auto/quickcontrols2/qquickmenu/data/subMenus.qml
index fad59ee6d1..8227a29fb2 100644
--- a/tests/auto/quickcontrols2/qquickmenu/data/subMenus.qml
+++ b/tests/auto/quickcontrols2/qquickmenu/data/subMenus.qml
@@ -69,6 +69,7 @@ ApplicationWindow {
}
Menu {
+ overlap: 0
id: subMenu1
objectName: "subMenu1"
title: "Sub Menu 1"
@@ -78,6 +79,7 @@ ApplicationWindow {
objectName: "subMenuItem1"
text: "Sub 1"
}
+
MenuItem {
id: subMenuItem2
objectName: "subMenuItem2"
@@ -85,6 +87,7 @@ ApplicationWindow {
}
Menu {
+ overlap: 0
id: subSubMenu1
objectName: "subSubMenu1"
title: "Sub Sub Menu 1"
diff --git a/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp b/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp
index 9806eff7c3..12707998d7 100644
--- a/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp
+++ b/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp
@@ -33,6 +33,8 @@
#include <QtGui/qkeysequence.h>
#endif
#include <QtGui/qstylehints.h>
+#include <QtGui/qpa/qplatformintegration.h>
+#include <QtGui/private/qguiapplication_p.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlcontext.h>
@@ -74,7 +76,9 @@ private slots:
void menuSeparator();
void repeater();
void order();
+#if QT_CONFIG(cursor)
void popup();
+#endif
void actions();
#if QT_CONFIG(shortcut)
void actionShortcuts();
@@ -103,6 +107,11 @@ private slots:
void menuItemWidthAfterImplicitWidthChanged();
void menuItemWidthAfterRetranslate();
void giveMenuItemFocusOnButtonPress();
+ void customMenuCullItems();
+ void customMenuUseRepeaterAsTheContentItem();
+
+private:
+ static bool hasWindowActivation();
};
tst_QQuickMenu::tst_QQuickMenu()
@@ -110,6 +119,11 @@ tst_QQuickMenu::tst_QQuickMenu()
{
}
+bool tst_QQuickMenu::hasWindowActivation()
+{
+ return (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation));
+}
+
void tst_QQuickMenu::defaults()
{
QQuickControlsApplicationHelper helper(this, QLatin1String("applicationwindow.qml"));
@@ -153,6 +167,9 @@ void tst_QQuickMenu::count()
void tst_QQuickMenu::mouse()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
QSKIP("Mouse hovering not functional on offscreen/minimal platforms");
@@ -183,14 +200,14 @@ void tst_QQuickMenu::mouse()
// Ensure that presses cause the current index to change,
// so that the highlight acts as a way of illustrating press state.
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier,
- QPoint(menu->leftPadding() + firstItem->width() / 2, menu->topPadding() + firstItem->height() / 2));
+ QPoint(menu->x() + menu->leftPadding() + firstItem->width() / 2, menu->y() + menu->topPadding() + firstItem->height() / 2));
QVERIFY(firstItem->hasActiveFocus());
QCOMPARE(menu->currentIndex(), 0);
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(0));
QVERIFY(menu->isVisible());
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier,
- QPoint(menu->leftPadding() + firstItem->width() / 2, menu->topPadding() + firstItem->height() / 2));
+ QPoint(menu->x() + menu->leftPadding() + firstItem->width() / 2, menu->y() + menu->topPadding() + firstItem->height() / 2));
QCOMPARE(clickedSpy.count(), 1);
QCOMPARE(triggeredSpy.count(), 1);
QTRY_COMPARE(visibleSpy.count(), 1);
@@ -232,8 +249,8 @@ void tst_QQuickMenu::mouse()
if (!hoverItem || !hoverItem->isVisible() || hoverItem == prevHoverItem)
continue;
QTest::mouseMove(window, QPoint(
- menu->leftPadding() + hoverItem->x() + hoverItem->width() / 2,
- menu->topPadding() + hoverItem->y() + hoverItem->height() / 2));
+ menu->x() + menu->leftPadding() + hoverItem->x() + hoverItem->width() / 2,
+ menu->y() + menu->topPadding() + hoverItem->y() + hoverItem->height() / 2));
QTRY_VERIFY(hoverItem->property("highlighted").toBool());
if (prevHoverItem)
QVERIFY(!prevHoverItem->property("highlighted").toBool());
@@ -265,7 +282,7 @@ void tst_QQuickMenu::pressAndHold()
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
QVERIFY(menu);
@@ -282,6 +299,9 @@ void tst_QQuickMenu::pressAndHold()
void tst_QQuickMenu::contextMenuKeyboard()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)
QSKIP("This platform only allows tab focus for text controls");
@@ -468,6 +488,9 @@ void tst_QQuickMenu::contextMenuKeyboard()
// QTBUG-70181
void tst_QQuickMenu::disabledMenuItemKeyNavigation()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)
QSKIP("This platform only allows tab focus for text controls");
@@ -532,6 +555,9 @@ void tst_QQuickMenu::disabledMenuItemKeyNavigation()
void tst_QQuickMenu::mnemonics()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
#ifdef Q_OS_MACOS
QSKIP("Mnemonics are not used on macOS");
#endif
@@ -587,6 +613,9 @@ void tst_QQuickMenu::mnemonics()
void tst_QQuickMenu::menuButton()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)
QSKIP("This platform only allows tab focus for text controls");
@@ -621,7 +650,7 @@ void tst_QQuickMenu::addItem()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
QVERIFY(menu);
@@ -639,6 +668,9 @@ void tst_QQuickMenu::addItem()
void tst_QQuickMenu::menuSeparator()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
QQuickControlsApplicationHelper helper(this, QLatin1String("menuSeparator.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
@@ -663,6 +695,7 @@ void tst_QQuickMenu::menuSeparator()
QVERIFY(saveMenuItem);
QCOMPARE(saveMenuItem->text(), QStringLiteral("Save"));
QTRY_VERIFY(!QQuickItemPrivate::get(saveMenuItem)->culled); // QTBUG-53262
+ QTRY_VERIFY(menu->isOpened());
// Clicking on items should still close the menu.
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier,
@@ -670,7 +703,7 @@ void tst_QQuickMenu::menuSeparator()
QTRY_VERIFY(!menu->isVisible());
menu->open();
- QVERIFY(menu->isVisible());
+ QTRY_VERIFY(menu->isOpened());
// Clicking on a separator shouldn't close the menu.
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier,
@@ -721,7 +754,7 @@ void tst_QQuickMenu::repeater()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
QVERIFY(menu);
@@ -766,7 +799,7 @@ void tst_QQuickMenu::order()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
QVERIFY(menu);
@@ -782,15 +815,29 @@ void tst_QQuickMenu::order()
}
}
+#if QT_CONFIG(cursor)
void tst_QQuickMenu::popup()
{
+#if defined(Q_OS_ANDROID)
+ QSKIP("Setting cursor position is not supported on Android");
+#endif
+ if (QGuiApplication::platformName().startsWith(QLatin1String("wayland")))
+ QSKIP("Setting cursor position is not supported on Wayland");
+
+ // Try moving the cursor from the current position
+ // Skip if it fails since the test relies on moving the cursor
+ const QPoint point = QCursor::pos() + QPoint(1, 1);
+ QCursor::setPos(point);
+ if (!QTest::qWaitFor([point]{ return QCursor::pos() == point; }))
+ QSKIP("Setting cursor position is not supported on this platform");
+
QQuickControlsApplicationHelper helper(this, QLatin1String("popup.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
centerOnScreen(window);
moveMouseAway(window);
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
QVERIFY(menu);
@@ -807,8 +854,6 @@ void tst_QQuickMenu::popup()
QQuickItem *button = window->property("button").value<QQuickItem *>();
QVERIFY(button);
- // Android does not support settings cursor position
-#if QT_CONFIG(cursor) && !defined(Q_OS_ANDROID)
QPoint oldCursorPos = QCursor::pos();
QPoint cursorPos = window->mapToGlobal(QPoint(11, 22));
QCursor::setPos(cursorPos);
@@ -832,16 +877,16 @@ void tst_QQuickMenu::popup()
QCOMPARE(menu->parentItem(), window->contentItem());
QCOMPARE(menu->currentIndex(), -1);
QCOMPARE(menu->contentItem()->property("currentIndex").toInt(), -1);
- QTRY_VERIFY(qFuzzyCompare(menu->x(), 33));
- QTRY_VERIFY(qFuzzyCompare(menu->y(), 44));
+ QTRY_VERIFY(qFuzzyCompare(menu->x(), qMax(qreal(33), menu->leftMargin())));
+ QTRY_VERIFY(qFuzzyCompare(menu->y(), qMax(qreal(44), menu->topMargin())));
menu->close();
QVERIFY(QMetaObject::invokeMethod(window, "popupAtCoord", Q_ARG(QVariant, 55), Q_ARG(QVariant, 66)));
QCOMPARE(menu->parentItem(), window->contentItem());
QCOMPARE(menu->currentIndex(), -1);
QCOMPARE(menu->contentItem()->property("currentIndex").toInt(), -1);
- QTRY_VERIFY(qFuzzyCompare(menu->x(), 55));
- QTRY_VERIFY(qFuzzyCompare(menu->y(), 66));
+ QTRY_VERIFY(qFuzzyCompare(menu->x(), qMax(qreal(55), menu->leftMargin())));
+ QTRY_VERIFY(qFuzzyCompare(menu->y(), qMax(qreal(66), menu->topMargin())));
menu->close();
menu->setParentItem(nullptr);
@@ -937,8 +982,8 @@ void tst_QQuickMenu::popup()
QCursor::setPos(oldCursorPos);
QTRY_COMPARE(QCursor::pos(), oldCursorPos);
-#endif
}
+#endif // QT_CONFIG(cursor)
void tst_QQuickMenu::actions()
{
@@ -946,7 +991,7 @@ void tst_QQuickMenu::actions()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
QVERIFY(menu);
@@ -1012,6 +1057,9 @@ void tst_QQuickMenu::actions()
#if QT_CONFIG(shortcut)
void tst_QQuickMenu::actionShortcuts()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
QQuickControlsApplicationHelper helper(this, QLatin1String("actionShortcuts.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
@@ -1065,7 +1113,7 @@ void tst_QQuickMenu::removeTakeItem()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
QVERIFY(menu);
@@ -1125,7 +1173,7 @@ void tst_QQuickMenu::subMenuMouse()
centerOnScreen(window);
moveMouseAway(window);
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickMenu *mainMenu = window->property("mainMenu").value<QQuickMenu *>();
QVERIFY(mainMenu);
@@ -1244,7 +1292,7 @@ void tst_QQuickMenu::subMenuDisabledMouse()
centerOnScreen(window);
moveMouseAway(window);
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickMenu *mainMenu = window->property("mainMenu").value<QQuickMenu *>();
QVERIFY(mainMenu);
@@ -1258,16 +1306,22 @@ void tst_QQuickMenu::subMenuDisabledMouse()
QVERIFY(subMenu);
mainMenu->open();
- QVERIFY(mainMenu->isVisible());
+ QTRY_VERIFY(mainMenu->isOpened());
QVERIFY(!menuItem1->isHighlighted());
QVERIFY(!subMenu->isVisible());
+ // Hover-highlighting does not work on Android
+#ifndef Q_OS_ANDROID
+ // Generate a hover event to set the current index
+ QTest::mouseMove(window, menuItem1->mapToScene(QPoint(2, 2)).toPoint());
+ QTRY_VERIFY(menuItem1->isHighlighted());
+#endif
// Open the sub-menu with a mouse click.
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, menuItem1->mapToScene(QPoint(1, 1)).toPoint());
- // Need to use the TRY variant here when cascade is false,
- // as e.g. Material style menus have transitions and don't close immediately.
+ // Need to use the TRY variant here,
+ // as e.g. Material, iOS style menus have transitions and don't open/close immediately.
QTRY_COMPARE(mainMenu->isVisible(), cascade);
- QVERIFY(subMenu->isVisible());
+ QTRY_VERIFY(subMenu->isOpened());
QTRY_VERIFY(menuItem1->isHighlighted());
// Now the sub-menu is open. The current behavior is that the first menu item
// in the new menu is highlighted; make sure that we choose the next item if
@@ -1298,6 +1352,9 @@ void tst_QQuickMenu::subMenuKeyboard_data()
void tst_QQuickMenu::subMenuKeyboard()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
QFETCH(bool, cascade);
QFETCH(bool, mirrored);
@@ -1424,6 +1481,9 @@ void tst_QQuickMenu::subMenuDisabledKeyboard_data()
// QTBUG-69540
void tst_QQuickMenu::subMenuDisabledKeyboard()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
QFETCH(bool, cascade);
QFETCH(bool, mirrored);
@@ -1539,7 +1599,7 @@ void tst_QQuickMenu::subMenuPosition()
// unpredictable results
window->showNormal();
#endif
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
if (mirrored) {
QQmlExpression mirroringExpression(qmlContext(window), window,
@@ -1653,7 +1713,7 @@ void tst_QQuickMenu::addRemoveSubMenus()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickMenu *mainMenu = window->property("mainMenu").value<QQuickMenu *>();
QVERIFY(mainMenu);
@@ -1729,7 +1789,7 @@ void tst_QQuickMenu::scrollable()
#else
window->showNormal();
#endif
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
menu->open();
@@ -1769,7 +1829,7 @@ void tst_QQuickMenu::disableWhenTriggered()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickMenu *menu = window->findChild<QQuickMenu*>("Menu");
QVERIFY(menu);
@@ -1834,7 +1894,7 @@ void tst_QQuickMenu::menuItemWidth()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
if (mirrored) {
QQmlExpression mirroringExpression(qmlContext(window), window,
@@ -1866,7 +1926,7 @@ void tst_QQuickMenu::menuItemWidthAfterMenuWidthChanged()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
if (mirrored) {
QQmlExpression mirroringExpression(qmlContext(window), window,
@@ -1914,7 +1974,7 @@ void tst_QQuickMenu::menuItemWidthAfterImplicitWidthChanged()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
if (mirrored) {
QQmlExpression mirroringExpression(qmlContext(window), window,
@@ -1948,7 +2008,7 @@ void tst_QQuickMenu::menuItemWidthAfterRetranslate()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
QVERIFY(menu);
@@ -1976,6 +2036,9 @@ void tst_QQuickMenu::menuItemWidthAfterRetranslate()
void tst_QQuickMenu::giveMenuItemFocusOnButtonPress()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
QQuickControlsApplicationHelper helper(this, QLatin1String("giveMenuItemFocusOnButtonPress.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
@@ -2000,6 +2063,46 @@ void tst_QQuickMenu::giveMenuItemFocusOnButtonPress()
QTRY_VERIFY(menu->isOpened());
}
+void tst_QQuickMenu::customMenuCullItems()
+{
+ QQuickControlsApplicationHelper helper(this, QLatin1String("customMenuCullItems.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickApplicationWindow *window = helper.appWindow;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
+ QVERIFY(menu);
+ menu->open();
+ QTRY_VERIFY(menu->isOpened());
+
+ QQuickItem *menuItemFirst = menu->itemAt(0);
+ QQuickItem *menuItemLast = menu->itemAt(menu->count() - 1);
+ QVERIFY(menuItemFirst);
+ QVERIFY(menuItemLast);
+ QTRY_VERIFY(!QQuickItemPrivate::get(menuItemFirst)->culled);
+ QTRY_VERIFY(QQuickItemPrivate::get(menuItemLast)->culled);
+}
+
+void tst_QQuickMenu::customMenuUseRepeaterAsTheContentItem()
+{
+ QQuickControlsApplicationHelper helper(this, QLatin1String("customMenuUseRepeaterAsTheContentItem.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickApplicationWindow *window = helper.appWindow;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
+ QVERIFY(menu);
+ menu->open();
+ QTRY_VERIFY(menu->isVisible());
+
+ QQuickItem *menuItemFirst = menu->itemAt(0);
+ QQuickItem *menuItemLast = menu->itemAt(menu->count() - 1);
+ QTRY_VERIFY(!QQuickItemPrivate::get(menuItemFirst)->culled);
+ QTRY_VERIFY(!QQuickItemPrivate::get(menuItemLast)->culled);
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickMenu)
#include "tst_qquickmenu.moc"
diff --git a/tests/auto/quickcontrols2/qquickmenubar/tst_qquickmenubar.cpp b/tests/auto/quickcontrols2/qquickmenubar/tst_qquickmenubar.cpp
index 1e718ff193..54c758ee23 100644
--- a/tests/auto/quickcontrols2/qquickmenubar/tst_qquickmenubar.cpp
+++ b/tests/auto/quickcontrols2/qquickmenubar/tst_qquickmenubar.cpp
@@ -26,6 +26,8 @@
**
****************************************************************************/
+#include <QtGui/qpa/qplatformintegration.h>
+#include <QtGui/private/qguiapplication_p.h>
#include <QtTest>
#include <QtQml>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -54,6 +56,9 @@ private slots:
void mnemonics();
void addRemove();
void checkHighlightWhenMenuDismissed();
+
+private:
+ static bool hasWindowActivation();
};
tst_qquickmenubar::tst_qquickmenubar()
@@ -61,6 +66,11 @@ tst_qquickmenubar::tst_qquickmenubar()
{
}
+bool tst_qquickmenubar::hasWindowActivation()
+{
+ return (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation));
+}
+
void tst_qquickmenubar::delegate()
{
QQmlApplicationEngine engine(testFileUrl("empty.qml"));
@@ -76,6 +86,9 @@ void tst_qquickmenubar::delegate()
void tst_qquickmenubar::mouse()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
QSKIP("Mouse highlight not functional on offscreen/minimal platforms");
@@ -192,9 +205,10 @@ void tst_qquickmenubar::mouse()
QQuickMenu *alignmentSubMenu = alignmentSubMenuItem->subMenu();
QVERIFY(alignmentSubMenu);
QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, alignmentSubMenuItem->mapToScene(QPointF(alignmentSubMenuItem->width() / 2, alignmentSubMenuItem->height() / 2)).toPoint());
-#ifndef Q_OS_ANDROID
+#if !defined(Q_OS_ANDROID) and !defined(Q_OS_WEBOS)
// The screen on Android is too small to fit the whole hierarchy, so the
// Alignment sub-menu is shown on top of View menu.
+ // WebOS also shows alignment sub-menu on top of View menu.
QVERIFY(viewMenuBarMenu->isVisible());
#endif
QVERIFY(alignmentSubMenu->isVisible());
@@ -206,9 +220,10 @@ void tst_qquickmenubar::mouse()
QQuickMenu *verticalSubMenu = verticalSubMenuItem->subMenu();
QVERIFY(verticalSubMenu);
QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, verticalSubMenuItem->mapToScene(QPointF(verticalSubMenuItem->width() / 2, verticalSubMenuItem->height() / 2)).toPoint());
-#ifndef Q_OS_ANDROID
+#if !defined(Q_OS_ANDROID) and !defined(Q_OS_WEBOS)
// The screen on Android is too small to fit the whole hierarchy, so the
// Vertical sub-menu is shown on top of View menu and Alignment sub-menu.
+ // WebOS also shows vertical sub-menu on top of View menu and Alignment sub-menu.
QVERIFY(viewMenuBarMenu->isVisible());
QVERIFY(alignmentSubMenu->isVisible());
#endif
@@ -250,6 +265,9 @@ void tst_qquickmenubar::mouse()
void tst_qquickmenubar::keys()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
QQmlApplicationEngine engine(testFileUrl("menubar.qml"));
QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(engine.rootObjects().value(0)));
@@ -296,6 +314,11 @@ void tst_qquickmenubar::keys()
QVERIFY(editMenuBarItem->isHighlighted());
QVERIFY(editMenuBarItem->hasActiveFocus());
QTRY_VERIFY(!editMenuBarMenu->isVisible());
+
+// There seem to be problems in focus handling in webOS QPA, see https://bugreports.qt.io/browse/WEBOSCI-45
+#ifdef Q_OS_WEBOS
+ QEXPECT_FAIL("", "WEBOSCI-45", Abort);
+#endif
QVERIFY(!cutMenuItem->isHighlighted());
QVERIFY(!cutMenuItem->hasActiveFocus());
@@ -434,8 +457,11 @@ void tst_qquickmenubar::keys()
void tst_qquickmenubar::mnemonics()
{
-#ifdef Q_OS_MACOS
- QSKIP("Mnemonics are not used on macOS");
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
+#if defined(Q_OS_MACOS) or defined(Q_OS_WEBOS)
+ QSKIP("Mnemonics are not used on this platform");
#endif
QQmlApplicationEngine engine(testFileUrl("menubar.qml"));
@@ -654,7 +680,7 @@ void tst_qquickmenubar::checkHighlightWhenMenuDismissed()
centerOnScreen(window.data());
moveMouseAway(window.data());
- QVERIFY(QTest::qWaitForWindowActive(window.data()));
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickMenuBar *menuBar = window->findChild<QQuickMenuBar *>("menuBar");
QVERIFY(menuBar);
diff --git a/tests/auto/quickcontrols2/qquickpopup/data/applicationwindow-hover.qml b/tests/auto/quickcontrols2/qquickpopup/data/applicationwindow-hover.qml
index 80b22e6d5d..fc93d16d91 100644
--- a/tests/auto/quickcontrols2/qquickpopup/data/applicationwindow-hover.qml
+++ b/tests/auto/quickcontrols2/qquickpopup/data/applicationwindow-hover.qml
@@ -68,9 +68,14 @@ ApplicationWindow {
id: popup
x: 1
y: 1
- padding: 1
+ leftPadding: 1
+ rightPadding: 1
+ topPadding: 1
+ bottomPadding: 1
+
Button {
+ anchors.centerIn: parent
id: childButton
text: "Child"
}
diff --git a/tests/auto/quickcontrols2/qquickpopup/data/applicationwindow.qml b/tests/auto/quickcontrols2/qquickpopup/data/applicationwindow.qml
index 7a298d9620..399f1c67c1 100644
--- a/tests/auto/quickcontrols2/qquickpopup/data/applicationwindow.qml
+++ b/tests/auto/quickcontrols2/qquickpopup/data/applicationwindow.qml
@@ -57,7 +57,9 @@ ApplicationWindow {
property alias popup: popup
property alias popup2: popup2
+ property alias popup3: popup3
property alias button: button
+ property alias slider: slider
Button {
id: button
@@ -79,6 +81,15 @@ ApplicationWindow {
}
}
}
+
+ Popup {
+ id: popup3
+ y: parent.height
+
+ Slider {
+ id: slider
+ }
+ }
}
Popup {
diff --git a/tests/auto/quickcontrols2/qquickpopup/data/window-hover.qml b/tests/auto/quickcontrols2/qquickpopup/data/window-hover.qml
index e0eef30206..5064fda7bc 100644
--- a/tests/auto/quickcontrols2/qquickpopup/data/window-hover.qml
+++ b/tests/auto/quickcontrols2/qquickpopup/data/window-hover.qml
@@ -69,9 +69,13 @@ Window {
id: popup
x: 1
y: 1
- padding: 1
+ topPadding: 1
+ bottomPadding: 1
+ leftPadding: 1
+ rightPadding: 1
Button {
+ anchors.centerIn: parent
id: childButton
text: "Child"
}
diff --git a/tests/auto/quickcontrols2/qquickpopup/data/window.qml b/tests/auto/quickcontrols2/qquickpopup/data/window.qml
index f6b76b7e14..d46d8ccbf1 100644
--- a/tests/auto/quickcontrols2/qquickpopup/data/window.qml
+++ b/tests/auto/quickcontrols2/qquickpopup/data/window.qml
@@ -58,7 +58,9 @@ Window {
property alias popup: popup
property alias popup2: popup2
+ property alias popup3: popup3
property alias button: button
+ property alias slider: slider
Button {
id: button
@@ -80,6 +82,15 @@ Window {
}
}
}
+
+ Popup {
+ id: popup3
+ y: parent.height
+
+ Slider {
+ id: slider
+ }
+ }
}
Popup {
diff --git a/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
index 87667989a2..44299d674f 100644
--- a/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
+++ b/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
@@ -31,6 +31,8 @@
#include <QtCore/qoperatingsystemversion.h>
#include <QtGui/qpa/qwindowsysteminterface.h>
+#include <QtGui/qpa/qplatformintegration.h>
+#include <QtGui/private/qguiapplication_p.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/private/qquickpalette_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
@@ -71,6 +73,8 @@ private slots:
void windowChange();
void closePolicy_data();
void closePolicy();
+ void closePolicy_grabberInside_data();
+ void closePolicy_grabberInside();
void activeFocusOnClose1();
void activeFocusOnClose2();
void activeFocusOnClose3();
@@ -106,6 +110,9 @@ private slots:
void dimmerContainmentMask();
void shrinkPopupThatWasLargerThanWindow_data();
void shrinkPopupThatWasLargerThanWindow();
+
+private:
+ static bool hasWindowActivation();
};
tst_QQuickPopup::tst_QQuickPopup()
@@ -126,6 +133,11 @@ void tst_QQuickPopup::visible_data()
QTest::newRow("ApplicationWindow") << "applicationwindow.qml";
}
+bool tst_QQuickPopup::hasWindowActivation()
+{
+ return (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation));
+}
+
void tst_QQuickPopup::visible()
{
QFETCH(QString, source);
@@ -134,8 +146,7 @@ void tst_QQuickPopup::visible()
QQuickWindow *window = helper.window;
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
@@ -231,8 +242,7 @@ void tst_QQuickPopup::overlay()
QQuickWindow *window = helper.window;
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickOverlay *overlay = QQuickOverlay::overlay(window);
QVERIFY(overlay);
@@ -383,8 +393,7 @@ void tst_QQuickPopup::zOrder()
QQuickWindow *window = helper.window;
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
@@ -485,6 +494,9 @@ void tst_QQuickPopup::closePolicy_data()
void tst_QQuickPopup::closePolicy()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
QFETCH(QString, source);
QFETCH(QQuickPopup::ClosePolicy, closePolicy);
@@ -510,12 +522,15 @@ void tst_QQuickPopup::closePolicy()
QVERIFY(popup->isVisible());
QTRY_VERIFY(popup->isOpened());
+ // wait for dimmer
+ QTest::qWait(50);
+
// press outside popup and its parent
- QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1), 50);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
if (closePolicy.testFlag(QQuickPopup::CloseOnPressOutside) || closePolicy.testFlag(QQuickPopup::CloseOnPressOutsideParent))
QTRY_VERIFY(!popup->isVisible());
else
- QVERIFY(popup->isVisible());
+ QVERIFY(popup->isOpened());
popup->open();
QVERIFY(popup->isVisible());
@@ -523,10 +538,10 @@ void tst_QQuickPopup::closePolicy()
// release outside popup and its parent
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
- if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside))
+ if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside) || closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutsideParent))
QTRY_VERIFY(!popup->isVisible());
else
- QVERIFY(popup->isVisible());
+ QVERIFY(popup->isOpened());
popup->open();
QVERIFY(popup->isVisible());
@@ -537,7 +552,7 @@ void tst_QQuickPopup::closePolicy()
if (closePolicy.testFlag(QQuickPopup::CloseOnPressOutside) && !closePolicy.testFlag(QQuickPopup::CloseOnPressOutsideParent))
QTRY_VERIFY(!popup->isVisible());
else
- QVERIFY(popup->isVisible());
+ QVERIFY(popup->isOpened());
popup->open();
QVERIFY(popup->isVisible());
@@ -548,7 +563,7 @@ void tst_QQuickPopup::closePolicy()
if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside) && !closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutsideParent))
QTRY_VERIFY(!popup->isVisible());
else
- QVERIFY(popup->isVisible());
+ QVERIFY(popup->isOpened());
popup->open();
QVERIFY(popup->isVisible());
@@ -557,9 +572,9 @@ void tst_QQuickPopup::closePolicy()
// press inside and release outside
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(button->x() + popup->x() + 1,
button->y() + popup->y() + 1));
- QVERIFY(popup->isVisible());
+ QVERIFY(popup->isOpened());
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
- QVERIFY(popup->isVisible());
+ QVERIFY(popup->isOpened());
// escape
QTest::keyClick(window, Qt::Key_Escape);
@@ -569,8 +584,59 @@ void tst_QQuickPopup::closePolicy()
QVERIFY(popup->isVisible());
}
+void tst_QQuickPopup::closePolicy_grabberInside_data()
+{
+ qRegisterMetaType<QQuickPopup::ClosePolicy>();
+
+ QTest::addColumn<QString>("source");
+ QTest::addColumn<QQuickPopup::ClosePolicy>("closePolicy");
+
+ QTest::newRow("Window:CloseOnReleaseOutside") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside);
+ QTest::newRow("Window:CloseOnReleaseOutside|Parent") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent);
+
+ QTest::newRow("ApplicationWindow:CloseOnReleaseOutside") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside);
+ QTest::newRow("ApplicationWindow:CloseOnReleaseOutside|Parent") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent);
+}
+
+void tst_QQuickPopup::closePolicy_grabberInside()
+{
+ QFETCH(QString, source);
+ QFETCH(QQuickPopup::ClosePolicy, closePolicy);
+
+ QQuickControlsApplicationHelper helper(this, source);
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ QQuickPopup *popup = window->property("popup3").value<QQuickPopup*>();
+ QVERIFY(popup);
+
+ QQuickSlider *slider = window->property("slider").value<QQuickSlider*>();
+ QVERIFY(slider);
+
+ popup->setModal(true);
+ popup->setClosePolicy(closePolicy);
+
+ popup->open();
+ QVERIFY(popup->isVisible());
+ QTRY_VERIFY(popup->isOpened());
+
+ // press on a mouse grabber inside and release outside
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier,
+ slider->handle()->mapToItem(window->contentItem(),slider->handle()->boundingRect().center()).toPoint());
+
+ QVERIFY(popup->isOpened());
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
+ QVERIFY(popup->isOpened());
+}
+
void tst_QQuickPopup::activeFocusOnClose1()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
// Test that a popup that never sets focus: true (e.g. ToolTip) doesn't affect
// the active focus item when it closes.
QQuickControlsApplicationHelper helper(this, QStringLiteral("activeFocusOnClose1.qml"));
@@ -614,6 +680,9 @@ void tst_QQuickPopup::activeFocusOnClose1()
void tst_QQuickPopup::activeFocusOnClose2()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
// Test that a popup that sets focus: true but relinquishes focus (e.g. by
// calling forceActiveFocus() on another item) before it closes doesn't
// affect the active focus item when it closes.
@@ -653,6 +722,9 @@ void tst_QQuickPopup::activeFocusOnClose2()
void tst_QQuickPopup::activeFocusOnClose3()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
// Test that a closing popup that had focus doesn't steal focus from
// another popup that the focus was transferred to.
QQuickControlsApplicationHelper helper(this, QStringLiteral("activeFocusOnClose3.qml"));
@@ -686,6 +758,9 @@ void tst_QQuickPopup::activeFocusOnClose3()
void tst_QQuickPopup::activeFocusOnClosingSeveralPopups()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
// Test that active focus isn't lost when multiple popup closing simultaneously
QQuickControlsApplicationHelper helper(this, QStringLiteral("activeFocusOnClosingSeveralPopups.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
@@ -735,6 +810,9 @@ void tst_QQuickPopup::activeFocusOnClosingSeveralPopups()
void tst_QQuickPopup::activeFocusAfterExit()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
// Test that after closing a popup the highest one in z-order receives it instead.
QQuickControlsApplicationHelper helper(this, QStringLiteral("activeFocusAfterExit.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
@@ -784,6 +862,9 @@ void tst_QQuickPopup::activeFocusAfterExit()
void tst_QQuickPopup::activeFocusOnDelayedEnter()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
// Test that after opening two popups, first of which has an animation, does not cause
// the first one to receive focus after the animation stops.
QQuickControlsApplicationHelper helper(this, QStringLiteral("activeFocusOnDelayedEnter.qml"));
@@ -826,8 +907,7 @@ void tst_QQuickPopup::hover()
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
@@ -1062,7 +1142,7 @@ void tst_QQuickPopup::grabber()
QCOMPARE(combo->isVisible(), false);
// click a menu item to open the popup
- QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(menu->width() / 2, menu->height() / 2));
+ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(menu->x() + menu->width() / 2, menu->y() + menu->height() / 2));
QTRY_COMPARE(menu->isVisible(), false);
QTRY_COMPARE(popup->isOpened(), true);
QCOMPARE(combo->isVisible(), false);
@@ -1152,13 +1232,16 @@ void tst_QQuickPopup::componentComplete()
void tst_QQuickPopup::closeOnEscapeWithNestedPopups()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
// Tests the scenario in the Gallery example, where there are nested popups that should
// close in the correct order when the Escape key is pressed.
QQuickControlsApplicationHelper helper(this, QStringLiteral("closeOnEscapeWithNestedPopups.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
window->show();
- QVERIFY(QTest::qWaitForWindowExposed(window));
+ QVERIFY(QTest::qWaitForWindowActive(window));
// The stack view should have two items, and it should pop the second when escape is pressed
// and it has focus.
@@ -1188,6 +1271,7 @@ void tst_QQuickPopup::closeOnEscapeWithNestedPopups()
QQuickPopup *settingsDialog = window->contentItem()->findChild<QQuickPopup*>("settingsDialog");
QVERIFY(settingsDialog);
+ QTRY_VERIFY(!optionsMenu->isVisible());
QTRY_VERIFY(settingsDialog->isOpened());
QQuickComboBox *comboBox = window->contentItem()->findChild<QQuickComboBox*>("comboBox");
@@ -1218,6 +1302,9 @@ void tst_QQuickPopup::closeOnEscapeWithNestedPopups()
void tst_QQuickPopup::closeOnEscapeWithVisiblePopup()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
QQuickControlsApplicationHelper helper(this, QStringLiteral("closeOnEscapeWithVisiblePopup.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickWindow *window = helper.window;
@@ -1309,7 +1396,7 @@ void tst_QQuickPopup::orientation()
QQuickWindow *window = helper.window;
window->reportContentOrientationChange(orientation);
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickPopup *popup = window->property("popup").value<QQuickPopup*>();
QVERIFY(popup);
@@ -1343,6 +1430,9 @@ void tst_QQuickPopup::qquickview()
// QTBUG-73447
void tst_QQuickPopup::disabledPalette()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
QQuickControlsApplicationHelper helper(this, "disabledPalette.qml");
QVERIFY2(helper.ready, helper.failureMessage());
@@ -1380,6 +1470,9 @@ void tst_QQuickPopup::disabledPalette()
void tst_QQuickPopup::disabledParentPalette()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
QQuickControlsApplicationHelper helper(this, "disabledPalette.qml");
QVERIFY2(helper.ready, helper.failureMessage());
@@ -1431,7 +1524,7 @@ void tst_QQuickPopup::countChanged()
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickComboBox *comboBox = window->property("comboBox").value<QQuickComboBox*>();
QVERIFY(comboBox);
@@ -1452,7 +1545,7 @@ void tst_QQuickPopup::toolTipCrashOnClose()
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QTest::mouseMove(window, QPoint(window->width() / 2, window->height() / 2));
QTRY_VERIFY(window->property("toolTipOpened").toBool());
@@ -1473,7 +1566,7 @@ void tst_QQuickPopup::setOverlayParentToNull()
centerOnScreen(window);
moveMouseAway(window);
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QVERIFY(QMetaObject::invokeMethod(window, "nullifyOverlayParent"));
@@ -1486,6 +1579,9 @@ void tst_QQuickPopup::setOverlayParentToNull()
void tst_QQuickPopup::tabFence()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)
QSKIP("This platform only allows tab focus for text controls");
@@ -1551,7 +1647,7 @@ void tst_QQuickPopup::invisibleToolTipOpen()
centerOnScreen(window);
moveMouseAway(window);
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickItem *mouseArea = qvariant_cast<QQuickItem *>(window->property("mouseArea"));
QVERIFY(mouseArea);
@@ -1596,6 +1692,9 @@ void tst_QQuickPopup::centerInOverlayWithinStackViewItem()
void tst_QQuickPopup::destroyDuringExitTransition()
{
+ if (!hasWindowActivation())
+ QSKIP("Window activation is not supported");
+
QQuickControlsApplicationHelper helper(this, "destroyDuringExitTransition.qml");
QVERIFY2(helper.ready, helper.failureMessage());
@@ -1633,7 +1732,7 @@ void tst_QQuickPopup::releaseAfterExitTransition()
QQuickWindow *window = helper.window;
window->show();
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickOverlay *overlay = QQuickOverlay::overlay(window);
QQuickPopup *modalPopup = window->property("modalPopup").value<QQuickPopup *>();
@@ -1683,7 +1782,7 @@ void tst_QQuickPopup::dimmerContainmentMask()
QQuickWindow *window = helper.window;
window->show();
QCOMPARE(window->property("clickCount").toInt(), expectedClickCount);
- QVERIFY(QTest::qWaitForWindowActive(window));
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QQuickOverlay *overlay = QQuickOverlay::overlay(window);
QQuickPopup *modalPopup = window->property("modalPopup").value<QQuickPopup *>();
diff --git a/tests/auto/quickcontrols2/qquickstyle/tst_qquickstyle.cpp b/tests/auto/quickcontrols2/qquickstyle/tst_qquickstyle.cpp
index 1408bafb43..c1e7505819 100644
--- a/tests/auto/quickcontrols2/qquickstyle/tst_qquickstyle.cpp
+++ b/tests/auto/quickcontrols2/qquickstyle/tst_qquickstyle.cpp
@@ -144,7 +144,9 @@ void tst_QQuickStyle::configurationFile()
// Make it small so that there's less possibility for the default/system
// pixel size to match it and give us false positives.
QCOMPARE(label->font().pixelSize(), 3);
+#ifdef QT_BUILD_INTERNAL
QCOMPARE(QQuickLabelPrivate::get(label)->palette()->windowText(), Qt::red);
+#endif
}
void tst_QQuickStyle::commandLineArgument()
diff --git a/tests/auto/quickcontrols2/snippets/CMakeLists.txt b/tests/auto/quickcontrols2/snippets/CMakeLists.txt
index ce76c4fbc5..aaae0ae783 100644
--- a/tests/auto/quickcontrols2/snippets/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/snippets/CMakeLists.txt
@@ -10,11 +10,27 @@ file(GLOB_RECURSE test_data_glob
${CMAKE_CURRENT_SOURCE_DIR}/data/*)
list(APPEND test_data ${test_data_glob})
+set(SNIPPETS_PATH \\\"${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/quickcontrols2/doc/snippets\\\")
+
+if(WEBOS)
+ # Collect snippets for webOS
+ file(GLOB_RECURSE test_snippets_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/quickcontrols2/doc/snippets/*)
+ list(APPEND test_snippets ${test_snippets_glob})
+
+ # Copy snippets to a location which is included in the webOS emulator image
+ file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/snippets)
+ file(COPY ${test_snippets} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/data/snippets)
+
+ set(SNIPPETS_PATH \\\"./data/snippets\\\")
+endif()
+
qt_internal_add_test(tst_snippets
SOURCES
tst_snippets.cpp
DEFINES
- QQC2_SNIPPETS_PATH=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/quickcontrols2/doc/snippets\\\"
+ QQC2_SNIPPETS_PATH=${SNIPPETS_PATH}
PUBLIC_LIBRARIES
Qt::Gui
Qt::Quick
diff --git a/tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp b/tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp
index 010924e378..5697ae50e2 100644
--- a/tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp
+++ b/tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp
@@ -60,7 +60,7 @@ tst_StyleImportsCompileTimeQmlOnly::tst_StyleImportsCompileTimeQmlOnly()
void tst_StyleImportsCompileTimeQmlOnly::importQmlOnlyStyleWithoutControls()
{
QQuickControlsApplicationHelper helper(this,
- QLatin1String("importQmlOnlyStyleWithoutControls.qml"), QStringList() << dataDirectory());
+ QLatin1String("importQmlOnlyStyleWithoutControls.qml"), {}, QStringList() << dataDirectory());
QVERIFY2(helper.ready, helper.failureMessage());
auto button = helper.window->property("button").value<QQuickButton*>();
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST b/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST
index bcf39c598d..822b92dd19 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST
@@ -5,6 +5,3 @@
# QTBUG-92585
[fileMode:OpenFiles]
*
-#QTBUG-101329
-[goUp]
-qnx
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/data/setSelectedFile.qml b/tests/auto/quickdialogs/qquickfiledialogimpl/data/setSelectedFile.qml
new file mode 100644
index 0000000000..b7ff588b68
--- /dev/null
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/data/setSelectedFile.qml
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+ApplicationWindow {
+ id: root
+ width: 640
+ height: 480
+
+ required property url tempFile1Url
+ required property int fileMode
+
+ property alias dialog: dialog
+
+ FileDialog {
+ id: dialog
+ objectName: "FileDialog"
+ fileMode: root.fileMode
+ selectedFile: root.tempFile1Url
+ }
+}
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
index e70c7e7171..2718bf52a0 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
@@ -26,6 +26,7 @@
**
****************************************************************************/
+#include <QtCore/qloggingcategory.h>
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
#include <QtQml/qqmlfile.h>
@@ -36,6 +37,7 @@
#include <QtQuickDialogs2/private/qquickfiledialog_p.h>
#include <QtQuickDialogs2QuickImpl/private/qquickplatformfiledialog_p.h>
#include <QtQuickDialogs2QuickImpl/private/qquickfiledialogdelegate_p.h>
+#include <QtQuickDialogs2QuickImpl/private/qquickfiledialogimpl_p_p.h>
#include <QtQuickDialogs2QuickImpl/private/qquickfolderbreadcrumbbar_p.h>
#include <QtQuickDialogs2QuickImpl/private/qquickfolderbreadcrumbbar_p_p.h>
#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>
@@ -51,6 +53,8 @@ using namespace QQuickVisualTestUtils;
using namespace QQuickDialogTestUtils;
using namespace QQuickControlsTestUtils;
+Q_LOGGING_CATEGORY(lcTest, "qt.quick.dialogs.tests.quickfiledialogimpl")
+
class tst_QQuickFileDialogImpl : public QQmlDataTest
{
Q_OBJECT
@@ -69,6 +73,7 @@ public:
private slots:
void initTestCase() override;
+ void init() override;
void cleanupTestCase();
void defaults();
@@ -79,14 +84,17 @@ private slots:
void bindCurrentFolder_data();
void bindCurrentFolder();
void changeFolderViaStandardButtons();
+ void changeFolderViaDoubleClick_data();
void changeFolderViaDoubleClick();
void chooseFolderViaTextEdit();
void chooseFolderViaEnter();
void chooseFileAndThenFolderViaTextEdit();
void cancelDialogWhileTextEditHasFocus();
+ void closingDialogCancels();
void goUp();
void goUpWhileTextEditHasFocus();
void goIntoLargeFolder();
+ void goUpIntoLargeFolder();
void keyAndShortcutHandling();
void bindNameFilters();
void changeNameFilters();
@@ -101,6 +109,8 @@ private slots:
void defaultSuffix();
void done_data();
void done();
+ void setSelectedFile_data();
+ void setSelectedFile();
private:
QTemporaryDir tempDir;
@@ -110,11 +120,23 @@ private:
QDir tempSubSubDir;
QScopedPointer<QFile> tempSubFile1;
QScopedPointer<QFile> tempSubFile2;
+
+ QTemporaryDir largeTempDir;
+ QStringList largeTempDirPaths;
+ QDir largeTempDirLargeSubDir;
+ const int largeTempDirLargeSubDirIndex = 80;
+
QDir oldCurrentDir;
+
+ const QKeySequence goUpKeySequence = QKeySequence(Qt::ALT | Qt::Key_Up);
+ const QKeySequence editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
};
+// We don't want to fail on warnings until QTBUG-98964 is fixed,
+// as we deliberately prevent deferred execution in some of the tests here,
+// which causes warnings.
tst_QQuickFileDialogImpl::tst_QQuickFileDialogImpl()
- : QQmlDataTest(QT_QMLTEST_DATADIR)
+ : QQmlDataTest(QT_QMLTEST_DATADIR, FailOnWarningsPolicy::DoNotFailOnWarnings)
{
}
@@ -122,7 +144,7 @@ void tst_QQuickFileDialogImpl::initTestCase()
{
QQmlDataTest::initTestCase();
- qputenv("QT_QUICK_DIALOGS_SHOW_DIRS_FIRST", "1");
+ qputenv("QT_QUICK_DIALOGS_PRESELECT_FIRST_FILE", "1");
QVERIFY(tempDir.isValid());
// QTEST_QUICKCONTROLS_MAIN constructs the test case object once,
@@ -167,58 +189,143 @@ void tst_QQuickFileDialogImpl::initTestCase()
tempFile2.reset(new QFile(tempDir.path() + "/file2.txt"));
QVERIFY(tempFile2->open(QIODevice::ReadWrite));
+ /*
+ Create another temporary directory that contains a large amount of folders.
+ */
+ QVERIFY(largeTempDir.isValid());
+ const static int largeFileCount = 100;
+ const QDir largeTempDirectory(largeTempDir.path());
+ for (int i = 0; i < largeFileCount; ++i) {
+ // Pad with zeroes so that the directories are ordered as we expect.
+ const QString dirName = QString::fromLatin1("dir%1").arg(i, 3, 10, QLatin1Char('0'));
+ QVERIFY(largeTempDirectory.mkdir(dirName));
+ largeTempDirPaths.append(largeTempDirectory.filePath(dirName));
+ }
+
+ // ... and within one of those folders, more folders.
+ largeTempDirLargeSubDir = QDir(largeTempDir.path() + "/dir"
+ + QString::fromLatin1("%1").arg(largeTempDirLargeSubDirIndex, 3, 10, QLatin1Char('0')));
+ QVERIFY(largeTempDirLargeSubDir.exists());
+ const QDir largeTempSubDirectory = QDir(largeTempDirLargeSubDir.path());
+ for (int i = 0; i < largeFileCount; ++i)
+ QVERIFY(largeTempSubDirectory.mkdir(QString::fromLatin1("sub-dir%1").arg(i, 3, 10, QLatin1Char('0'))));
+
// Ensure that each test starts off in the temporary directory.
oldCurrentDir = QDir::current();
QDir::setCurrent(tempDir.path());
}
+void tst_QQuickFileDialogImpl::init()
+{
+ // Do this before each test function in case the test sets it.
+ qputenv("QT_QUICK_DIALOGS_SHOW_DIRS_FIRST", "1");
+}
+
void tst_QQuickFileDialogImpl::cleanupTestCase()
{
// Just in case...
QDir::setCurrent(oldCurrentDir.path());
}
-void tst_QQuickFileDialogImpl::defaults()
+#define VERIFY_FILE_SELECTED(expectedCurrentFolderUrl, expectedCurrentFileUrl) \
+{ \
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), expectedCurrentFileUrl); \
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { expectedCurrentFileUrl }); \
+ /* We also test the deprecated currentFile* API until it's removed. */ \
+ COMPARE_URL(dialogHelper.dialog->currentFile(), expectedCurrentFileUrl); \
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { expectedCurrentFileUrl }); \
+ COMPARE_URL(dialogHelper.quickDialog->selectedFile(), expectedCurrentFileUrl); \
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), expectedCurrentFolderUrl); \
+ COMPARE_URL(dialogHelper.quickDialog->currentFolder(), expectedCurrentFolderUrl); \
+}
+
+#define VERIFY_DELEGATE_CURRENT(expectedCurrentFileUrl, expectedCurrentIndex) \
+{ \
+ QTRY_COMPARE(dialogHelper.fileDialogListView->currentIndex(), expectedCurrentIndex); \
+ QQuickFileDialogDelegate *fileDelegate = nullptr; \
+ QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, expectedCurrentIndex, fileDelegate)); \
+ COMPARE_URL(fileDelegate->file(), expectedCurrentFileUrl); \
+}
+
+#define VERIFY_DELEGATE_FOCUSED(expectedCurrentIndex) \
+{ \
+ QTRY_COMPARE(dialogHelper.fileDialogListView->currentIndex(), expectedCurrentIndex); \
+ QQuickFileDialogDelegate *fileDelegate = nullptr; \
+ QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, expectedCurrentIndex, fileDelegate)); \
+ QVERIFY2(fileDelegate->hasActiveFocus(), qPrintable(QString::fromLatin1( \
+ "Expected delegate at index %1 to have focus, but %2 has it") \
+ .arg(QString::number(expectedCurrentIndex)) \
+ .arg(QDebug::toString(dialogHelper.window()->activeFocusItem())))); \
+ QVERIFY(fileDelegate->isHighlighted()); \
+}
+
+/*
+ Checks that currentFolder, selectedFile, and currentIndex are what we expect.
+
+ It also checks that the relevant delegate in the view is current, has focus, and is highlighted.
+ As the FolderListModel (the view's model) is asynchronous, we need to wait for the currentIndex to change.
+
+ Can only be called when the dialog is open.
+*/
+#define VERIFY_FILE_SELECTED_AND_FOCUSED(expectedCurrentFolderUrl, expectedCurrentFileUrl, expectedCurrentIndex) \
+VERIFY_FILE_SELECTED(expectedCurrentFolderUrl, expectedCurrentFileUrl) \
+VERIFY_DELEGATE_CURRENT(expectedCurrentFileUrl, expectedCurrentIndex) \
+VERIFY_DELEGATE_FOCUSED(expectedCurrentIndex)
+
+class FileDialogTestHelper : public DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl>
{
- QQuickApplicationHelper helper(this, "fileDialog.qml");
- QVERIFY2(helper.ready, helper.failureMessage());
+public:
+ FileDialogTestHelper(QQmlDataTest *testCase, const QString &testFilePath,
+ const QStringList &qmlImportPaths = {}, const QVariantMap &initialProperties = {});
- QQuickWindow *window = helper.window;
- window->show();
- window->requestActivate();
- QVERIFY(QTest::qWaitForWindowExposed(window));
+ bool openDialog() override;
- QQuickFileDialog *dialog = window->property("dialog").value<QQuickFileDialog*>();
- QVERIFY(dialog);
- COMPARE_URL(dialog->selectedFile(), QUrl());
+ QPointer<QQuickListView> fileDialogListView;
+};
+
+FileDialogTestHelper::FileDialogTestHelper(QQmlDataTest *testCase, const QString &testFilePath,
+ const QStringList &qmlImportPaths, const QVariantMap &initialProperties)
+ : DialogTestHelper(testCase, testFilePath, qmlImportPaths, initialProperties)
+{
+}
+
+bool FileDialogTestHelper::openDialog()
+{
+ if (!DialogTestHelper::openDialog())
+ return false;
+
+ fileDialogListView = quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ return fileDialogListView != nullptr;
+}
+
+void tst_QQuickFileDialogImpl::defaults()
+{
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl());
// It should default to the current directory.
- COMPARE_URL(dialog->currentFolder(), QUrl::fromLocalFile(QDir().absolutePath()));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(QDir().absolutePath()));
// The first file in the directory should be selected, but not until the dialog is actually open,
// as QQuickFileDialogImpl hasn't been created yet.
- COMPARE_URL(dialog->currentFile(), QUrl());
- COMPARE_URLS(dialog->currentFiles(), {});
- QCOMPARE(dialog->title(), QString());
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl());
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), {});
+ QCOMPARE(dialogHelper.dialog->title(), QString());
- dialog->open();
- QQuickFileDialogImpl *quickDialog = window->findChild<QQuickFileDialogImpl*>();
- QTRY_VERIFY(quickDialog->isOpened());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ QQuickFileDialogImpl *quickDialog = dialogHelper.window()->findChild<QQuickFileDialogImpl*>();
QVERIFY(quickDialog);
- COMPARE_URL(quickDialog->currentFolder(), QUrl::fromLocalFile(QDir().absolutePath()));
- COMPARE_URL(dialog->selectedFile(), QUrl::fromLocalFile(tempSubDir.path()));
- COMPARE_URLS(dialog->selectedFiles(), { QUrl::fromLocalFile(tempSubDir.path()) });
- COMPARE_URL(dialog->currentFile(), dialog->selectedFile());
- COMPARE_URLS(dialog->currentFiles(), dialog->selectedFiles());
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(QDir().absolutePath()), QUrl::fromLocalFile(tempSubDir.path()), 0);
QCOMPARE(quickDialog->title(), QString());
}
void tst_QQuickFileDialogImpl::chooseFileViaStandardButtons()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
+ OPEN_QUICK_DIALOG();
// Select the delegate by clicking once.
QSignalSpy dialogSelectedFileChangedSpy(dialogHelper.dialog, SIGNAL(selectedFileChanged()));
@@ -226,16 +333,11 @@ void tst_QQuickFileDialogImpl::chooseFileViaStandardButtons()
QSignalSpy dialogCurrentFileChangedSpy(dialogHelper.dialog, SIGNAL(currentFileChanged()));
QVERIFY(dialogCurrentFileChangedSpy.isValid());
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
QQuickFileDialogDelegate *delegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 2, delegate));
+ QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 2, delegate));
COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempFile2->fileName()));
QVERIFY(clickButton(delegate));
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile2->fileName()));
- COMPARE_URL(dialogHelper.quickDialog->selectedFile(), QUrl::fromLocalFile(tempFile2->fileName()));
- COMPARE_URL(dialogHelper.dialog->currentFile(), dialogHelper.dialog->selectedFile());
- COMPARE_URLS(dialogHelper.dialog->currentFiles(), { dialogHelper.dialog->selectedFile() });
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempFile2->fileName()), 2);
QCOMPARE(dialogSelectedFileChangedSpy.count(), 1);
QCOMPARE(dialogCurrentFileChangedSpy.count(), 1);
@@ -258,17 +360,12 @@ void tst_QQuickFileDialogImpl::chooseFileViaStandardButtons()
void tst_QQuickFileDialogImpl::chooseFileViaDoubleClick()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
+ OPEN_QUICK_DIALOG();
// Select the delegate by double-clicking.
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
QQuickFileDialogDelegate *delegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 2, delegate));
+ QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 2, delegate));
COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempFile2->fileName()))
QVERIFY(doubleClickButton(delegate));
COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile2->fileName()))
@@ -282,14 +379,15 @@ void tst_QQuickFileDialogImpl::chooseFileViaDoubleClick()
void tst_QQuickFileDialogImpl::chooseFileViaTextEdit()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
+ OPEN_QUICK_DIALOG();
+ // Ensure that fileDialogListView has loaded its items, as we force active focus
+ // on the current item when we set it in setFileDialogListViewCurrentIndex(),
+ // which can make the TextField's visibility check
+ // below fail due to it being hidden when it loses activeFocus.
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
// Get the text edit visible with Ctrl+L.
- const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
@@ -313,20 +411,11 @@ void tst_QQuickFileDialogImpl::chooseFileViaTextEdit()
void tst_QQuickFileDialogImpl::chooseFileViaEnter()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
+ OPEN_QUICK_DIALOG();
// Before moving down, the first delegate in the view should be selected and have focus.
- COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempSubDir.path()));
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
- QQuickFileDialogDelegate *subDirDelegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subDirDelegate));
- COMPARE_URL(subDirDelegate->file(), QUrl::fromLocalFile(tempSubDir.path()));
- QVERIFY(subDirDelegate->hasActiveFocus());
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
// Select the first file in the view by navigating with the down key.
QTest::keyClick(dialogHelper.window(), Qt::Key_Down);
@@ -367,21 +456,16 @@ void tst_QQuickFileDialogImpl::bindCurrentFolder()
QFETCH(QStringList, expectedVisibleFiles);
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindCurrentFolder.qml", {},
+ FileDialogTestHelper dialogHelper(this, "bindCurrentFolder.qml", {},
{{ "initialFolder", initialFolder }});
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ OPEN_QUICK_DIALOG();
COMPARE_URL(dialogHelper.dialog->currentFolder(), expectedFolder);
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
QString failureMessage;
// Even waiting for ListView polish and that the FolderListModel's status is ready aren't enough
// on Windows, apparently, as sometimes there just aren't any delegates by the time we do the check.
// So, we use QTRY_VERIFY2 each time we call this function just to be safe.
- QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView, expectedVisibleFiles, failureMessage), qPrintable(failureMessage));
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView, expectedVisibleFiles, failureMessage), qPrintable(failureMessage));
// Check that the breadcrumb bar is correct by constructing the expected files from the expectedFolder.
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
@@ -392,25 +476,16 @@ void tst_QQuickFileDialogImpl::bindCurrentFolder()
void tst_QQuickFileDialogImpl::changeFolderViaStandardButtons()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
+ OPEN_QUICK_DIALOG();
// Select the delegate by clicking once.
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
QQuickFileDialogDelegate *delegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, delegate));
+ QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 0, delegate));
COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempSubDir.path()));
QVERIFY(clickButton(delegate));
// The selectedFile should change, but not currentFolder.
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempSubDir.path()));
- COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempSubDir.path()) });
- COMPARE_URL(dialogHelper.dialog->currentFile(), dialogHelper.dialog->selectedFile());
- COMPARE_URLS(dialogHelper.dialog->currentFiles(), dialogHelper.dialog->selectedFiles());
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
// Click the "Open" button. The dialog should navigate to that directory, but still be open.
QVERIFY(dialogHelper.quickDialog->footer());
@@ -428,33 +503,38 @@ void tst_QQuickFileDialogImpl::changeFolderViaStandardButtons()
QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
}
+void tst_QQuickFileDialogImpl::changeFolderViaDoubleClick_data()
+{
+ QTest::addColumn<bool>("showDirsFirst");
+
+ QTest::newRow("showDirsFirst=true") << true;
+ QTest::newRow("showDirsFirst=false") << false;
+}
+
void tst_QQuickFileDialogImpl::changeFolderViaDoubleClick()
{
+ QFETCH(bool, showDirsFirst);
+
+ qputenv("QT_QUICK_DIALOGS_SHOW_DIRS_FIRST", showDirsFirst ? "true" : "false");
+
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
+ OPEN_QUICK_DIALOG();
// Select the "sub-dir" delegate by double-clicking.
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
QQuickFileDialogDelegate *subDirDelegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subDirDelegate));
+ const int subDirIndex = showDirsFirst ? 0 : 2;
+ QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, subDirIndex, subDirDelegate));
COMPARE_URL(subDirDelegate->file(), QUrl::fromLocalFile(tempSubDir.path()));
QVERIFY(doubleClickButton(subDirDelegate));
- // The first file in the directory should be selected, which is "sub-sub-dir".
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempSubSubDir.path()));
- COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempSubSubDir.path()) });
- COMPARE_URL(dialogHelper.dialog->currentFile(), dialogHelper.dialog->selectedFile());
- COMPARE_URLS(dialogHelper.dialog->currentFiles(), { dialogHelper.dialog->selectedFiles() });
- QQuickFileDialogDelegate *subSubDirDelegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subSubDirDelegate));
- QCOMPARE(subSubDirDelegate->isHighlighted(), true);
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempSubSubDir.path()));
- COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempSubSubDir.path()) });
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
+ const QStringList expectedVisibleFiles = showDirsFirst
+ ? QStringList { tempSubSubDir.path(), tempSubFile1->fileName(), tempSubFile2->fileName() }
+ : QStringList { tempSubFile1->fileName(), tempSubFile2->fileName(), tempSubSubDir.path() };
+ QString failureMessage;
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView, expectedVisibleFiles, failureMessage), qPrintable(failureMessage));
+ // The first file in the directory should now be selected.
+ const QUrl firstFileUrl = showDirsFirst ? QUrl::fromLocalFile(tempSubSubDir.path()) : QUrl::fromLocalFile(tempSubFile1->fileName());
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempSubDir.path()), firstFileUrl, 0);
// Since we only chose a folder, the dialog should still be open.
QVERIFY(dialogHelper.dialog->isVisible());
@@ -466,11 +546,10 @@ void tst_QQuickFileDialogImpl::changeFolderViaDoubleClick()
void tst_QQuickFileDialogImpl::chooseFolderViaTextEdit()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
+ OPEN_QUICK_DIALOG();
+ // See comment in chooseFileViaTextEdit for why we check for this.
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
// Get the text edit visible with Ctrl+L.
const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
@@ -488,16 +567,10 @@ void tst_QQuickFileDialogImpl::chooseFolderViaTextEdit()
// Hit enter to accept.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
// The first file in the directory should be selected, which is "sub-sub-dir".
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempSubSubDir.path()));
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
- QQuickFileDialogDelegate *subSubDirDelegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subSubDirDelegate));
- QCOMPARE(subSubDirDelegate->isHighlighted(), true);
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempSubSubDir.path()));
- COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempSubSubDir.path()) });
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
- QTRY_VERIFY(dialogHelper.dialog->isVisible());
+ // Note that the TextEdit will still have focus, so we can't use VERIFY_FILE_SELECTED_AND_FOCUSED.
+ VERIFY_FILE_SELECTED(QUrl::fromLocalFile(tempSubDir.path()), QUrl::fromLocalFile(tempSubSubDir.path()));
+ VERIFY_DELEGATE_CURRENT(QUrl::fromLocalFile(tempSubSubDir.path()), 0)
+ QVERIFY(dialogHelper.dialog->isVisible());
dialogHelper.dialog->close();
QVERIFY(!dialogHelper.dialog->isVisible());
@@ -507,28 +580,19 @@ void tst_QQuickFileDialogImpl::chooseFolderViaTextEdit()
void tst_QQuickFileDialogImpl::chooseFolderViaEnter()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
+ OPEN_QUICK_DIALOG();
// The first delegate in the view should be selected and have focus.
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
- COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempSubDir.path()));
- COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempSubDir.path()) });
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
- QQuickFileDialogDelegate *subDirDelegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subDirDelegate));
- COMPARE_URL(subDirDelegate->file(), QUrl::fromLocalFile(tempSubDir.path()));
- QVERIFY(subDirDelegate->hasActiveFocus());
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
// Select the delegate by pressing enter.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
+ const QStringList expectedVisibleFiles = { tempSubSubDir.path(), tempSubFile1->fileName(), tempSubFile2->fileName() };
+ QString failureMessage;
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView, expectedVisibleFiles, failureMessage), qPrintable(failureMessage));
// The first file in the new directory should be selected, which is "sub-sub-dir".
- COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempSubSubDir.path()));
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempSubDir.path()), QUrl::fromLocalFile(tempSubSubDir.path()), 0);
// Since we only chose a folder, the dialog should still be open.
QVERIFY(dialogHelper.dialog->isVisible());
@@ -540,14 +604,12 @@ void tst_QQuickFileDialogImpl::chooseFolderViaEnter()
void tst_QQuickFileDialogImpl::chooseFileAndThenFolderViaTextEdit()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
+ OPEN_QUICK_DIALOG();
+ // See comment in chooseFileViaTextEdit for why we check for this.
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
// Get the text edit visible with Ctrl+L.
- const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
@@ -589,17 +651,9 @@ void tst_QQuickFileDialogImpl::chooseFileAndThenFolderViaTextEdit()
// Hit enter to accept.
QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
// The first file in the directory should be selected, which is "sub-sub-dir".
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempSubSubDir.path()));
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
- QQuickFileDialogDelegate *subSubDirDelegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subSubDirDelegate));
- QCOMPARE(subSubDirDelegate->isHighlighted(), true);
- // We just changed directories, so the actual selected file shouldn't change.
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempSubSubDir.path()));
- COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempSubSubDir.path()) });
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
- QTRY_VERIFY(dialogHelper.dialog->isVisible());
+ // Note that the TextEdit will still have focus, so we can't use VERIFY_FILE_SELECTED_AND_FOCUSED.
+ VERIFY_FILE_SELECTED(QUrl::fromLocalFile(tempSubDir.path()), QUrl::fromLocalFile(tempSubSubDir.path()));
+ VERIFY_DELEGATE_CURRENT(QUrl::fromLocalFile(tempSubSubDir.path()), 0)
// Close the dialog.
dialogHelper.dialog->close();
@@ -610,14 +664,12 @@ void tst_QQuickFileDialogImpl::chooseFileAndThenFolderViaTextEdit()
void tst_QQuickFileDialogImpl::cancelDialogWhileTextEditHasFocus()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
+ OPEN_QUICK_DIALOG();
+ // See comment in chooseFileViaTextEdit for why we check for this.
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
// Get the text edit visible with Ctrl+L.
- const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
@@ -641,17 +693,42 @@ void tst_QQuickFileDialogImpl::cancelDialogWhileTextEditHasFocus()
QVERIFY(!breadcrumbBar->textField()->isVisible());
}
+void tst_QQuickFileDialogImpl::closingDialogCancels()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ OPEN_QUICK_DIALOG();
+
+ QSignalSpy accepted(dialogHelper.dialog, &QQuickAbstractDialog::accepted);
+ QSignalSpy rejected(dialogHelper.dialog, &QQuickAbstractDialog::rejected);
+
+ // Accept the dialog.
+ QVERIFY(QMetaObject::invokeMethod(dialogHelper.window(), "doneAccepted"));
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+ QCOMPARE(accepted.size(), 1);
+ QCOMPARE(rejected.size(), 0);
+
+ // Re-open the dialog.
+ accepted.clear();
+ OPEN_QUICK_DIALOG();
+
+ // Close the dialog.
+ CLOSE_QUICK_DIALOG();
+ QCOMPARE(accepted.size(), 0);
+ QCOMPARE(rejected.size(), 1);
+}
+
void tst_QQuickFileDialogImpl::goUp()
{
// Open the dialog. Start off in "sub-dir".
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindCurrentFolder.qml", {},
+ FileDialogTestHelper dialogHelper(this, "bindCurrentFolder.qml", {},
{{ "initialFolder", QUrl::fromLocalFile(tempSubDir.path()) }});
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ OPEN_QUICK_DIALOG();
// Go up a directory via the button next to the breadcrumb bar.
+ qCDebug(lcTest) << "going up to" << tempDir.path() << "- files in that dir:\n"
+ << QQuickFileDialogImplPrivate::get(dialogHelper.quickDialog)->fileList(QDir(tempDir.path()));
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
auto barListView = qobject_cast<QQuickListView*>(breadcrumbBar->contentItem());
@@ -659,38 +736,33 @@ void tst_QQuickFileDialogImpl::goUp()
if (QQuickTest::qIsPolishScheduled(barListView))
QVERIFY(QQuickTest::qWaitForItemPolished(barListView));
QVERIFY(clickButton(breadcrumbBar->upButton()));
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
// The previous directory that we were in should now be selected (matches e.g. Windows and Ubuntu).
- COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempSubDir.path()));
- COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempSubDir.path()) });
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
- QQuickFileDialogDelegate *subDirDelegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subDirDelegate));
- QCOMPARE(subDirDelegate->isHighlighted(), true);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
- // Go up a directory via the keyboard shortcut next to the breadcrumb bar.
- const auto goUpKeySequence = QKeySequence(Qt::ALT | Qt::Key_Up);
- QTest::keySequence(dialogHelper.window(), goUpKeySequence);
+ // Go up a directory via the keyboard shortcut.
QDir tempParentDir(tempDir.path());
QVERIFY(tempParentDir.cdUp());
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempParentDir.path()));
- COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempDir.path()));
- COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempDir.path()) });
+ const auto filesInTempParentDir = QQuickFileDialogImplPrivate::get(dialogHelper.quickDialog)->fileList(tempParentDir);
+ qCDebug(lcTest) << "going up to" << tempParentDir.path() << "- files in that dir:\n" << filesInTempParentDir;
+ QTest::keySequence(dialogHelper.window(), goUpKeySequence);
+ // Ubuntu on QEMU arm shows no files in /tmp even if there are.
+ if (!filesInTempParentDir.isEmpty()) {
+ const int expectedCurrentIndex = filesInTempParentDir.indexOf(QFileInfo(tempDir.path()));
+ QVERIFY(expectedCurrentIndex != -1);
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempParentDir.path()), QUrl::fromLocalFile(tempDir.path()), expectedCurrentIndex);
+ }
}
void tst_QQuickFileDialogImpl::goUpWhileTextEditHasFocus()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindCurrentFolder.qml", {},
+ FileDialogTestHelper dialogHelper(this, "bindCurrentFolder.qml", {},
{{ "initialFolder", QUrl::fromLocalFile(tempSubDir.path()) }});
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ OPEN_QUICK_DIALOG();
+ // See comment in chooseFileViaTextEdit for why we check for this.
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempSubDir.path()), QUrl::fromLocalFile(tempSubSubDir.path()), 0);
// Get the text edit visible with Ctrl+L.
- const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
@@ -709,82 +781,72 @@ void tst_QQuickFileDialogImpl::goUpWhileTextEditHasFocus()
QVERIFY(!breadcrumbBar->textField()->isVisible());
// The focus should be given to the first delegate.
QVERIFY(dialogHelper.window()->activeFocusItem());
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
QQuickFileDialogDelegate *firstDelegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, firstDelegate));
+ QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 0, firstDelegate));
QCOMPARE(dialogHelper.window()->activeFocusItem(), firstDelegate);
}
void tst_QQuickFileDialogImpl::goIntoLargeFolder()
{
- // Create a throwaway directory with a lot of folders within it...
- QTemporaryDir anotherTempDir;
- QVERIFY(anotherTempDir.isValid());
- for (int i = 0; i < 30; ++i) {
- QDir newDir(anotherTempDir.path());
- QVERIFY(newDir.exists());
- // Pad with zeroes so that the directories are ordered as we expect.
- QVERIFY(newDir.mkdir(QString::fromLatin1("dir%1").arg(i, 2, 10, QLatin1Char('0'))));
- }
-
- // ... and within one of those folders, more folders.
- QDir dir20(anotherTempDir.path() + "/dir20");
- QVERIFY(dir20.exists());
- for (int i = 0; i < 30; ++i) {
- QDir newDir(dir20.path());
- QVERIFY(newDir.exists());
- QVERIFY(newDir.mkdir(QString::fromLatin1("subdir%1").arg(i, 2, 10, QLatin1Char('0'))));
- }
-
- // Open the dialog. Start off in the throwaway directory.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindCurrentFolder.qml", {},
- {{ "initialFolder", QUrl::fromLocalFile(anotherTempDir.path()) }});
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ // Open the dialog. Start off in the large directory.
+ FileDialogTestHelper dialogHelper(this, "bindCurrentFolder.qml", {},
+ {{ "initialFolder", QUrl::fromLocalFile(largeTempDir.path()) }});
+ OPEN_QUICK_DIALOG();
// If the screen is so tall that the contentItem is not vertically larger than the view,
// then the test makes no sense.
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
- if (QQuickTest::qIsPolishScheduled(fileDialogListView))
- QVERIFY(QQuickTest::qWaitForItemPolished(fileDialogListView));
+ if (QQuickTest::qIsPolishScheduled(dialogHelper.fileDialogListView))
+ QVERIFY(QQuickTest::qWaitForItemPolished(dialogHelper.fileDialogListView));
// Just to be safe, make sure it's at least twice as big.
- if (fileDialogListView->contentItem()->height() < fileDialogListView->height() * 2) {
- QSKIP(qPrintable(QString::fromLatin1("Expected height of fileDialogListView's contentItem (%1)" \
+ if (dialogHelper.fileDialogListView->contentItem()->height() < dialogHelper.fileDialogListView->height() * 2) {
+ QSKIP(qPrintable(QString::fromLatin1("Expected height of dialogHelper.fileDialogListView's contentItem (%1)" \
" to be at least twice as big as the ListView's height (%2)")
- .arg(fileDialogListView->contentItem()->height()).arg(fileDialogListView->height())));
+ .arg(dialogHelper.fileDialogListView->contentItem()->height()).arg(dialogHelper.fileDialogListView->height())));
}
- // Scroll down to dir20. The view should be somewhere past the middle.
- QVERIFY(QMetaObject::invokeMethod(fileDialogListView, "positionViewAtIndex", Qt::DirectConnection,
- Q_ARG(int, 20), Q_ARG(int, QQuickItemView::PositionMode::Center)));
- QVERIFY(fileDialogListView->contentY() > 0);
+ // Scroll down to largeTempDirLargeSubDirIndex. The view should be somewhere towards the end.
+ QVERIFY(QMetaObject::invokeMethod(dialogHelper.fileDialogListView, "positionViewAtIndex", Qt::DirectConnection,
+ Q_ARG(int, largeTempDirLargeSubDirIndex), Q_ARG(int, QQuickItemView::PositionMode::Center)));
+ QVERIFY(dialogHelper.fileDialogListView->contentY() > 0);
// Go into it. The view should start at the top of the directory, not at the same contentY
// that it had in the previous directory.
- QQuickFileDialogDelegate *dir20Delegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 20, dir20Delegate));
- COMPARE_URL(dir20Delegate->file(), QUrl::fromLocalFile(dir20.path()));
- QVERIFY(doubleClickButton(dir20Delegate));
- COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(dir20.path()));
- COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(dir20.path()));
- QCOMPARE(fileDialogListView->contentY(), 0);
+ QQuickFileDialogDelegate *subDirDelegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, largeTempDirLargeSubDirIndex, subDirDelegate));
+ COMPARE_URL(subDirDelegate->file(), QUrl::fromLocalFile(largeTempDirLargeSubDir.path()));
+ QVERIFY(doubleClickButton(subDirDelegate));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(largeTempDirLargeSubDir.path()));
+ COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(largeTempDirLargeSubDir.path()));
+ QCOMPARE(dialogHelper.fileDialogListView->contentY(), 0);
+}
+
+void tst_QQuickFileDialogImpl::goUpIntoLargeFolder()
+{
+ // Open the dialog. Start off in the large sub-directory.
+ FileDialogTestHelper dialogHelper(this, "bindCurrentFolder.qml", {},
+ {{ "initialFolder", QUrl::fromLocalFile(largeTempDirLargeSubDir.path()) }});
+ OPEN_QUICK_DIALOG();
+ // We didn't set an initial selectedFile, so the first file in the directory will be selected.
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(largeTempDirLargeSubDir.path()),
+ QUrl::fromLocalFile(largeTempDirLargeSubDir.path() + "/sub-dir000"), 0);
+
+ // Go up a directory via the keyboard shortcut.
+ QTest::keySequence(dialogHelper.window(), goUpKeySequence);
+ QString failureMessage;
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
+ largeTempDirPaths, failureMessage), qPrintable(failureMessage));
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(largeTempDir.path()),
+ QUrl::fromLocalFile(largeTempDirLargeSubDir.path()), largeTempDirLargeSubDirIndex);
}
void tst_QQuickFileDialogImpl::keyAndShortcutHandling()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
+ OPEN_QUICK_DIALOG();
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), QUrl::fromLocalFile(tempSubDir.path()), 0);
// Get the text edit visible with Ctrl+L.
- const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
@@ -819,23 +881,18 @@ void tst_QQuickFileDialogImpl::keyAndShortcutHandling()
void tst_QQuickFileDialogImpl::bindNameFilters()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindTxtHtmlNameFilters.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "bindTxtHtmlNameFilters.qml");
+ OPEN_QUICK_DIALOG();
// Only "sub-dir", "text1.txt" and "text2.txt" should be visible, since *.txt is the first filter.
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
QString failureMessage;
- QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView,
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
{ tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() }, failureMessage), qPrintable(failureMessage));
}
void tst_QQuickFileDialogImpl::changeNameFilters()
{
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
QVERIFY(dialogHelper.waitForWindowActive());
@@ -879,10 +936,8 @@ void tst_QQuickFileDialogImpl::changeNameFilters()
QCOMPARE(dialogHelper.quickDialog->selectedNameFilter()->globs(), { "*.txt" });
// Only "sub-dir", "text1.txt" and "text2.txt" should be visible, since *.txt is the first filter.
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
QString failureMessage;
- QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView,
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
{ tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() }, failureMessage), qPrintable(failureMessage));
// Open the ComboBox's popup.
@@ -903,7 +958,7 @@ void tst_QQuickFileDialogImpl::changeNameFilters()
QTRY_VERIFY(!comboBox->popup()->isVisible());
// Use QTRY_VERIFY2 here to fix a failure on QEMU armv7 (QT_QPA_PLATFORM=offscreen).
// Not sure why it's necessary.
- QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView, { tempSubDir.path() }, failureMessage), qPrintable(failureMessage));
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView, { tempSubDir.path() }, failureMessage), qPrintable(failureMessage));
// Open the popup again.
QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, comboBoxCenterPos);
@@ -917,27 +972,22 @@ void tst_QQuickFileDialogImpl::changeNameFilters()
QVERIFY(clickButton(txtDelegate));
}
QTRY_VERIFY(!comboBox->popup()->isVisible());
- QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView,
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
{ tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() }, failureMessage), qPrintable(failureMessage));
}
void tst_QQuickFileDialogImpl::changeNameFiltersAfterChangingFolder()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindAllTxtHtmlNameFilters.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "bindAllTxtHtmlNameFilters.qml");
+ OPEN_QUICK_DIALOG();
// Go into the "sub-dir" folder.
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
QString failureMessage;
- QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView,
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView,
{ tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() }, failureMessage), qPrintable(failureMessage));
QQuickFileDialogDelegate *subDirDelegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subDirDelegate));
+ QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 0, subDirDelegate));
QVERIFY(doubleClickButton(subDirDelegate));
COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
@@ -959,25 +1009,20 @@ void tst_QQuickFileDialogImpl::changeNameFiltersAfterChangingFolder()
}
QTRY_VERIFY(!comboBox->popup()->isVisible());
// There are no HTML files in "sub-dir", so there should only be the one "sub-sub-dir" delegate.
- QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView, { tempSubSubDir.path() }, failureMessage), qPrintable(failureMessage));
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView, { tempSubSubDir.path() }, failureMessage), qPrintable(failureMessage));
}
void tst_QQuickFileDialogImpl::tabFocusNavigation()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindTxtHtmlNameFilters.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "bindTxtHtmlNameFilters.qml");
+ OPEN_QUICK_DIALOG();
QList<QQuickItem*> expectedFocusItems;
// The initial focus should be on the first delegate.
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
QQuickFileDialogDelegate *firstDelegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, firstDelegate));
+ QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 0, firstDelegate));
expectedFocusItems << firstDelegate;
// Tab should move to the name filters combobox.
@@ -1004,9 +1049,9 @@ void tst_QQuickFileDialogImpl::tabFocusNavigation()
expectedFocusItems << breadcrumbBar->upButton();
// Finally, add each bread crumb delegate.
- for (int i = 0; i < fileDialogListView->count(); ++i) {
+ for (int i = 0; i < dialogHelper.fileDialogListView->count(); ++i) {
QQuickFileDialogDelegate *delegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, i, delegate));
+ QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, i, delegate));
expectedFocusItems << delegate;
}
@@ -1036,11 +1081,8 @@ void tst_QQuickFileDialogImpl::tabFocusNavigation()
void tst_QQuickFileDialogImpl::acceptRejectLabel()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "acceptRejectLabel.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "acceptRejectLabel.qml");
+ OPEN_QUICK_DIALOG();
// Check that the accept and reject buttons' labels have changed.
auto dialogButtonBox = dialogHelper.quickDialog->footer()->findChild<QQuickDialogButtonBox*>();
@@ -1074,11 +1116,8 @@ void tst_QQuickFileDialogImpl::acceptRejectLabel()
void tst_QQuickFileDialogImpl::bindTitle()
{
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindTitle.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "bindTitle.qml");
+ OPEN_QUICK_DIALOG();
// Open the dialog and check that the correct title is displayed.
QQuickFileDialog *dialog = dialogHelper.window()->property("dialog").value<QQuickFileDialog*>();
@@ -1102,12 +1141,9 @@ void tst_QQuickFileDialogImpl::itemsDisabledWhenNecessary()
QVERIFY(subDir.cd("emptyDir"));
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindCurrentFolder.qml", {},
+ FileDialogTestHelper dialogHelper(this, "bindCurrentFolder.qml", {},
{{ "initialFolder", QUrl::fromLocalFile(subDir.path()) }});
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ OPEN_QUICK_DIALOG();
COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(subDir.path()));
COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(subDir.path()));
@@ -1128,7 +1164,6 @@ void tst_QQuickFileDialogImpl::itemsDisabledWhenNecessary()
COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(anotherTempDir.path()));
// Get the text edit visible with Ctrl+L. The Open button should now be disabled.
- const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
QVERIFY(breadcrumbBar->textField()->isVisible());
QCOMPARE(openButton->isEnabled(), false);
@@ -1153,18 +1188,13 @@ void tst_QQuickFileDialogImpl::fileMode()
QFETCH(QQuickFileDialog::FileMode, fileMode);
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
dialogHelper.dialog->setFileMode(fileMode);
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ OPEN_QUICK_DIALOG();
// Select the first file (not a directory).
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
QQuickFileDialogDelegate *tempFile1Delegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 1, tempFile1Delegate));
+ QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 1, tempFile1Delegate));
COMPARE_URL(tempFile1Delegate->file(), QUrl::fromLocalFile(tempFile1->fileName()));
QVERIFY(clickButton(tempFile1Delegate));
COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1->fileName()));
@@ -1180,7 +1210,7 @@ void tst_QQuickFileDialogImpl::fileMode()
// Only the OpenFiles mode should allow multiple files to be selected, however.
QQuickFileDialogDelegate *tempFile2Delegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 2, tempFile2Delegate));
+ QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 2, tempFile2Delegate));
COMPARE_URL(tempFile2Delegate->file(), QUrl::fromLocalFile(tempFile2->fileName()));
QTest::keyPress(dialogHelper.window(), Qt::Key_Shift);
QVERIFY(clickButton(tempFile2Delegate));
@@ -1199,7 +1229,6 @@ void tst_QQuickFileDialogImpl::fileMode()
}
// Get the text edit visible with Ctrl+L.
- const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
QTest::keySequence(dialogHelper.window(), editPathKeySequence);
auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
QVERIFY(breadcrumbBar);
@@ -1252,25 +1281,20 @@ void tst_QQuickFileDialogImpl::defaultSuffix()
QVERIFY(tempFile1.open(QIODevice::ReadWrite));
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindCurrentFolder.qml", {},
+ FileDialogTestHelper dialogHelper(this, "bindCurrentFolder.qml", {},
{{ "initialFolder", QUrl::fromLocalFile(tempSubSubDir.path()) }});
dialogHelper.dialog->setDefaultSuffix(defaultSuffix);
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ OPEN_QUICK_DIALOG();
COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubSubDir.path()));
// There should be one extension-less file: "file1".
- auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
- QVERIFY(fileDialogListView);
QString failureMessage;
const QStringList expectedVisibleFiles = { tempFile1.fileName() };
- QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView, expectedVisibleFiles, failureMessage), qPrintable(failureMessage));
+ QTRY_VERIFY2(verifyFileDialogDelegates(dialogHelper.fileDialogListView, expectedVisibleFiles, failureMessage), qPrintable(failureMessage));
// Choose the delegate. The suffix should be added to the delegates.
QQuickFileDialogDelegate *file1Delegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, file1Delegate));
+ QTRY_VERIFY(findViewDelegateItem(dialogHelper.fileDialogListView, 0, file1Delegate));
COMPARE_URL(file1Delegate->file(), QUrl::fromLocalFile(tempFile1.fileName()));
QVERIFY(doubleClickButton(file1Delegate));
QVERIFY(!dialogHelper.dialog->isVisible());
@@ -1293,11 +1317,8 @@ void tst_QQuickFileDialogImpl::done()
QFETCH(QQuickFileDialog::StandardCode, result);
// Open the dialog.
- DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
- QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
- QVERIFY(dialogHelper.waitForWindowActive());
- QVERIFY(dialogHelper.openDialog());
- QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ FileDialogTestHelper dialogHelper(this, "fileDialog.qml");
+ OPEN_QUICK_DIALOG();
switch (result) {
case QQuickFileDialog::Accepted:
@@ -1313,6 +1334,70 @@ void tst_QQuickFileDialogImpl::done()
QCOMPARE(dialogHelper.dialog->result(), result);
}
+void tst_QQuickFileDialogImpl::setSelectedFile_data()
+{
+ fileMode_data();
+}
+
+void tst_QQuickFileDialogImpl::setSelectedFile()
+{
+ QFETCH(QQuickFileDialog::FileMode, fileMode);
+
+ // Open the dialog.
+ const auto tempFile1Url = QUrl::fromLocalFile(tempFile1->fileName());
+ const QVariantMap initialProperties = {
+ { "tempFile1Url", QVariant::fromValue(tempFile1Url) },
+ { "fileMode", QVariant::fromValue(fileMode) }
+ };
+ FileDialogTestHelper dialogHelper(
+ this, "setSelectedFile.qml", {}, initialProperties);
+ OPEN_QUICK_DIALOG();
+
+ // The selected file should be what we set.
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), tempFile1Url, 1);
+
+ // Select the next file in the view by navigating with the down key.
+ // We know it already has focus, as VERIFY_FILE_SELECTED_AND_FOCUSED checks that.
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Down);
+ const auto tempFile2Url = QUrl::fromLocalFile(tempFile2->fileName());
+ COMPARE_URL(dialogHelper.quickDialog->selectedFile(), tempFile2Url);
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), tempFile2Url);
+
+ // Select the delegate by pressing enter.
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), tempFile2Url);
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { tempFile2Url });
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+ QCOMPARE(dialogHelper.dialog->result(), QQuickFileDialog::Accepted);
+
+ // Set a different initial selectedFile and re-open.
+ dialogHelper.dialog->setSelectedFile(QUrl::fromLocalFile(tempFile1->fileName()));
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), tempFile1Url, 1);
+
+ // Close it.
+ dialogHelper.dialog->close();
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+
+ // Try to set an invalid selectedFile; it should be a no-op for modes other than SaveFile,
+ // and the previous selectedFile should still be selected.
+ const QString invalidPath = tempDir.path() + "/does-not-exist.txt";
+ if (fileMode != QQuickFileDialog::SaveFile) {
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(QLatin1String(".*QML FileDialog: Cannot set ")
+ + invalidPath + QLatin1String(" as a selected file because it doesn't exist")));
+ }
+ dialogHelper.dialog->setSelectedFile(QUrl::fromLocalFile(invalidPath));
+ QVERIFY(dialogHelper.openDialog());
+ if (fileMode != QQuickFileDialog::SaveFile) {
+ VERIFY_FILE_SELECTED_AND_FOCUSED(QUrl::fromLocalFile(tempDir.path()), tempFile1Url, 1);
+ } else {
+ QTRY_COMPARE(dialogHelper.fileDialogListView->currentIndex(), -1);
+ }
+}
+
QTEST_MAIN(tst_QQuickFileDialogImpl)
#include "tst_qquickfiledialogimpl.moc"
diff --git a/tests/auto/quickdialogs/qquickfontdialogimpl/tst_qquickfontdialogimpl.cpp b/tests/auto/quickdialogs/qquickfontdialogimpl/tst_qquickfontdialogimpl.cpp
index 1bfb8b6199..a4c4f6aab5 100644
--- a/tests/auto/quickdialogs/qquickfontdialogimpl/tst_qquickfontdialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickfontdialogimpl/tst_qquickfontdialogimpl.cpp
@@ -133,8 +133,11 @@ private:
QVERIFY2(closePopup(&dialogHelper, BUTTON, errorMessage), qPrintable(errorMessage)); \
QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+// We don't want to fail on warnings until QTBUG-98964 is fixed,
+// as we deliberately prevent deferred execution in some of the tests here,
+// which causes warnings.
tst_QQuickFontDialogImpl::tst_QQuickFontDialogImpl()
- : QQmlDataTest(QT_QMLTEST_DATADIR)
+ : QQmlDataTest(QT_QMLTEST_DATADIR, FailOnWarningsPolicy::DoNotFailOnWarnings)
{
}
diff --git a/tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp b/tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp
index 4123772317..f7edd2dd70 100644
--- a/tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickmessagedialogimpl/tst_qquickmessagedialogimpl.cpp
@@ -77,7 +77,13 @@ private slots:
void detailedText();
};
-tst_QQuickMessageDialogImpl::tst_QQuickMessageDialogImpl() : QQmlDataTest(QT_QMLTEST_DATADIR) { }
+// We don't want to fail on warnings until QTBUG-98964 is fixed,
+// as we deliberately prevent deferred execution in some of the tests here,
+// which causes warnings.
+tst_QQuickMessageDialogImpl::tst_QQuickMessageDialogImpl()
+ : QQmlDataTest(QT_QMLTEST_DATADIR, FailOnWarningsPolicy::DoNotFailOnWarnings)
+{
+}
void tst_QQuickMessageDialogImpl::changeText_data()
{
diff --git a/tests/auto/quicktest/signalspy/tst_signalspy.cpp b/tests/auto/quicktest/signalspy/tst_signalspy.cpp
index 6ae099845e..120e4394e1 100644
--- a/tests/auto/quicktest/signalspy/tst_signalspy.cpp
+++ b/tests/auto/quicktest/signalspy/tst_signalspy.cpp
@@ -79,7 +79,7 @@ void tst_SignalSpy::testCount()
window.resize(200, 200);
window.setSource(url);
window.show();
- QVERIFY(QTest::qWaitForWindowActive(&window));
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
QVERIFY(window.rootObject() != nullptr);
QObject *mouseSpy = window.rootObject()->findChild<QObject *>("mouseSpy");
diff --git a/tests/auto/quickwidgets/qquickwidget/BLACKLIST b/tests/auto/quickwidgets/qquickwidget/BLACKLIST
index 44ab3e9397..095e9ee484 100644
--- a/tests/auto/quickwidgets/qquickwidget/BLACKLIST
+++ b/tests/auto/quickwidgets/qquickwidget/BLACKLIST
@@ -3,5 +3,3 @@ opensuse-42.3
opensuse-leap
[enterLeave]
macos
-[synthMouseFromTouch] # QTBUG-86729
-*
diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
index 35187feaf8..1ef70e6515 100644
--- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
+++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
@@ -86,17 +86,18 @@ public:
class MouseRecordingItem : public QQuickItem
{
public:
- MouseRecordingItem(bool acceptTouch, QQuickItem *parent = nullptr)
+ MouseRecordingItem(bool acceptTouch, bool acceptTouchPress, QQuickItem *parent = nullptr)
: QQuickItem(parent)
- , m_acceptTouch(acceptTouch)
+ , m_acceptTouchPress(acceptTouchPress)
{
setSize(QSizeF(300, 300));
setAcceptedMouseButtons(Qt::LeftButton);
+ setAcceptTouchEvents(acceptTouch);
}
protected:
void touchEvent(QTouchEvent* event) override {
- event->setAccepted(m_acceptTouch);
+ event->setAccepted(m_acceptTouchPress);
m_touchEvents << event->type();
qCDebug(lcTests) << "accepted?" << event->isAccepted() << event;
}
@@ -118,7 +119,7 @@ public:
QList<QEvent::Type> m_touchEvents;
private:
- bool m_acceptTouch;
+ bool m_acceptTouchPress;
};
class tst_qquickwidget : public QQmlDataTest
@@ -625,7 +626,7 @@ void tst_qquickwidget::synthMouseFromTouch()
QWidget window;
window.setAttribute(Qt::WA_AcceptTouchEvents);
QScopedPointer<MouseRecordingQQWidget> childView(new MouseRecordingQQWidget(&window));
- MouseRecordingItem *item = new MouseRecordingItem(acceptTouch, nullptr);
+ MouseRecordingItem *item = new MouseRecordingItem(!synthMouse, acceptTouch, nullptr);
childView->setContent(QUrl(), nullptr, item);
window.resize(300, 300);
childView->resize(300, 300);
@@ -640,8 +641,8 @@ void tst_qquickwidget::synthMouseFromTouch()
QTest::touchEvent(&window, device).move(0, p2, &window);
QTest::touchEvent(&window, device).release(0, p2, &window);
- QCOMPARE(item->m_touchEvents.count(), !synthMouse && !acceptTouch ? 1 : 3);
- QCOMPARE(item->m_mouseEvents.count(), (acceptTouch || !synthMouse) ? 0 : 3);
+ QCOMPARE(item->m_touchEvents.count(), synthMouse ? 0 : (acceptTouch ? 3 : 1));
+ QCOMPARE(item->m_mouseEvents.count(), synthMouse ? 3 : 0);
QCOMPARE(childView->m_mouseEvents.count(), 0);
for (const auto &ev : item->m_mouseEvents)
QCOMPARE(ev, Qt::MouseEventSynthesizedByQt);
diff --git a/tests/baseline/scenegraph/data/text/text_nativerendering_no_antialiasing.qml b/tests/baseline/scenegraph/data/text/text_nativerendering_no_antialiasing.qml
new file mode 100644
index 0000000000..92598eb490
--- /dev/null
+++ b/tests/baseline/scenegraph/data/text/text_nativerendering_no_antialiasing.qml
@@ -0,0 +1,25 @@
+import QtQuick 2.0
+
+//vary font style, native rendering without antialiasing
+
+Item {
+ id: topLevel
+ width: 320
+ height: 580
+
+ Repeater {
+ model: [Text.Normal, Text.Outline, Text.Raised, Text.Sunken]
+ Text {
+ y: 20 * index
+ clip: true
+ renderType: Text.NativeRendering
+ width: parent.width
+ wrapMode: Text.Wrap
+ font.pointSize: 10
+ style: modelData
+ styleColor: "green"
+ antialiasing: false
+ text: "The quick fox jumps in style " + modelData
+ }
+ }
+}
diff --git a/tests/benchmarks/quickcontrols2/creationtime/CMakeLists.txt b/tests/benchmarks/quickcontrols2/creationtime/CMakeLists.txt
index 9e3a6d58c1..ad39b3ea0b 100644
--- a/tests/benchmarks/quickcontrols2/creationtime/CMakeLists.txt
+++ b/tests/benchmarks/quickcontrols2/creationtime/CMakeLists.txt
@@ -1,5 +1,11 @@
# Generated from creationtime.pro.
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_creationtime LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
#####################################################################
## tst_creationtime Test:
#####################################################################
diff --git a/tests/benchmarks/quickcontrols2/objectcount/CMakeLists.txt b/tests/benchmarks/quickcontrols2/objectcount/CMakeLists.txt
index 752230ec7e..e0434a08eb 100644
--- a/tests/benchmarks/quickcontrols2/objectcount/CMakeLists.txt
+++ b/tests/benchmarks/quickcontrols2/objectcount/CMakeLists.txt
@@ -1,5 +1,11 @@
# Generated from objectcount.pro.
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_objectcount LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
#####################################################################
## tst_objectcount Test:
#####################################################################
diff --git a/tests/manual/quickcontrols2/testbench/controls/DelayButton.qml b/tests/manual/quickcontrols2/testbench/controls/DelayButton.qml
index c0f6faa29b..914c1c5632 100644
--- a/tests/manual/quickcontrols2/testbench/controls/DelayButton.qml
+++ b/tests/manual/quickcontrols2/testbench/controls/DelayButton.qml
@@ -63,10 +63,9 @@ QtObject {
property Component component: Component {
DelayButton {
text: "DelayButton"
-// enabled: !is("disabled")
+ enabled: !is("disabled")
// Only set it if it's pressed, or the non-pressed examples will have no press effects
-// down: is("pressed") ? true : undefined
- onDownChanged: print("down", down)
+ down: is("pressed") ? true : undefined
}
}
}
diff --git a/tests/manual/quickdialogs/dialogs/FileDialogPage.qml b/tests/manual/quickdialogs/dialogs/FileDialogPage.qml
index 211725b8ce..d0eae9b07b 100644
--- a/tests/manual/quickdialogs/dialogs/FileDialogPage.qml
+++ b/tests/manual/quickdialogs/dialogs/FileDialogPage.qml
@@ -273,7 +273,6 @@ ColumnLayout {
TextField {
id: selectedFileTextField
text: fileDialog.selectedFile
- readOnly: true
selectByMouse: true
Layout.fillWidth: true
@@ -352,6 +351,7 @@ ColumnLayout {
| hideNameFilterDetailsCheckBox.fileOption
nameFilters: nameFiltersTextField.text.split(",")
rejectLabel: rejectLabelTextField.text
+ selectedFile: selectedFileTextField.text
}
}
}