diff options
Diffstat (limited to 'tests')
209 files changed, 4433 insertions, 935 deletions
diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt index f2d0ca547a..3ef1b6e735 100644 --- a/tests/auto/CMakeLists.txt +++ b/tests/auto/CMakeLists.txt @@ -23,7 +23,7 @@ endif() if(TARGET Qt::QuickWidgets) add_subdirectory(quickwidgets) endif() -if(TARGET Qt::QmlDomPrivate) +if(TARGET Qt::QmlDomPrivate AND NOT CMAKE_CROSSCOMPILING) add_subdirectory(qmldom) add_subdirectory(qmlls) endif() diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt index 1d75a0bdba..4ea17fb0eb 100644 --- a/tests/auto/cmake/CMakeLists.txt +++ b/tests/auto/cmake/CMakeLists.txt @@ -65,7 +65,9 @@ if(TARGET Qt::Qml) _qt_internal_test_expect_pass(test_plugins) endif() _qt_internal_test_expect_pass(empty_qmldir) - _qt_internal_test_expect_pass(qmlquery) + If(NOT ANDROID) # QML only project cannot run on Android with C++ enty point + _qt_internal_test_expect_pass(qmlquery) + endif() endif() if(TARGET Qt::Quick) diff --git a/tests/auto/cmake/empty_qmldir/CMakeLists.txt b/tests/auto/cmake/empty_qmldir/CMakeLists.txt index 432e6e99da..259adc2610 100644 --- a/tests/auto/cmake/empty_qmldir/CMakeLists.txt +++ b/tests/auto/cmake/empty_qmldir/CMakeLists.txt @@ -14,6 +14,11 @@ endif() qt_add_executable(empty_qmldir_test main.cpp) target_link_libraries(empty_qmldir_test PRIVATE Qt6::Test) +if(ANDROID) + # Tests link to Gui by default beacuse it's needed for Android (QTBUG-83997) when + # using qt_internal_add_test() but not here where qt_add_executable() is used. + target_link_libraries(empty_qmldir_test PRIVATE Qt6::Gui) +endif() qt_add_qml_module(empty_qmldir_test URI Third diff --git a/tests/auto/core/CMakeLists.txt b/tests/auto/core/CMakeLists.txt index 31413de9c4..a57498db91 100644 --- a/tests/auto/core/CMakeLists.txt +++ b/tests/auto/core/CMakeLists.txt @@ -14,3 +14,7 @@ qt_internal_add_test(tst_core Qt::Qml TESTDATA ${test_data} ) + +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_core) +endif() diff --git a/tests/auto/qml/CMakeLists.txt b/tests/auto/qml/CMakeLists.txt index 4c61f59736..fd587e93a6 100644 --- a/tests/auto/qml/CMakeLists.txt +++ b/tests/auto/qml/CMakeLists.txt @@ -32,7 +32,12 @@ add_subdirectory(qtqmlmodules) add_subdirectory(qquickfolderlistmodel) add_subdirectory(qqmlapplicationengine) add_subdirectory(qqmlsettings) -add_subdirectory(qmldiskcache) + +if(NOT INTEGRITY) +# There's no mounted filesystem on INTEGRITY therefore skipping qmldiskcache + add_subdirectory(qmldiskcache) +endif() + add_subdirectory(qqmlmetatype) if(TARGET Qt::Quick) add_subdirectory(qmltc_manual) @@ -44,7 +49,9 @@ if(TARGET Qt::Quick) (QT_BUILD_STANDALONE_TESTS OR QT6_IS_SHARED_LIBS_BUILD)) add_subdirectory(qmlbasicapp) endif() - add_subdirectory(qqmljsscope) + if(NOT CMAKE_CROSSCOMPILING) + add_subdirectory(qqmljsscope) + endif() endif() add_subdirectory(qmlsplitlib) if(TARGET Qt::Widgets) diff --git a/tests/auto/qml/bindingdependencyapi/dummy_imports.qml b/tests/auto/qml/bindingdependencyapi/dummy_imports.qml index b9a196e188..63d33cbea6 100644 --- a/tests/auto/qml/bindingdependencyapi/dummy_imports.qml +++ b/tests/auto/qml/bindingdependencyapi/dummy_imports.qml @@ -1,8 +1,7 @@ // This file exists for the sole purpose for qmlimportscanner to find // which modules it needs to extract for deployment. -// Otherwise, it fails to find the imports that are expressed in C++ -// code in tst_parserstress.cpp +// Otherwise, it fails to find the imports that are expressed in C++. -import QtQuick 2.0 +import QtQuick -QtObject { } // This is needed in order to keep importscanner happy +QtObject { } diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp index 69bb86ef09..2c7d2ed9f5 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp +++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp @@ -1075,7 +1075,7 @@ void tst_QQmlDebugJS::letConstLocals() const auto value = m_client->response(); if (value.command == QStringLiteral("frame")) { const auto scopes = value.body.toObject().value(QStringLiteral("scopes")).toArray(); - for (const QJsonValueRef scope : scopes) { + for (const auto scope : scopes) { const auto scopeObject = scope.toObject(); const int type = scopeObject.value("type").toInt(); if (type == 1 || type == 4) { @@ -1087,7 +1087,7 @@ void tst_QQmlDebugJS::letConstLocals() } else if (value.command == QStringLiteral("scope")) { const auto props = value.body.toObject().value(QStringLiteral("object")).toObject() .value(QStringLiteral("properties")).toArray(); - for (const QJsonValueRef prop : props) { + for (const auto prop : props) { const auto propObj = prop.toObject(); QString name = propObj.value(QStringLiteral("name")).toString(); QVERIFY(name.length() == 1); diff --git a/tests/auto/qml/parserstress/CMakeLists.txt b/tests/auto/qml/parserstress/CMakeLists.txt index 2d2f3df4c3..4505dab1da 100644 --- a/tests/auto/qml/parserstress/CMakeLists.txt +++ b/tests/auto/qml/parserstress/CMakeLists.txt @@ -18,8 +18,12 @@ qt_internal_add_test(tst_parserstress Qt::Gui Qt::GuiPrivate Qt::QmlPrivate - TESTDATA ${test_data} + TESTDATA ${test_data} "dummy_imports.qml" ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_parserstress) +endif() + ## Scopes: ##################################################################### diff --git a/tests/auto/qml/parserstress/dummy_imports.qml b/tests/auto/qml/parserstress/dummy_imports.qml index b9a196e188..63d33cbea6 100644 --- a/tests/auto/qml/parserstress/dummy_imports.qml +++ b/tests/auto/qml/parserstress/dummy_imports.qml @@ -1,8 +1,7 @@ // This file exists for the sole purpose for qmlimportscanner to find // which modules it needs to extract for deployment. -// Otherwise, it fails to find the imports that are expressed in C++ -// code in tst_parserstress.cpp +// Otherwise, it fails to find the imports that are expressed in C++. -import QtQuick 2.0 +import QtQuick -QtObject { } // This is needed in order to keep importscanner happy +QtObject { } diff --git a/tests/auto/qml/qjsengine/CMakeLists.txt b/tests/auto/qml/qjsengine/CMakeLists.txt index 6dc74b8858..3e8939a546 100644 --- a/tests/auto/qml/qjsengine/CMakeLists.txt +++ b/tests/auto/qml/qjsengine/CMakeLists.txt @@ -27,7 +27,7 @@ qt_internal_add_test(tst_qjsengine Qt::QmlPrivate LIBRARIES # special case Threads::Threads # special case - TESTDATA ${test_data} + TESTDATA ${test_data} "dummy_imports.qml" ) # Resources: @@ -61,6 +61,10 @@ qt_internal_add_resource(tst_qjsengine "qmake_immediate" ${qmake_immediate_resource_files} ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qjsengine) +endif() + ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qjsengine/dummy_imports.qml b/tests/auto/qml/qjsengine/dummy_imports.qml index 8d86f3583b..afe2b33adf 100644 --- a/tests/auto/qml/qjsengine/dummy_imports.qml +++ b/tests/auto/qml/qjsengine/dummy_imports.qml @@ -1,8 +1,7 @@ // This file exists for the sole purpose for qmlimportscanner to find // which modules it needs to extract for deployment. -// Otherwise, it fails to find the imports that are expressed in C++ -// code in tst_parserstress.cpp +// Otherwise, it fails to find the imports that are expressed in C++. -import QtQml 2.0 +import QtQml -QtObject { } // This is needed in order to keep importscanner happy +QtObject { } diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index a92141a1e5..6ffc1da3a5 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -69,6 +69,8 @@ private slots: void newArray_HooliganTask233836(); void toScriptValueBuiltin_data(); void toScriptValueBuiltin(); + void toScriptValueQmlBuiltin_data(); + void toScriptValueQmlBuiltin(); void toScriptValueQtQml_data(); void toScriptValueQtQml(); void toScriptValuenotroundtripped_data(); @@ -580,6 +582,36 @@ void tst_QJSEngine::toScriptValueBuiltin() QCOMPARE(input, output); } +void tst_QJSEngine::toScriptValueQmlBuiltin_data() +{ + QTest::addColumn<QVariant>("input"); + + QTest::newRow("QList<QVariant>") << QVariant(QList<QVariant>{true, 5, 13.2f, 42.24, QString("world"), QUrl("htt://a.com"), QDateTime::currentDateTime(), QRegularExpression("a*b*c"), QByteArray("hello")}); + QTest::newRow("QList<bool>") << QVariant::fromValue(QList<bool>{true, false, true, false}); + QTest::newRow("QList<int>") << QVariant::fromValue(QList<int>{1, 2, 3, 4}); + QTest::newRow("QList<float>") << QVariant::fromValue(QList<float>{1.1f, 2.2f, 3.3f, 4.4f}); + QTest::newRow("QList<double>") << QVariant::fromValue(QList<double>{1.1, 2.2, 3.3, 4.4}); + QTest::newRow("QList<QString>") << QVariant::fromValue(QList<QString>{"a", "b", "c", "d"}); + QTest::newRow("QList<QUrl>") << QVariant::fromValue(QList<QUrl>{QUrl("htt://a.com"), QUrl("file:///tmp/b/"), QUrl("c.foo"), QUrl("/some/d")}); + QTest::newRow("QList<QDateTime>") << QVariant::fromValue(QList<QDateTime>{QDateTime::currentDateTime(), QDateTime::fromMSecsSinceEpoch(300), QDateTime()}); + QTest::newRow("QList<QRegularExpression>") << QVariant::fromValue(QList<QRegularExpression>{QRegularExpression("abcd"), QRegularExpression("a[b|c]d$"), QRegularExpression("a*b*d")}); + QTest::newRow("QList<QByteArray>") << QVariant::fromValue(QList<QByteArray>{QByteArray("aaa"), QByteArray("bbb"), QByteArray("ccc")}); +} + +void tst_QJSEngine::toScriptValueQmlBuiltin() +{ + QFETCH(QVariant, input); + + // We need the type registrations in QQmlEngine::init() for this. + QQmlEngine engine; + + QJSValue outputJS = engine.toScriptValue(input); + QVariant output = engine.fromScriptValue<QVariant>(outputJS); + + QVERIFY(output.convert(input.metaType())); + QCOMPARE(input, output); +} + void tst_QJSEngine::toScriptValueQtQml_data() { QTest::addColumn<QVariant>("input"); @@ -592,10 +624,6 @@ void tst_QJSEngine::toScriptValueQtQml_data() QTest::newRow("std::vector<QString>") << QVariant::fromValue(std::vector<QString>{"a", "b", "c", "d"}); QTest::newRow("std::vector<QUrl>") << QVariant::fromValue(std::vector<QUrl>{QUrl("htt://a.com"), QUrl("file:///tmp/b/"), QUrl("c.foo"), QUrl("/some/d")}); - QTest::newRow("QList<int>") << QVariant::fromValue(QList<int>{1, 2, 3, 4}); - QTest::newRow("QList<bool>") << QVariant::fromValue(QList<bool>{true, false, true, false}); - QTest::newRow("QStringList") << QVariant::fromValue(QStringList{"a", "b", "c", "d"}); - QTest::newRow("QList<QUrl>") << QVariant::fromValue(QList<QUrl>{QUrl("htt://a.com"), QUrl("file:///tmp/b/"), QUrl("c.foo"), QUrl("/some/d")}); QTest::newRow("QList<QPoint>") << QVariant::fromValue(QList<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)); static const QStandardItemModel model(4, 4); @@ -5325,7 +5353,7 @@ void tst_QJSEngine::typedArraySet() QJSEngine engine; const auto value = engine.evaluate( "(function() {" - " var length = 0xffffffe;" + " var length = 0xfffffe0;" " var offset = 0xfffffff0;" " var e1;" " var e2;" diff --git a/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt b/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt index 3e466d8e2e..2449b9a320 100644 --- a/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt +++ b/tests/auto/qml/qjsmanagedvalue/CMakeLists.txt @@ -10,7 +10,12 @@ qt_internal_add_test(tst_qjsmanagedvalue PUBLIC_LIBRARIES Qt::Qml Qt::QmlPrivate + TESTDATA "dummy_imports.qml" ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qjsmanagedvalue) +endif() + ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qjsmanagedvalue/dummy_imports.qml b/tests/auto/qml/qjsmanagedvalue/dummy_imports.qml index 1cc20b8aba..afe2b33adf 100644 --- a/tests/auto/qml/qjsmanagedvalue/dummy_imports.qml +++ b/tests/auto/qml/qjsmanagedvalue/dummy_imports.qml @@ -1,7 +1,7 @@ // This file exists for the sole purpose for qmlimportscanner to find // which modules it needs to extract for deployment. -// Otherwise, it fails to find the imports that are expressed in C++ +// Otherwise, it fails to find the imports that are expressed in C++. import QtQml -QtObject { } // This is needed in order to keep importscanner happy +QtObject { } diff --git a/tests/auto/qml/qjsvalue/CMakeLists.txt b/tests/auto/qml/qjsvalue/CMakeLists.txt index bb00f3221a..b8ee79713c 100644 --- a/tests/auto/qml/qjsvalue/CMakeLists.txt +++ b/tests/auto/qml/qjsvalue/CMakeLists.txt @@ -22,7 +22,12 @@ qt_internal_add_test(tst_qjsvalue Qt::Widgets LIBRARIES # special case Threads::Threads # special case + TESTDATA "dummy_imports.qml" ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qjsvalue) +endif() + ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qjsvalue/dummy_imports.qml b/tests/auto/qml/qjsvalue/dummy_imports.qml index 1cc20b8aba..afe2b33adf 100644 --- a/tests/auto/qml/qjsvalue/dummy_imports.qml +++ b/tests/auto/qml/qjsvalue/dummy_imports.qml @@ -1,7 +1,7 @@ // This file exists for the sole purpose for qmlimportscanner to find // which modules it needs to extract for deployment. -// Otherwise, it fails to find the imports that are expressed in C++ +// Otherwise, it fails to find the imports that are expressed in C++. import QtQml -QtObject { } // This is needed in order to keep importscanner happy +QtObject { } diff --git a/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp b/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp index 2fd7ef634b..a6c6e4accd 100644 --- a/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp +++ b/tests/auto/qml/qmlbasicapp/tst_qmlbasicapp.cpp @@ -44,6 +44,9 @@ private slots: void tst_basicapp::loadComponent() { QQmlEngine engine; +#ifdef Q_OS_ANDROID + engine.addImportPath(":/"); +#endif QQmlComponent c(&engine, QStringLiteral("qrc:/BasicApp/main.qml")); QVERIFY2(c.isReady(), qPrintable(c.errorString())); QScopedPointer o(c.create()); @@ -83,6 +86,9 @@ void tst_basicapp::resourceFiles() void tst_basicapp::fileSystemFiles() { +#ifdef Q_OS_ANDROID + QSKIP("This test is not valid for Android, because the files can exist only as resources."); +#endif const QString basedir = QCoreApplication::applicationDirPath(); QVERIFY(QFile::exists(basedir + QStringLiteral("/BasicApp/main.qml"))); QVERIFY(QFile::exists(basedir + QStringLiteral("/BasicApp/qmldir"))); @@ -99,8 +105,13 @@ void tst_basicapp::fileSystemFiles() void tst_basicapp::qmldirContents() { +#ifdef Q_OS_ANDROID + const QString basedir = QStringLiteral(":"); // Use qrc resource path on Android +#else + const QString basedir = QCoreApplication::applicationDirPath(); +#endif { - QFile qmldir(QCoreApplication::applicationDirPath() + "/BasicApp/qmldir"); + QFile qmldir(basedir + "/BasicApp/qmldir"); QVERIFY(qmldir.open(QIODevice::ReadOnly)); const QByteArray contents = qmldir.readAll(); QVERIFY(contents.contains("module BasicApp")); @@ -115,7 +126,7 @@ void tst_basicapp::qmldirContents() } { - QFile qmldir(QCoreApplication::applicationDirPath() + "/TimeExample/qmldir"); + QFile qmldir(basedir + "/TimeExample/qmldir"); QVERIFY(qmldir.open(QIODevice::ReadOnly)); const QByteArray contents = qmldir.readAll(); QVERIFY(contents.contains("module TimeExample")); @@ -132,7 +143,7 @@ void tst_basicapp::qmldirContents() } { - QFile qmldir(QCoreApplication::applicationDirPath() + "/BasicExtension/qmldir"); + QFile qmldir(basedir + "/BasicExtension/qmldir"); QVERIFY(qmldir.open(QIODevice::ReadOnly)); const QByteArray contents = qmldir.readAll(); QVERIFY(contents.contains("More 1.0 More.ui.qml")); diff --git a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt index 4c4cf0b1e5..946fa38e44 100644 --- a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt +++ b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt @@ -4,7 +4,7 @@ qt_internal_add_test(tst_qmlcppcodegen SOURCES tst_qmlcppcodegen.cpp LIBRARIES - Qt::Qml + Qt::QmlPrivate Qt::Gui codegen_test_module codegen_test_moduleplugin diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt index 053c3bccc2..bdfe81a1ac 100644 --- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt +++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt @@ -1,6 +1,7 @@ set(cpp_sources birthdayparty.cpp birthdayparty.h cppbaseclass.h + dynamicmeta.h objectwithmethod.h person.cpp person.h theme.cpp theme.h @@ -35,6 +36,7 @@ set(qml_files asCast.qml attachedBaseEnum.qml bindToValueType.qml + blockComments.qml callContextPropertyLookupResult.qml childobject.qml colorAsVariant.qml @@ -49,6 +51,7 @@ set(qml_files curlygrouped.qml cycleHead.qml deadShoeSize.qml + deadStoreLoop.qml dialog.qml dynamicscene.qml enumInvalid.qml @@ -59,19 +62,24 @@ set(qml_files excessiveParameters.qml extendedTypes.qml failures.qml + fallbacklookups.qml fileDialog.qml + functionLookup.qml funcWithParams.qml functionReturningVoid.qml + functionTakingVar.qml globals.qml idAccess.qml immediateQuit.qml imports/QmlBench/Globals.qml importsFromImportPath.qml + infinities.qml invisibleBase.qml intEnumCompare.qml intOverflow.qml interactive.qml interceptor.qml + isnan.qml jsMathObject.qml jsimport.qml jsmoduleimport.qml @@ -82,12 +90,14 @@ set(qml_files math.qml methods.qml modulePrefix.qml + moveRegVoid.qml noBindingLoop.qml noQQmlData.qml nonNotifyable.qml noscope.qml notEqualsInt.qml nullAccess.qml + objectInVar.qml outOfBounds.qml overriddenMember.qml ownProperty.qml @@ -110,6 +120,7 @@ set(qml_files text.qml themerbad.qml themergood.qml + typedArray.qml undefinedResets.qml unknownAttached.qml unknownParameter.qml diff --git a/tests/auto/qml/qmlcppcodegen/data/blockComments.qml b/tests/auto/qml/qmlcppcodegen/data/blockComments.qml new file mode 100644 index 0000000000..da4bb2fd25 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/blockComments.qml @@ -0,0 +1,10 @@ +pragma Strict +import QtQml + +QtObject { + property real implicitHeight: { + return /*+ (control.implicitHeaderHeight > 0 + ? control.implicitHeaderHeight + control.spacing + : 0)*/ 8; + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/conversions2.qml b/tests/auto/qml/qmlcppcodegen/data/conversions2.qml index e7bde6c3e6..c3a9414ae2 100644 --- a/tests/auto/qml/qmlcppcodegen/data/conversions2.qml +++ b/tests/auto/qml/qmlcppcodegen/data/conversions2.qml @@ -5,6 +5,10 @@ Item { id: spy visible: false + Rectangle { + id: rect + } + TestUtil { id: util } @@ -15,6 +19,10 @@ Item { readonly property alias valid:spy.qtest_valid readonly property alias signalArguments:spy.qtest_signalArguments + function clearRectGradient() { + rect.gradient = null; + } + function clear() { qtest_count = 0 qtest_expectedCount = 0 diff --git a/tests/auto/qml/qmlcppcodegen/data/deadStoreLoop.qml b/tests/auto/qml/qmlcppcodegen/data/deadStoreLoop.qml new file mode 100644 index 0000000000..f493e4b942 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/deadStoreLoop.qml @@ -0,0 +1,13 @@ +import QtQuick +import QtQuick.Controls.Basic + +Item { + Menu { + id: m + } + function c() { + while (m.count > 0) { + m.removeItem(m.itemAt(0)) + } + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h b/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h new file mode 100644 index 0000000000..3f02e460e7 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h @@ -0,0 +1,78 @@ +#ifndef DYNAMICMETA_H +#define DYNAMICMETA_H + +#include <private/qobject_p.h> +#include <QtQmlIntegration/qqmlintegration.h> + +struct FreeDeleter { + void operator()(QMetaObject *meta) { free(meta); } +}; + +template<typename T> +class MetaObjectData : public QDynamicMetaObjectData +{ + Q_DISABLE_COPY_MOVE(MetaObjectData) +public: + MetaObjectData() = default; + ~MetaObjectData() = default; + + QMetaObject *toDynamicMetaObject(QObject *) override + { + return const_cast<QMetaObject *>(&T::staticMetaObject); + } + int metaCall(QObject *o, QMetaObject::Call call, int idx, void **argv) override + { + return o->qt_metacall(call, idx, argv); + } +}; + +class DynamicMeta : public QObject +{ + Q_OBJECT + Q_PROPERTY(int foo READ foo WRITE setFoo NOTIFY fooChanged FINAL) + QML_ELEMENT +public: + + DynamicMeta(QObject *parent = nullptr) + : QObject(parent) + { + // deletes itself + QObjectPrivate::get(this)->metaObject = new MetaObjectData<DynamicMeta>; + } + + int foo() const { return m_foo; } + void setFoo(int newFoo) + { + if (m_foo != newFoo) { + m_foo = newFoo; + emit fooChanged(); + } + } + + Q_INVOKABLE int bar(int baz) { return baz + 12; } + +Q_SIGNALS: + void fooChanged(); + +private: + int m_foo = 0; +}; + +class DynamicMetaSingleton : public DynamicMeta +{ + Q_OBJECT + QML_ELEMENT + QML_SINGLETON + Q_PROPERTY(DynamicMetaSingleton *itself READ itself CONSTANT FINAL) +public: + DynamicMetaSingleton(QObject *parent = nullptr) : DynamicMeta(parent) + { + QObjectPrivate *d = QObjectPrivate::get(this); + delete d->metaObject; + d->metaObject = new MetaObjectData<DynamicMetaSingleton>; + } + + DynamicMetaSingleton *itself() { return this; } +}; + +#endif // DYNAMICMETA_H diff --git a/tests/auto/qml/qmlcppcodegen/data/fallbacklookups.qml b/tests/auto/qml/qmlcppcodegen/data/fallbacklookups.qml new file mode 100644 index 0000000000..4b58cd344d --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/fallbacklookups.qml @@ -0,0 +1,34 @@ +import TestTypes +import QtQml + +DynamicMeta { + id: self + + function getSingleton(): QtObject { + return DynamicMetaSingleton.itself + } + + function withContext(): int { + foo = 93; + objectName = "aa" + foo; + return bar(4); + } + + function withId(): int { + self.foo = 94; + self.objectName = "bb" + foo; + return self.bar(5); + } + + function withSingleton(): int { + DynamicMetaSingleton.foo = 95; + DynamicMetaSingleton.objectName = "cc" + DynamicMetaSingleton.foo; + return DynamicMetaSingleton.bar(6); + } + + function withProperty(): int { + DynamicMetaSingleton.itself.foo = 96; + DynamicMetaSingleton.itself.objectName = "dd" + DynamicMetaSingleton.itself.foo; + return DynamicMetaSingleton.itself.bar(7); + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/functionLookup.qml b/tests/auto/qml/qmlcppcodegen/data/functionLookup.qml new file mode 100644 index 0000000000..211f524088 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/functionLookup.qml @@ -0,0 +1,6 @@ +import QtQml + +QtObject { + function foo() { return "a" + 99 } + property var bar: foo +} 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/infinities.qml b/tests/auto/qml/qmlcppcodegen/data/infinities.qml new file mode 100644 index 0000000000..5813d7a3ac --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/infinities.qml @@ -0,0 +1,9 @@ +import QtQml + +QtObject { + property real positiveInfinity: Infinity + property real negativeInfinity: -Infinity + property real positiveZero: 0.0 + property real negativeZero: -0.0 + property real naN: NaN +} diff --git a/tests/auto/qml/qmlcppcodegen/data/isnan.qml b/tests/auto/qml/qmlcppcodegen/data/isnan.qml new file mode 100644 index 0000000000..dfc64e8002 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/isnan.qml @@ -0,0 +1,9 @@ +import QtQml + +QtObject { + property real good: 10.1 + property real bad: "f" / 10 + + property bool a: isNaN(good) + property bool b: isNaN(bad) +} diff --git a/tests/auto/qml/qmlcppcodegen/data/moveRegVoid.qml b/tests/auto/qml/qmlcppcodegen/data/moveRegVoid.qml new file mode 100644 index 0000000000..f1e78babba --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/moveRegVoid.qml @@ -0,0 +1,20 @@ +import QtQuick + +Rectangle { + id: root + property bool translucency: false + + gradient: Gradient { + id: grad + } + + onTranslucencyChanged: { + if (translucency) { + root.color = "transparent"; + root.gradient = null; + } else { + root.color = "white"; + root.gradient = grad; + } + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/objectInVar.qml b/tests/auto/qml/qmlcppcodegen/data/objectInVar.qml new file mode 100644 index 0000000000..9177ff2089 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/objectInVar.qml @@ -0,0 +1,15 @@ +pragma Strict +import QtQml + +QtObject { + id: self + + property var thing: self + + function doThing() : bool { + if (self.thing) + return true; + else + return false; + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/typedArray.qml b/tests/auto/qml/qmlcppcodegen/data/typedArray.qml new file mode 100644 index 0000000000..0072357ae3 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/typedArray.qml @@ -0,0 +1,7 @@ +pragma Strict +import QtQml + +QtObject { + property list<bool> values1: [] + property list<int> values2: [] +} diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index 99376491fd..74794eb7fc 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -23,6 +23,8 @@ #include <data/cppbaseclass.h> #include <data/objectwithmethod.h> +#include <QtQml/private/qqmlengine_p.h> + #include <QtTest> #include <QtQml> #include <QtGui/qcolor.h> @@ -117,6 +119,13 @@ private slots: void revisions(); void invisibleBase(); void notEqualsInt(); + void infinities(); + void blockComments(); + void functionLookup(); + void objectInVar(); + void functionTakingVar(); + void testIsnan(); + void fallbackLookups(); }; void tst_QmlCppCodegen::simpleBinding() @@ -646,6 +655,8 @@ void tst_QmlCppCodegen::interestingFiles_data() QTest::addRow("dynamicscene") << u"dynamicscene.qml"_qs << true; QTest::addRow("curlygrouped") << u"curlygrouped.qml"_qs << true; QTest::addRow("cycleHead") << u"cycleHead.qml"_qs << false; + QTest::addRow("deadStoreLoop") << u"deadStoreLoop.qml"_qs << true; + QTest::addRow("moveRegVoid") << u"moveRegVoid.qml"_qs << true; } void tst_QmlCppCodegen::interestingFiles() @@ -1753,6 +1764,146 @@ void tst_QmlCppCodegen::notEqualsInt() QCOMPARE(t->property("text").toString(), u"Bar"_qs); } +void tst_QmlCppCodegen::infinities() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/infinities.qml"_qs)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + + QCOMPARE(o->property("positiveInfinity").toDouble(), std::numeric_limits<double>::infinity()); + QCOMPARE(o->property("negativeInfinity").toDouble(), -std::numeric_limits<double>::infinity()); + + const double positiveZero = o->property("positiveZero").toDouble(); + QCOMPARE(positiveZero, 0.0); + QVERIFY(!std::signbit(positiveZero)); + + const double negativeZero = o->property("negativeZero").toDouble(); + QCOMPARE(negativeZero, -0.0); + QVERIFY(std::signbit(negativeZero)); + + QVERIFY(qIsNaN(o->property("naN").toDouble())); +} + +void tst_QmlCppCodegen::blockComments() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/blockComments.qml"_qs)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(o->property("implicitHeight").toDouble(), 8.0); +} + +void tst_QmlCppCodegen::functionLookup() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/functionLookup.qml"_qs)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + const QVariant foo = o->property("bar"); + QCOMPARE(foo.metaType(), QMetaType::fromType<QJSValue>()); + const QJSManagedValue method(engine.toScriptValue(foo), &engine); + QVERIFY(method.isFunction()); + const QJSValue result = method.call(); + QVERIFY(result.isString()); + QCOMPARE(result.toString(), QStringLiteral("a99")); +} + +void tst_QmlCppCodegen::objectInVar() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/objectInVar.qml"_qs)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(qvariant_cast<QObject*>(o->property("thing")), o.data()); + + bool result = false; + QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result))); + QVERIFY(result); + + o->setProperty("thing", QVariant::fromValue<std::nullptr_t>(nullptr)); + QVERIFY(QMetaObject::invokeMethod(o.data(), "doThing", Q_RETURN_ARG(bool, result))); + QVERIFY(!result); +} + +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::testIsnan() +{ + QQmlEngine engine; + const QUrl document(u"qrc:/TestTypes/isnan.qml"_qs); + QQmlComponent c(&engine, document); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + + QCOMPARE(o->property("good").toDouble(), 10.1); + QVERIFY(qIsNaN(o->property("bad").toDouble())); + + const QVariant a = o->property("a"); + QCOMPARE(a.metaType(), QMetaType::fromType<bool>()); + QVERIFY(!a.toBool()); + + const QVariant b = o->property("b"); + QCOMPARE(b.metaType(), QMetaType::fromType<bool>()); + QVERIFY(b.toBool()); +} + +void tst_QmlCppCodegen::fallbackLookups() +{ + QQmlEngine engine; + const QUrl document(u"qrc:/TestTypes/fallbacklookups.qml"_qs); + QQmlComponent c(&engine, document); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + + QCOMPARE(o->objectName(), QString()); + int result = 0; + + QMetaObject::invokeMethod(o.data(), "withContext", Q_RETURN_ARG(int, result)); + QCOMPARE(result, 16); + QCOMPARE(o->objectName(), QStringLiteral("aa93")); + + QMetaObject::invokeMethod(o.data(), "withId", Q_RETURN_ARG(int, result)); + QCOMPARE(result, 17); + QCOMPARE(o->objectName(), QStringLiteral("bb94")); + + QObject *singleton = nullptr; + QMetaObject::invokeMethod(o.data(), "getSingleton", Q_RETURN_ARG(QObject*, singleton)); + QVERIFY(singleton); + + QMetaObject::invokeMethod(o.data(), "withSingleton", Q_RETURN_ARG(int, result)); + QCOMPARE(result, 18); + QCOMPARE(singleton->objectName(), QStringLiteral("cc95")); + + QMetaObject::invokeMethod(o.data(), "withProperty", Q_RETURN_ARG(int, result)); + QCOMPARE(result, 19); + QCOMPARE(singleton->objectName(), QStringLiteral("dd96")); +} + void tst_QmlCppCodegen::runInterpreted() { if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER")) diff --git a/tests/auto/qml/qmldiskcache/dummy_imports.qml b/tests/auto/qml/qmldiskcache/dummy_imports.qml index b9a196e188..63d33cbea6 100644 --- a/tests/auto/qml/qmldiskcache/dummy_imports.qml +++ b/tests/auto/qml/qmldiskcache/dummy_imports.qml @@ -1,8 +1,7 @@ // This file exists for the sole purpose for qmlimportscanner to find // which modules it needs to extract for deployment. -// Otherwise, it fails to find the imports that are expressed in C++ -// code in tst_parserstress.cpp +// Otherwise, it fails to find the imports that are expressed in C++. -import QtQuick 2.0 +import QtQuick -QtObject { } // This is needed in order to keep importscanner happy +QtObject { } diff --git a/tests/auto/qml/qmlformat/BLACKLIST b/tests/auto/qml/qmlformat/BLACKLIST index 4f2b3f7272..8b904fd665 100644 --- a/tests/auto/qml/qmlformat/BLACKLIST +++ b/tests/auto/qml/qmlformat/BLACKLIST @@ -1,3 +1,3 @@ -# QTBUG-09404 +# QTBUG-98494 [testExample] -* +windows-10 gcc diff --git a/tests/auto/qml/qmllint/data/Things/qmldir b/tests/auto/qml/qmllint/data/Things/qmldir index 8902de116b..e1d493a480 100644 --- a/tests/auto/qml/qmllint/data/Things/qmldir +++ b/tests/auto/qml/qmllint/data/Things/qmldir @@ -3,4 +3,4 @@ Something 1.0 SomethingElse.qml typeinfo plugins.qmltypes depends QtQuick 2.0 import QtQml 2.0 -optional import QtQuick.LocalStorage auto +default import QtQuick.LocalStorage auto diff --git a/tests/auto/qml/qmllint/data/callJSValueProp.qml b/tests/auto/qml/qmllint/data/callJSValueProp.qml new file mode 100644 index 0000000000..357c810383 --- /dev/null +++ b/tests/auto/qml/qmllint/data/callJSValueProp.qml @@ -0,0 +1,6 @@ +import QtQml + +QtObject { + function foo() {} + Component.onCompleted: Qt.callLater(foo); +} diff --git a/tests/auto/qml/qmllint/data/callVarProp.qml b/tests/auto/qml/qmllint/data/callVarProp.qml new file mode 100644 index 0000000000..ad69e2a679 --- /dev/null +++ b/tests/auto/qml/qmllint/data/callVarProp.qml @@ -0,0 +1,7 @@ +import QtQml + +QtObject { + property var foo: () => {} + + Component.onCompleted: foo() +} diff --git a/tests/auto/qml/qmllint/data/optionalImport.qml b/tests/auto/qml/qmllint/data/defaultImport.qml index 2be7cd444a..2be7cd444a 100644 --- a/tests/auto/qml/qmllint/data/optionalImport.qml +++ b/tests/auto/qml/qmllint/data/defaultImport.qml diff --git a/tests/auto/qml/qmllint/data/incompleteQmltypes3.qml b/tests/auto/qml/qmllint/data/incompleteQmltypes3.qml new file mode 100644 index 0000000000..142134d49e --- /dev/null +++ b/tests/auto/qml/qmllint/data/incompleteQmltypes3.qml @@ -0,0 +1,6 @@ +import Things 1.0 + +SomethingEntirelyStrange { + id: self + property var a: palette +} diff --git a/tests/auto/qml/qmllint/data/multilineStringTortureQuote.qml b/tests/auto/qml/qmllint/data/multilineStringTortureQuote.qml new file mode 100644 index 0000000000..ca8924f3f3 --- /dev/null +++ b/tests/auto/qml/qmllint/data/multilineStringTortureQuote.qml @@ -0,0 +1,10 @@ +import QtQml + +QtObject { + property string quote: " + quote: \" \\\" \\\\\" + ticks: ` \` \\\` \\\` + singleTicks: ' \' \\' \\\' + expression: \${expr} \${expr} \\\${expr} \\\${expr} + " +} diff --git a/tests/auto/qml/qmllint/data/multilineStringTortureTick.qml b/tests/auto/qml/qmllint/data/multilineStringTortureTick.qml new file mode 100644 index 0000000000..40484603d0 --- /dev/null +++ b/tests/auto/qml/qmllint/data/multilineStringTortureTick.qml @@ -0,0 +1,10 @@ +import QtQml + +QtObject { + property string quote: ' + quote: " \" \\" \\\" + ticks: \` \` \\\` \\\` + singleTicks: \' \\\' \\\\\' + expression: \${expr} \${expr} \\\${expr} \\\${expr} + ' +} diff --git a/tests/auto/qml/qmllint/data/qmodelIndex.qml b/tests/auto/qml/qmllint/data/qmodelIndex.qml new file mode 100644 index 0000000000..e0df84b68b --- /dev/null +++ b/tests/auto/qml/qmllint/data/qmodelIndex.qml @@ -0,0 +1,6 @@ +import QtQml + +QtObject { + property ItemSelectionModel itemSelectionModel; + function row() { return itemSelectionModel.currentIndex.row; } +} diff --git a/tests/auto/qml/qmllint/data/shadowedMethod.qml b/tests/auto/qml/qmllint/data/shadowedMethod.qml new file mode 100644 index 0000000000..72f18aaec7 --- /dev/null +++ b/tests/auto/qml/qmllint/data/shadowedMethod.qml @@ -0,0 +1,8 @@ +import QtQuick + +QtObject { + function foo() {} + property bool foo: false + + Component.onCompleted: foo() +} diff --git a/tests/auto/qml/qmllint/data/shadowedSignal.qml b/tests/auto/qml/qmllint/data/shadowedSignal.qml new file mode 100644 index 0000000000..e4bb003495 --- /dev/null +++ b/tests/auto/qml/qmllint/data/shadowedSignal.qml @@ -0,0 +1,6 @@ +import QtQuick + +MouseArea { + id: mouseArea + Component.onCompleted: pressed() +} diff --git a/tests/auto/qml/qmllint/data/shadowedSignalWithId.qml b/tests/auto/qml/qmllint/data/shadowedSignalWithId.qml new file mode 100644 index 0000000000..ed7cc9f6c4 --- /dev/null +++ b/tests/auto/qml/qmllint/data/shadowedSignalWithId.qml @@ -0,0 +1,6 @@ +import QtQuick + +MouseArea { + id: mouseArea + Component.onCompleted: mouseArea.pressed() +} diff --git a/tests/auto/qml/qmllint/data/shadowedSlot.qml b/tests/auto/qml/qmllint/data/shadowedSlot.qml new file mode 100644 index 0000000000..cb09645746 --- /dev/null +++ b/tests/auto/qml/qmllint/data/shadowedSlot.qml @@ -0,0 +1,6 @@ +import QtQml + +ObjectModel { + property bool move: false + Component.onCompleted: move() +} diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index 2469228dc1..32065c0246 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -40,6 +40,28 @@ class TestQmllint: public QQmlDataTest public: TestQmllint(); + struct Message + { + QString text = QString(); + quint32 line = 0, column = 0; + QtMsgType severity = QtWarningMsg; + }; + + struct Result + { + enum Flag { ExitsNormally = 0x1, NoMessages = 0x2 }; + + Q_DECLARE_FLAGS(Flags, Flag) + + static Result clean() { return Result { {}, {}, {}, { NoMessages, ExitsNormally } }; } + + QList<Message> expectedMessages = {}; + QList<Message> badMessages = {}; + QList<Message> expectedReplacements = {}; + + Flags flags = {}; + }; + private Q_SLOTS: void initTestCase() override; @@ -96,20 +118,37 @@ private: QString runQmllint(const QString &fileToLint, std::function<void(QProcess &)> handleResult, const QStringList &extraArgs = QStringList(), bool ignoreSettings = true, - bool addImportDirs = true); + bool addImportDirs = true, bool absolutePath = true); QString runQmllint(const QString &fileToLint, bool shouldSucceed, const QStringList &extraArgs = QStringList(), bool ignoreSettings = true, - bool addImportDirs = true); + bool addImportDirs = true, bool absolutePath = true); void callQmllint(const QString &fileToLint, bool shouldSucceed, QJsonArray *warnings = nullptr, QStringList importDirs = {}, QStringList qmltypesFiles = {}, QStringList resources = {}, DefaultImportOption defaultImports = UseDefaultImports, QMap<QString, QQmlJSLogger::Option> *options = nullptr); - void searchWarnings(const QJsonArray &warnings, const QString &string, const QString &filename, + void searchWarnings(const QJsonArray &warnings, const QString &string, + QtMsgType type = QtWarningMsg, quint32 line = 0, quint32 column = 0, ContainOption shouldContain = StringContained, ReplacementOption searchReplacements = NoReplacementSearch); + template<typename ExpectedMessageFailureHandler, typename BadMessageFailureHandler> + void checkResult(const QJsonArray &warnings, const Result &result, + ExpectedMessageFailureHandler onExpectedMessageFailures, + BadMessageFailureHandler onBadMessageFailures); + + void checkResult(const QJsonArray &warnings, const Result &result) + { + checkResult( + warnings, result, [] {}, [] {}); + } + + void runTest(const QString &testFile, const Result &result, QStringList importDirs = {}, + QStringList qmltypesFiles = {}, QStringList resources = {}, + DefaultImportOption defaultImports = UseDefaultImports, + QMap<QString, QQmlJSLogger::Option> *options = nullptr); + QString m_qmllintPath; QString m_qmljsrootgenPath; QString m_qmltyperegistrarPath; @@ -118,6 +157,8 @@ private: QQmlJSLinter m_linter; }; +Q_DECLARE_METATYPE(TestQmllint::Result) + TestQmllint::TestQmllint() : QQmlDataTest(QT_QMLTEST_DATADIR), m_defaultImportPaths({ QLibraryInfo::path(QLibraryInfo::QmlImportsPath), dataDirectory() }), @@ -184,14 +225,14 @@ void TestQmllint::testUnqualified_data() // access injected name from signal QTest::newRow("SignalHandler1") << QStringLiteral("SignalHandler.qml") - << QStringLiteral("onDoubleClicked: function(mouse) {") << 5 << 21; + << QStringLiteral("onDoubleClicked: function(mouse) {") << 5 << 21; QTest::newRow("SignalHandler2") << QStringLiteral("SignalHandler.qml") - << QStringLiteral("onPositionChanged: function(mouse) {") << 10 << 21; + << QStringLiteral("onPositionChanged: function(mouse) {") << 10 << 21; QTest::newRow("SignalHandlerShort1") << QStringLiteral("SignalHandler.qml") - << QStringLiteral("onClicked: (mouse) => ") << 8 << 29; + << QStringLiteral("onClicked: (mouse) => ") << 8 << 29; QTest::newRow("SignalHandlerShort2") - << QStringLiteral("SignalHandler.qml") << QStringLiteral("onPressAndHold: (mouse) => ") + << QStringLiteral("SignalHandler.qml") << QStringLiteral("onPressAndHold: (mouse) => ") << 12 << 34; // access catch identifier outside catch block QTest::newRow("CatchStatement") << QStringLiteral("CatchStatement.qml") << QStringLiteral("err") << 6 << 21; @@ -205,56 +246,38 @@ void TestQmllint::testUnqualified_data() void TestQmllint::testUnknownCausesFail() { - { - QJsonArray warnings; - callQmllint("unknownElement.qml", false, &warnings); - searchWarnings( - warnings, - QStringLiteral( - "Warning: %1:4:5: Unknown was not found. Did you add all import paths?") - .arg(testFile("unknownElement.qml")), - "unknownElement.qml"); - } - { - QJsonArray warnings; - callQmllint("TypeWithUnknownPropertyType.qml", false, &warnings); - searchWarnings( - warnings, - QStringLiteral( - "Warning: %1:4:5: Something was not found. Did you add all import paths?") - .arg(testFile("TypeWithUnknownPropertyType.qml")), - "TypeWithUnknownPropertyType.qml"); - } + runTest("unknownElement.qml", + Result { { Message { + QStringLiteral("Unknown was not found. Did you add all import paths?"), 4, 5, + QtWarningMsg } } }); + runTest("TypeWithUnknownPropertyType.qml", + Result { { Message { + QStringLiteral("Something was not found. Did you add all import paths?"), 4, 5, + QtWarningMsg } } }); } void TestQmllint::directoryPassedAsQmlTypesFile() { - QJsonArray warnings; - callQmllint("unknownElement.qml", false, &warnings, {}, { dataDirectory() }); - searchWarnings(warnings, - QStringLiteral("QML types file cannot be a directory: ") + dataDirectory(), - "unknownElement.qml"); + runTest("unknownElement.qml", + Result { { Message { QStringLiteral("QML types file cannot be a directory: ") + + dataDirectory() } } }, + {}, { dataDirectory() }); } void TestQmllint::oldQmltypes() { - QJsonArray warnings; - callQmllint("oldQmltypes.qml", false, &warnings); - searchWarnings(warnings, QStringLiteral("typeinfo not declared in qmldir file"), - "oldQmltypes.qml"); - searchWarnings(warnings, - QStringLiteral("QQuickItem was not found. Did you add all import paths?"), - "oldQmltypes.qml", StringNotContained); - searchWarnings(warnings, QStringLiteral("Found deprecated dependency specifications"), - "oldQmltypes.qml"); - - // Checking for both lines separately so that we don't have to mess with the line endings.b - searchWarnings(warnings, - QStringLiteral("Meta object revision and export version differ."), - "oldQmltypes.qml"); - searchWarnings(warnings, - QStringLiteral("Revision 0 corresponds to version 0.0; it should be 1.0."), - "oldQmltypes.qml"); + runTest("oldQmltypes.qml", + Result { { + Message { QStringLiteral("typeinfo not declared in qmldir file") }, + Message { + QStringLiteral("Found deprecated dependency specifications") }, + Message { QStringLiteral( + "Meta object revision and export version differ.") }, + Message { QStringLiteral( + "Revision 0 corresponds to version 0.0; it should be 1.0.") }, + }, + { Message { QStringLiteral( + "QQuickItem was not found. Did you add all import paths?") } } }); } void TestQmllint::qmltypes_data() @@ -366,509 +389,595 @@ void TestQmllint::resources() void TestQmllint::dirtyQmlCode_data() { QTest::addColumn<QString>("filename"); - QTest::addColumn<QString>("warningMessage"); - QTest::addColumn<QString>("notContained"); - QTest::addColumn<QString>("replacement"); - QTest::addColumn<bool>("exitsNormally"); + QTest::addColumn<Result>("result"); QTest::newRow("Invalid_syntax_QML") - << QStringLiteral("failure1.qml") << QStringLiteral("%1:4:8: Expected token `:'") - << QString() << QString() << false; - QTest::newRow("Invalid_syntax_JS") - << QStringLiteral("failure1.js") << QStringLiteral("%1:4:12: Expected token `;'") - << QString() << QString() << false; + << QStringLiteral("failure1.qml") + << Result { { Message { QStringLiteral("Expected token `:'"), 4, 8, QtCriticalMsg } } }; + QTest::newRow("Invalid_syntax_JS") << QStringLiteral("failure1.js") + << Result { { Message { QStringLiteral("Expected token `;'"), + 4, 12, QtCriticalMsg } } }; QTest::newRow("AutomatchedSignalHandler") << QStringLiteral("AutomatchedSignalHandler.qml") - << QString("Warning: %1:12:36: Unqualified access") << QString() << QString() << false; + << Result { { Message { QStringLiteral("Unqualified access"), 12, 36 } } }; QTest::newRow("AutomatchedSignalHandler2") << QStringLiteral("AutomatchedSignalHandler.qml") - << QString("Info: Implicitly defining onClicked as signal handler") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Implicitly defining onClicked as signal handler"), 0, 0, + QtInfoMsg } } }; QTest::newRow("MemberNotFound") << QStringLiteral("memberNotFound.qml") - << QString("Warning: %1:6:31: Property \"foo\" not found on type \"QtObject\"") - << QString() << QString() << false; + << Result { { Message { + QStringLiteral("Property \"foo\" not found on type \"QtObject\""), 6, + 31 } } }; QTest::newRow("UnknownJavascriptMethd") << QStringLiteral("unknownJavascriptMethod.qml") - << QString("Warning: %1:5:25: Property \"foo2\" not found on type \"Methods\"") - << QString() << QString() << false; - QTest::newRow("badAlias") << QStringLiteral("badAlias.qml") - << QString("Warning: %1:3:1: Cannot resolve alias \"wrong\"") - << QString() << QString() << false; - QTest::newRow("badAliasProperty1") << QStringLiteral("badAliasProperty.qml") - << QString("Warning: %1:3:1: Cannot resolve alias \"wrong\"") - << QString() << QString() << false; + << Result { { Message { + QStringLiteral("Property \"foo2\" not found on type \"Methods\""), 5, + 25 } } }; + QTest::newRow("badAlias") + << QStringLiteral("badAlias.qml") + << Result { { Message { QStringLiteral("Cannot resolve alias \"wrong\""), 3, 1 } } }; + QTest::newRow("badAliasProperty1") + << QStringLiteral("badAliasProperty.qml") + << Result { { Message { QStringLiteral("Cannot resolve alias \"wrong\""), 3, 1 } } }; QTest::newRow("badAliasExpression") << QStringLiteral("badAliasExpression.qml") - << QString("Warning: %1:5:26: Invalid alias expression. Only IDs and field member " - "expressions can be aliased") - << QString() << QString() << false; - QTest::newRow("aliasCycle1") << QStringLiteral( - "aliasCycle.qml") << QString("Warning: %1:3:1: Alias \"b\" is part of an alias cycle") - << QString() << QString() << false; - QTest::newRow("aliasCycle2") << QStringLiteral( - "aliasCycle.qml") << QString("Warning: %1:3:1: Alias \"a\" is part of an alias cycle") - << QString() << QString() << false; + << Result { { Message { + QStringLiteral("Invalid alias expression. Only IDs and field member " + "expressions can be aliased"), + 5, 26 } } }; + QTest::newRow("aliasCycle1") << QStringLiteral("aliasCycle.qml") + << Result { { Message { + QStringLiteral("Alias \"b\" is part of an alias cycle"), + 3, 1 } } }; + QTest::newRow("aliasCycle2") << QStringLiteral("aliasCycle.qml") + << Result { { Message { + QStringLiteral("Alias \"a\" is part of an alias cycle"), + 3, 1 } } }; QTest::newRow("badParent") << QStringLiteral("badParent.qml") - << QString("Warning: %1:5:34: Property \"rrr\" not found on type \"Item\"") << QString() - << QString() << false; + << Result { { Message { QStringLiteral("Property \"rrr\" not found on type \"Item\""), + 5, 34 } } }; QTest::newRow("parentIsComponent") << QStringLiteral("parentIsComponent.qml") - << QString("Warning: %1:7:39: Property \"progress\" not found on type \"QQuickItem\"") - << QString() << QString() << false; + << Result { { Message { + QStringLiteral("Property \"progress\" not found on type \"QQuickItem\""), 7, + 39 } } }; QTest::newRow("badTypeAssertion") << QStringLiteral("badTypeAssertion.qml") - << QString("Warning: %1:5:39: Property \"rrr\" not found on type \"QQuickItem\"") - << QString() << QString() << false; + << Result { { Message { + QStringLiteral("Property \"rrr\" not found on type \"QQuickItem\""), 5, + 39 } } }; QTest::newRow("incompleteQmltypes") << QStringLiteral("incompleteQmltypes.qml") - << QString("Warning: %1:5:26: Type \"QPalette\" of property \"palette\" not found") - << QString() << QString() << false; - QTest::newRow("incompleteQmltypes2") << QStringLiteral("incompleteQmltypes2.qml") - << QString("Warning: %1:5:35: Property \"weDontKnowIt\" " - "not found on type \"CustomPalette\"") - << QString() << QString() << false; - QTest::newRow("inheritanceCylce") << QStringLiteral("Cycle1.qml") - << QString("Warning: %1: Cycle2 is part of an inheritance " - "cycle: Cycle2 -> Cycle3 -> Cycle1 -> Cycle2") - << QString() << QString() << false; + << Result { { Message { + QStringLiteral("Type \"QPalette\" of property \"palette\" not found"), 5, + 26 } } }; + QTest::newRow("incompleteQmltypes2") + << QStringLiteral("incompleteQmltypes2.qml") + << Result { { Message { QStringLiteral("Property \"weDontKnowIt\" " + "not found on type \"CustomPalette\""), + 5, 35 } } }; + QTest::newRow("incompleteQmltypes3") + << QStringLiteral("incompleteQmltypes3.qml") + << Result { { Message { + QStringLiteral("Type \"QPalette\" of property \"palette\" not found"), 5, + 21 } } }; + QTest::newRow("inheritanceCycle") + << QStringLiteral("Cycle1.qml") + << Result { { Message { + QStringLiteral("Cycle2 is part of an inheritance cycle: Cycle2 -> Cycle3 " + "-> Cycle1 -> Cycle2"), + 2, 1 } } }; QTest::newRow("badQmldirImportAndDepend") << QStringLiteral("qmldirImportAndDepend/bad.qml") - << QString("Warning: %1:3:1: Item was not found. Did you add all import paths?") - << QString() << QString() << false; + << Result { { Message { + QStringLiteral("Item was not found. Did you add all import paths?"), 3, + 1 } } }; QTest::newRow("javascriptMethodsInModule") << QStringLiteral("javascriptMethodsInModuleBad.qml") - << QString("Warning: %1:5:21: Property \"unknownFunc\" not found on type \"Foo\"") - << QString() << QString() << false; - QTest::newRow("badEnumFromQtQml") << QStringLiteral("badEnumFromQtQml.qml") - << QString("Warning: %1:4:30: Property \"Linear123\" not " - "found on type \"QQmlEasingEnums\"") - << QString() << QString() << false; + << Result { { Message { + QStringLiteral("Property \"unknownFunc\" not found on type \"Foo\""), 5, + 21 } } }; + QTest::newRow("badEnumFromQtQml") + << QStringLiteral("badEnumFromQtQml.qml") + << Result { { Message { QStringLiteral("Property \"Linear123\" not " + "found on type \"QQmlEasingEnums\""), + 4, 30 } } }; QTest::newRow("anchors3") << QStringLiteral("anchors3.qml") - << QString("Cannot assign binding of type QQuickItem to QQuickAnchorLine") << QString() - << QString() << false; + << Result { { Message { QStringLiteral( + "Cannot assign binding of type QQuickItem to QQuickAnchorLine") } } }; QTest::newRow("nanchors1") << QStringLiteral("nanchors1.qml") - << QString("unknown grouped property scope nanchors.") << QString() - << QString() << false; + << Result { { Message { QStringLiteral( + "unknown grouped property scope nanchors.") } } }; QTest::newRow("nanchors2") << QStringLiteral("nanchors2.qml") - << QString("unknown grouped property scope nanchors.") << QString() - << QString() << false; + << Result { { Message { QStringLiteral( + "unknown grouped property scope nanchors.") } } }; QTest::newRow("nanchors3") << QStringLiteral("nanchors3.qml") - << QString("unknown grouped property scope nanchors.") << QString() - << QString() << false; - QTest::newRow("badAliasObject") << QStringLiteral("badAliasObject.qml") - << QString("Warning: %1:8:40: Property \"wrongwrongwrong\" not " - "found on type \"QtObject\"") - << QString() << QString() << false; - QTest::newRow("badScript") - << QStringLiteral("badScript.qml") - << QString("Warning: %1:5:21: Property \"stuff\" not found on type \"Empty\"") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "unknown grouped property scope nanchors.") } } }; + QTest::newRow("badAliasObject") + << QStringLiteral("badAliasObject.qml") + << Result { { Message { QStringLiteral("Property \"wrongwrongwrong\" not " + "found on type \"QtObject\""), + 8, 40 } } }; + QTest::newRow("badScript") << QStringLiteral("badScript.qml") + << Result { { Message { + QStringLiteral( + "Property \"stuff\" not found on type \"Empty\""), + 5, 21 } } }; QTest::newRow("badScriptOnAttachedProperty") << QStringLiteral("badScript.attached.qml") - << QString("Warning: %1:3:26: Unqualified access") << QString() << QString() << false; - QTest::newRow("brokenNamespace") << QStringLiteral("brokenNamespace.qml") - << QString("Warning: %1:4:19: Type not found in namespace") - << QString() << QString() << false; + << Result { { Message { QStringLiteral("Unqualified access"), 3, 26 } } }; + QTest::newRow("brokenNamespace") + << QStringLiteral("brokenNamespace.qml") + << Result { { Message { QStringLiteral("Type not found in namespace"), 4, 19 } } }; QTest::newRow("segFault (bad)") << QStringLiteral("SegFault.bad.qml") - << QStringLiteral("Property \"foobar\" not found on type \"QQuickScreenAttached\"") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Property \"foobar\" not found on type \"QQuickScreenAttached\"") } } }; QTest::newRow("VariableUsedBeforeDeclaration") << QStringLiteral("useBeforeDeclaration.qml") - << QStringLiteral("%1:5:9: Variable \"argq\" is used here before its declaration. " - "The declaration is at 6:13.") - << QString() << QString() << false; + << Result { { Message { + QStringLiteral("Variable \"argq\" is used here before its declaration. " + "The declaration is at 6:13."), + 5, 9 } } }; QTest::newRow("SignalParameterMismatch") << QStringLiteral("namedSignalParameters.qml") - << QStringLiteral("Parameter 1 to signal handler for \"onSig\" is called \"argarg\". " - "The signal has a parameter of the same name in position 2.") - << QStringLiteral("onSig2") << QString() << false; + << Result { { Message { QStringLiteral( + "Parameter 1 to signal handler for \"onSig\" is called \"argarg\". " + "The signal has a parameter of the same name in position 2.") } }, + { Message { QStringLiteral("onSig2") } } }; QTest::newRow("TooManySignalParameters") << QStringLiteral("tooManySignalParameters.qml") - << QStringLiteral("Signal handler for \"onSig\" has more formal parameters " - "than the signal it handles.") - << QString() << QString() << false; + << Result { { Message { + QStringLiteral("Signal handler for \"onSig\" has more formal parameters " + "than the signal it handles.") } } }; QTest::newRow("OnAssignment") << QStringLiteral("onAssignment.qml") - << QStringLiteral("Property \"loops\" not found on type \"bool\"") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Property \"loops\" not found on type \"bool\"") } } }; QTest::newRow("BadAttached") << QStringLiteral("badAttached.qml") - << QStringLiteral("unknown attached property scope WrongAttached.") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "unknown attached property scope WrongAttached.") } } }; QTest::newRow("BadBinding") << QStringLiteral("badBinding.qml") - << QStringLiteral( + << Result { { Message { QStringLiteral( "Binding assigned to \"doesNotExist\", but no property " - "\"doesNotExist\" exists in the current element.") - << QString() << QString() << false; + "\"doesNotExist\" exists in the current element.") } } }; QTest::newRow("bad template literal (simple)") << QStringLiteral("badTemplateStringSimple.qml") - << QStringLiteral("Cannot assign binding of type string to int") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Cannot assign binding of type string to int") } } }; QTest::newRow("bad template literal (substitution)") << QStringLiteral("badTemplateStringSubstitution.qml") - << QStringLiteral("Cannot assign binding of type QString to int") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Cannot assign binding of type QString to int") } } }; QTest::newRow("bad constant number to string") << QStringLiteral("numberToStringProperty.qml") - << QStringLiteral("Cannot assign a numeric constant to a string property") << QString() - << QString() << false; + << Result { { Message { QStringLiteral( + "Cannot assign a numeric constant to a string property") } } }; QTest::newRow("bad unary minus to string") << QStringLiteral("unaryMinusToStringProperty.qml") - << QStringLiteral("Cannot assign a numeric constant to a string property") << QString() - << QString() << false; - QTest::newRow("bad tranlsation binding (qsTr)") - << QStringLiteral("bad_qsTr.qml") << QStringLiteral("") << QString() << QString() - << false; + << Result { { Message { QStringLiteral( + "Cannot assign a numeric constant to a string property") } } }; + QTest::newRow("bad tranlsation binding (qsTr)") << QStringLiteral("bad_qsTr.qml") << Result {}; QTest::newRow("bad string binding (QT_TR_NOOP)") << QStringLiteral("bad_QT_TR_NOOP.qml") - << QStringLiteral("Cannot assign binding of type string to int") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Cannot assign binding of type string to int") } } }; QTest::newRow("BadScriptBindingOnGroup") << QStringLiteral("badScriptBinding.group.qml") - << QStringLiteral("Warning: %1:3:10: Binding assigned to \"bogusProperty\", but no " - "property \"bogusProperty\" exists in the current element.") - << QString() << QString() << false; + << Result { { Message { + QStringLiteral("Binding assigned to \"bogusProperty\", but no " + "property \"bogusProperty\" exists in the current element."), + 3, 10 } } }; QTest::newRow("BadScriptBindingOnAttachedType") << QStringLiteral("badScriptBinding.attached.qml") - << QStringLiteral("Warning: %1:5:12: Binding assigned to \"bogusProperty\", but no " - "property \"bogusProperty\" exists in the current element.") - << QString() << QString() << false; + << Result { { Message { + QStringLiteral("Binding assigned to \"bogusProperty\", but no " + "property \"bogusProperty\" exists in the current element."), + 5, 12 } } }; QTest::newRow("BadScriptBindingOnAttachedSignalHandler") << QStringLiteral("badScriptBinding.attachedSignalHandler.qml") - << QStringLiteral( - "Warning: %1:3:10: no matching signal found for handler \"onBogusSignal\"") - << QString() << QString() << false; + << Result { { Message { + QStringLiteral("no matching signal found for handler \"onBogusSignal\""), 3, + 10 } } }; QTest::newRow("BadPropertyType") << QStringLiteral("badPropertyType.qml") - << QStringLiteral("No type found for property \"bad\". This may be due to a missing " - "import statement or incomplete qmltypes files.") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "No type found for property \"bad\". This may be due to a missing " + "import statement or incomplete qmltypes files.") } } }; QTest::newRow("Deprecation (Property, with reason)") << QStringLiteral("deprecatedPropertyReason.qml") - << QStringLiteral("Property \"deprecated\" is deprecated (Reason: Test)") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Property \"deprecated\" is deprecated (Reason: Test)") } } }; QTest::newRow("Deprecation (Property, no reason)") << QStringLiteral("deprecatedProperty.qml") - << QStringLiteral("Property \"deprecated\" is deprecated") << QString() << QString() - << false; + << Result { { Message { QStringLiteral("Property \"deprecated\" is deprecated") } } }; QTest::newRow("Deprecation (Property binding, with reason)") << QStringLiteral("deprecatedPropertyBindingReason.qml") - << QStringLiteral("Binding on deprecated property \"deprecatedReason\" (Reason: Test)") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Binding on deprecated property \"deprecatedReason\" (Reason: Test)") } } }; QTest::newRow("Deprecation (Property binding, no reason)") << QStringLiteral("deprecatedPropertyBinding.qml") - << QStringLiteral("Binding on deprecated property \"deprecated\"") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Binding on deprecated property \"deprecated\"") } } }; QTest::newRow("Deprecation (Type, with reason)") << QStringLiteral("deprecatedTypeReason.qml") - << QStringLiteral("Type \"TypeDeprecatedReason\" is deprecated (Reason: Test)") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Type \"TypeDeprecatedReason\" is deprecated (Reason: Test)") } } }; QTest::newRow("Deprecation (Type, no reason)") << QStringLiteral("deprecatedType.qml") - << QStringLiteral("Type \"TypeDeprecated\" is deprecated") << QString() << QString() - << false; + << Result { { Message { QStringLiteral("Type \"TypeDeprecated\" is deprecated") } } }; QTest::newRow("MissingDefaultProperty") << QStringLiteral("defaultPropertyWithoutKeyword.qml") - << QStringLiteral("Cannot assign to non-existent default property") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Cannot assign to non-existent default property") } } }; QTest::newRow("MissingDefaultPropertyDefinedInTheSameType") << QStringLiteral("defaultPropertyWithinTheSameType.qml") - << QStringLiteral("Cannot assign to non-existent default property") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Cannot assign to non-existent default property") } } }; QTest::newRow("DoubleAssignToDefaultProperty") << QStringLiteral("defaultPropertyWithDoubleAssignment.qml") - << QStringLiteral("Cannot assign multiple objects to a default non-list property") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Cannot assign multiple objects to a default non-list property") } } }; QTest::newRow("DefaultPropertyWithWrongType(string)") << QStringLiteral("defaultPropertyWithWrongType.qml") - << QStringLiteral("Cannot assign to default property of incompatible type") - << QStringLiteral("Cannot assign to non-existent default property") << QString() - << false; + << Result { { Message { QStringLiteral( + "Cannot assign to default property of incompatible type") } }, + { Message { QStringLiteral( + "Cannot assign to non-existent default property") } } }; QTest::newRow("MultiDefaultPropertyWithWrongType") << QStringLiteral("multiDefaultPropertyWithWrongType.qml") - << QStringLiteral("Cannot assign to default property of incompatible type") - << QStringLiteral("Cannot assign to non-existent default property") << QString() - << false; + << Result { { Message { QStringLiteral( + "Cannot assign to default property of incompatible type") } }, + { Message { QStringLiteral( + "Cannot assign to non-existent default property") } } }; QTest::newRow("InvalidImport") << QStringLiteral("invalidImport.qml") - << QStringLiteral("Failed to import FooBar. Are your import paths set up properly?") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Failed to import FooBar. Are your import paths set up properly?") } } }; QTest::newRow("Unused Import (simple)") - << QStringLiteral("unused_simple.qml") << QStringLiteral("Unused import at %1:1:1") - << QString() << QString() << true; + << QStringLiteral("unused_simple.qml") + << Result { { Message { QStringLiteral("Unused import at"), 1, 1, QtInfoMsg } }, + {}, + {}, + Result::ExitsNormally }; QTest::newRow("Unused Import (prefix)") - << QStringLiteral("unused_prefix.qml") << QStringLiteral("Unused import at %1:1:1") - << QString() << QString() << true; - QTest::newRow("TypePropertAccess") << QStringLiteral("typePropertyAccess.qml") << QString() - << QString() << QString() << false; + << QStringLiteral("unused_prefix.qml") + << Result { { Message { QStringLiteral("Unused import at"), 1, 1, QtInfoMsg } }, + {}, + {}, + Result::ExitsNormally }; + QTest::newRow("TypePropertAccess") << QStringLiteral("typePropertyAccess.qml") << Result {}; QTest::newRow("badAttachedProperty") << QStringLiteral("badAttachedProperty.qml") - << QString("Property \"progress\" not found on type \"TestType\"") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Property \"progress\" not found on type \"TestType\"") } } }; QTest::newRow("badAttachedPropertyNested") << QStringLiteral("badAttachedPropertyNested.qml") - << QString("12:41: Property \"progress\" not found on type \"QObject\"") - << QString("6:37: Property \"progress\" not found on type \"QObject\"") << QString() - << false; + << Result { { Message { QStringLiteral( + "Property \"progress\" not found on type \"QObject\""), + 12, 41 } }, + { Message { QString("Property \"progress\" not found on type \"QObject\""), + 6, 37 } } }; QTest::newRow("badAttachedPropertyTypeString") << QStringLiteral("badAttachedPropertyTypeString.qml") - << QString("Cannot assign binding of type string to int") << QString() << QString() - << false; + << Result { { Message { + QStringLiteral("Cannot assign binding of type string to int") } } }; QTest::newRow("badAttachedPropertyTypeQtObject") << QStringLiteral("badAttachedPropertyTypeQtObject.qml") - << QString("Property \"count\" of type \"int\" is assigned an incompatible type " - "\"QtObject\"") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Property \"count\" of type \"int\" is assigned an incompatible type " + "\"QtObject\"") } } }; // should succeed, but it does not: - QTest::newRow("attachedPropertyAccess") << QStringLiteral("goodAttachedPropertyAccess.qml") - << QString() << QString() << QString() << true; + QTest::newRow("attachedPropertyAccess") + << QStringLiteral("goodAttachedPropertyAccess.qml") << Result::clean(); // should succeed, but it does not: - QTest::newRow("attachedPropertyNested") << QStringLiteral("goodAttachedPropertyNested.qml") - << QString() << QString() << QString() << true; + QTest::newRow("attachedPropertyNested") + << QStringLiteral("goodAttachedPropertyNested.qml") << Result::clean(); QTest::newRow("deprecatedFunction") << QStringLiteral("deprecatedFunction.qml") - << QStringLiteral("Method \"deprecated(foobar)\" is deprecated (Reason: No particular " - "reason.)") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Method \"deprecated(foobar)\" is deprecated (Reason: No particular " + "reason.)") } } }; QTest::newRow("deprecatedFunctionInherited") << QStringLiteral("deprecatedFunctionInherited.qml") - << QStringLiteral("Method \"deprecatedInherited(c, d)\" is deprecated (Reason: This " - "deprecation should be visible!)") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Method \"deprecatedInherited(c, d)\" is deprecated (Reason: This " + "deprecation should be visible!)") } } }; QTest::newRow("duplicated id") - << QStringLiteral("duplicateId.qml") - << QStringLiteral("Found a duplicated id. id root was first declared ") - << QString() << QString() << false; + << QStringLiteral("duplicateId.qml") + << Result { { Message { + QStringLiteral("Found a duplicated id. id root was first declared ") } } }; QTest::newRow("string as id") << QStringLiteral("stringAsId.qml") - << QStringLiteral("ids do not need quotation marks") << QString() - << QString() << false; - QTest::newRow("stringIdUsedInWarning") << QStringLiteral("stringIdUsedInWarning.qml") - << QStringLiteral("i is a member of a parent element") - << QString() << QStringLiteral("stringy.") << false; + << Result { { Message { QStringLiteral( + "ids do not need quotation marks") } } }; + QTest::newRow("stringIdUsedInWarning") + << QStringLiteral("stringIdUsedInWarning.qml") + << Result { { Message { + QStringLiteral("i is a member of a parent element"), + } }, + {}, + { Message { QStringLiteral("stringy.") } } }; QTest::newRow("Invalid id (expression)") - << QStringLiteral("invalidId1.qml") << QStringLiteral("Failed to parse id") << QString() - << QString() << false; + << QStringLiteral("invalidId1.qml") + << Result { { Message { QStringLiteral("Failed to parse id") } } }; QTest::newRow("multilineString") << QStringLiteral("multilineString.qml") - << QStringLiteral("String contains unescaped line terminator which is deprecated. Use " - "a template literal instead.") - << QString() << QString() << true; + << Result { { Message { QStringLiteral("String contains unescaped line terminator " + "which is deprecated."), + 0, 0, QtInfoMsg } }, + {}, + {}, + Result::ExitsNormally }; + QTest::newRow("multilineStringTortureQuote") + << QStringLiteral("multilineStringTortureQuote.qml") + << Result { { Message { QStringLiteral("String contains unescaped line terminator which is deprecated."), 0, 0, QtInfoMsg }}, {}, {Message {R"(` + quote: " \\" \\\\" + ticks: \` \` \\\` \\\` + singleTicks: ' \' \\' \\\' + expression: \${expr} \${expr} \\\${expr} \\\${expr} + `)"}}, Result::ExitsNormally }; + QTest::newRow("multilineStringTortureTick") + << QStringLiteral("multilineStringTortureTick.qml") + << Result { { Message { QStringLiteral("String contains unescaped line terminator which is deprecated."), 0, 0, QtInfoMsg } }, {}, {Message { + R"(` + quote: " \" \\" \\\" + ticks: \` \` \\\` \\\` + singleTicks: ' \\' \\\\' + expression: \${expr} \${expr} \\\${expr} \\\${expr} + `)" +}}, Result::ExitsNormally}; QTest::newRow("unresolvedType") << QStringLiteral("unresolvedType.qml") - << QStringLiteral("UnresolvedType was not found. Did you add all import paths?") - << QStringLiteral("incompatible type") << QString() << false; + << Result { { Message { QStringLiteral( + "UnresolvedType was not found. Did you add all import paths?") } }, + { Message { QStringLiteral("incompatible type") } } }; QTest::newRow("invalidInterceptor") << QStringLiteral("invalidInterceptor.qml") - << QStringLiteral("On-binding for property \"angle\" has wrong type \"Item\"") - << QString() << QString() << false; - QTest::newRow("2Interceptors") << QStringLiteral("2interceptors.qml") - << QStringLiteral("Duplicate interceptor on property \"x\"") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "On-binding for property \"angle\" has wrong type \"Item\"") } } }; + QTest::newRow("2Interceptors") + << QStringLiteral("2interceptors.qml") + << Result { { Message { QStringLiteral("Duplicate interceptor on property \"x\"") } } }; QTest::newRow("ValueSource+2Interceptors") << QStringLiteral("valueSourceBetween2interceptors.qml") - << QStringLiteral("Duplicate interceptor on property \"x\"") << QString() << QString() - << false; + << Result { { Message { QStringLiteral("Duplicate interceptor on property \"x\"") } } }; QTest::newRow("2ValueSources") << QStringLiteral("2valueSources.qml") - << QStringLiteral("Duplicate value source on property \"x\"") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Duplicate value source on property \"x\"") } } }; QTest::newRow("ValueSource+Value") << QStringLiteral("valueSource_Value.qml") - << QStringLiteral("Cannot combine value source and binding on property \"obj\"") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Cannot combine value source and binding on property \"obj\"") } } }; QTest::newRow("ValueSource+ListValue") << QStringLiteral("valueSource_listValue.qml") - << QStringLiteral("Cannot combine value source and binding on property \"objs\"") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Cannot combine value source and binding on property \"objs\"") } } }; QTest::newRow("NonExistentListProperty") << QStringLiteral("nonExistentListProperty.qml") - << QStringLiteral("Property \"objs\" is invalid or does not exist") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Property \"objs\" is invalid or does not exist") } } }; QTest::newRow("QtQuick.Window 2.0") << QStringLiteral("qtquickWindow20.qml") - << QStringLiteral("Property \"window\" not found on type \"QQuickWindow\"") << QString() - << QString() << false; + << Result { { Message { QStringLiteral( + "Property \"window\" not found on type \"QQuickWindow\"") } } }; QTest::newRow("unresolvedAttachedType") << QStringLiteral("unresolvedAttachedType.qml") - << QStringLiteral("unknown attached property scope UnresolvedAttachedType.") - << QStringLiteral("Property \"property\" is invalid or does not exist") << QString() - << false; + << Result { { Message { QStringLiteral( + "unknown attached property scope UnresolvedAttachedType.") } }, + { Message { QStringLiteral( + "Property \"property\" is invalid or does not exist") } } }; QTest::newRow("nestedInlineComponents") << QStringLiteral("nestedInlineComponents.qml") - << QStringLiteral("Nested inline components are not supported") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Nested inline components are not supported") } } }; QTest::newRow("WithStatement") << QStringLiteral("WithStatement.qml") - << QStringLiteral("with statements are strongly discouraged") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "with statements are strongly discouraged") } } }; QTest::newRow("BindingTypeMismatch") << QStringLiteral("bindingTypeMismatch.qml") - << QStringLiteral("Cannot assign binding of type QString to int") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Cannot assign binding of type QString to int") } } }; QTest::newRow("BindingTypeMismatchFunction") << QStringLiteral("bindingTypeMismatchFunction.qml") - << QStringLiteral("Cannot assign binding of type QString to int") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Cannot assign binding of type QString to int") } } }; QTest::newRow("BadLiteralBinding") << QStringLiteral("badLiteralBinding.qml") - << QStringLiteral("Cannot assign binding of type string to int") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Cannot assign binding of type string to int") } } }; QTest::newRow("BadLiteralBindingDate") << QStringLiteral("badLiteralBindingDate.qml") - << QStringLiteral("Cannot assign binding of type QString to QDateTime") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Cannot assign binding of type QString to QDateTime") } } }; QTest::newRow("BadModulePrefix") << QStringLiteral("badModulePrefix.qml") - << QStringLiteral("Cannot load singleton as property of object") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Cannot load singleton as property of object") } } }; QTest::newRow("BadModulePrefix2") << QStringLiteral("badModulePrefix2.qml") - << QStringLiteral("Cannot use non-reference type QRectF as base " - "of namespaced attached type") - << QString() << QString() << false; + << Result { { Message { QStringLiteral("Cannot use non-reference type QRectF as base " + "of namespaced attached type") } } }; QTest::newRow("AssignToReadOnlyProperty") << QStringLiteral("assignToReadOnlyProperty.qml") - << QStringLiteral("Cannot assign to read-only property activeFocus") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Cannot assign to read-only property activeFocus") } } }; QTest::newRow("AssignToReadOnlyProperty") << QStringLiteral("assignToReadOnlyProperty2.qml") - << QStringLiteral("Cannot assign to read-only property activeFocus") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Cannot assign to read-only property activeFocus") } } }; QTest::newRow("DeferredPropertyID") << QStringLiteral("deferredPropertyID.qml") - << QStringLiteral( + << Result { { Message { QStringLiteral( "Cannot defer property assignment to " "\"contentData\". Assigning an id to an object or one of its sub-objects " - "bound to a deferred property will make the assignment immediate.") - << QString() << QString() << false; + "bound to a deferred property will make the assignment immediate.") } } }; QTest::newRow("DeferredPropertyNestedID") << QStringLiteral("deferredPropertyNestedID.qml") - << QStringLiteral( + << Result { { Message { QStringLiteral( "Cannot defer property assignment to " "\"contentData\". Assigning an id to an object or one of its sub-objects " - "bound to a deferred property will make the assignment immediate.") - << QString() << QString() << false; + "bound to a deferred property will make the assignment immediate.") } } }; QTest::newRow("cachedDependency") - << QStringLiteral("cachedDependency.qml") << QStringLiteral("Unused import at %1:1:1") - << QStringLiteral("Cannot assign binding of type QQuickItem to QObject") << QString() - << true; + << QStringLiteral("cachedDependency.qml") + << Result { { Message { QStringLiteral("Unused import at"), 1, 1, QtInfoMsg } }, + { Message { QStringLiteral( + "Cannot assign binding of type QQuickItem to QObject") } }, + {}, + Result::ExitsNormally }; QTest::newRow("cycle in import") << QStringLiteral("cycleHead.qml") - << QStringLiteral("MenuItem is part of an inheritance cycle: MenuItem -> MenuItem") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "MenuItem is part of an inheritance cycle: MenuItem -> MenuItem") } } }; QTest::newRow("badGeneralizedGroup1") << QStringLiteral("badGeneralizedGroup1.qml") - << QStringLiteral("Binding assigned to \"aaaa\", " - "but no property \"aaaa\" exists in the current element") - << QString() << QString() << false; - QTest::newRow("badGeneralizedGroup2") << QStringLiteral("badGeneralizedGroup2.qml") - << QStringLiteral("unknown grouped property scope aself") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Binding assigned to \"aaaa\", " + "but no property \"aaaa\" exists in the current element") } } }; + QTest::newRow("badGeneralizedGroup2") + << QStringLiteral("badGeneralizedGroup2.qml") + << Result { { Message { QStringLiteral("unknown grouped property scope aself") } } }; QTest::newRow("missingQmltypes") << QStringLiteral("missingQmltypes.qml") - << QStringLiteral("QML types file does not exist") << QString() << QString() << false; - QTest::newRow("enumInvalid") << QStringLiteral( - "enumInvalid.qml") << QStringLiteral("Property \"red\" not found on type \"QtObject\"") - << QString() << QString() << false; + << Result { { Message { QStringLiteral("QML types file does not exist") } } }; + QTest::newRow("enumInvalid") + << QStringLiteral("enumInvalid.qml") + << Result { { Message { + QStringLiteral("Property \"red\" not found on type \"QtObject\"") } } }; QTest::newRow("inaccessibleId") << QStringLiteral("inaccessibleId.qml") - << QStringLiteral("Property \"objectName\" not found on type \"int\"") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Property \"objectName\" not found on type \"int\"") } } }; QTest::newRow("inaccessibleId2") << QStringLiteral("inaccessibleId2.qml") - << QStringLiteral("Property \"objectName\" not found on type \"int\"") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Property \"objectName\" not found on type \"int\"") } } }; QTest::newRow("unknownTypeCustomParser") << QStringLiteral("unknownTypeCustomParser.qml") - << QStringLiteral("TypeDoesNotExist was not found.") << QString() << QString() << false; + << Result { { Message { QStringLiteral("TypeDoesNotExist was not found.") } } }; QTest::newRow("nonNullStored") << QStringLiteral("nonNullStored.qml") - << QStringLiteral("Property \"objectName\" not found on type \"Foozle\"") - << QStringLiteral("Unqualified access") - << QString() << false; + << Result { { Message { QStringLiteral( + "Property \"objectName\" not found on type \"Foozle\"") } }, + { Message { QStringLiteral("Unqualified access") } } }; QTest::newRow("cppPropertyChangeHandlers-wrong-parameters-size-bindable") << QStringLiteral("badCppPropertyChangeHandlers1.qml") - << QStringLiteral("Signal handler for \"onAChanged\" has more formal parameters than " - "the signal it handles") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Signal handler for \"onAChanged\" has more formal parameters than " + "the signal it handles") } } }; QTest::newRow("cppPropertyChangeHandlers-wrong-parameters-size-notify") << QStringLiteral("badCppPropertyChangeHandlers2.qml") - << QStringLiteral("Signal handler for \"onBChanged\" has more formal parameters than " - "the signal it handles") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Signal handler for \"onBChanged\" has more formal parameters than " + "the signal it handles") } } }; QTest::newRow("cppPropertyChangeHandlers-no-property") << QStringLiteral("badCppPropertyChangeHandlers3.qml") - << QStringLiteral("no matching signal found for handler \"onXChanged\"") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("no matching signal found for handler \"onXChanged\"") } } }; QTest::newRow("cppPropertyChangeHandlers-not-a-signal") << QStringLiteral("badCppPropertyChangeHandlers4.qml") - << QStringLiteral("no matching signal found for handler \"onWannabeSignal\"") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "no matching signal found for handler \"onWannabeSignal\"") } } }; QTest::newRow("didYouMean(binding)") << QStringLiteral("didYouMeanBinding.qml") - << QStringLiteral("Binding assigned to \"witdh\", but no property \"witdh\" exists in " - "the current element.") - << QString() << QStringLiteral("width") << false; + << Result { + { Message { QStringLiteral( + "Binding assigned to \"witdh\", but no property \"witdh\" exists in " + "the current element.") } }, + {}, + { Message { QStringLiteral("width") } } + }; QTest::newRow("didYouMean(unqualified)") - << QStringLiteral("didYouMeanUnqualified.qml") << QStringLiteral("Unqualified access") - << QString() << QStringLiteral("height") << false; + << QStringLiteral("didYouMeanUnqualified.qml") + << Result { { Message { QStringLiteral("Unqualified access") } }, + {}, + { Message { QStringLiteral("height") } } }; QTest::newRow("didYouMean(unqualifiedCall)") << QStringLiteral("didYouMeanUnqualifiedCall.qml") - << QStringLiteral("Unqualified access") << QString() << QStringLiteral("func") << false; + << Result { { Message { QStringLiteral("Unqualified access") } }, + {}, + { Message { QStringLiteral("func") } } }; QTest::newRow("didYouMean(property)") << QStringLiteral("didYouMeanProperty.qml") - << QStringLiteral("Property \"hoight\" not found on type \"Rectangle\"") << QString() - << QStringLiteral("height") << false; + << Result { { Message { QStringLiteral( + "Property \"hoight\" not found on type \"Rectangle\"") }, + {}, + { Message { QStringLiteral("height") } } } }; QTest::newRow("didYouMean(propertyCall)") << QStringLiteral("didYouMeanPropertyCall.qml") - << QStringLiteral("Property \"lgg\" not found on type \"Console\"") << QString() - << QStringLiteral("log") << false; + << Result { + { Message { QStringLiteral("Property \"lgg\" not found on type \"Console\"") }, + {}, + { Message { QStringLiteral("log") } } } + }; QTest::newRow("didYouMean(component)") << QStringLiteral("didYouMeanComponent.qml") - << QStringLiteral("Itym was not found. Did you add all import paths?") << QString() - << QStringLiteral("Item") << false; + << Result { { Message { QStringLiteral( + "Itym was not found. Did you add all import paths?") }, + {}, + { Message { QStringLiteral("Item") } } } }; QTest::newRow("nullBinding") << QStringLiteral("nullBinding.qml") - << QStringLiteral("Cannot assign binding of type $internal$.std::nullptr_t to double") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Cannot assign binding of type $internal$.std::nullptr_t to double") } } }; QTest::newRow("nullBindingFunction") << QStringLiteral("nullBindingFunction.qml") - << QStringLiteral("Cannot assign binding of type std::nullptr_t to double") << QString() - << QString() << false; + << Result { { Message { QStringLiteral( + "Cannot assign binding of type std::nullptr_t to double") } } }; QTest::newRow("missingRequiredAlias") << QStringLiteral("missingRequiredAlias.qml") - << QStringLiteral("Component is missing required property requiredAlias from " - "RequiredWithRootLevelAlias") - << QString() << QString() << false; + << Result { { Message { + QStringLiteral("Component is missing required property requiredAlias from " + "RequiredWithRootLevelAlias") } } }; QTest::newRow("missingSingletonPragma") << QStringLiteral("missingSingletonPragma.qml") - << QStringLiteral("Type MissingPragma declared as singleton in qmldir but missing " - "pragma Singleton") - << QString() << QString() << false; + << Result { { Message { QStringLiteral( + "Type MissingPragma declared as singleton in qmldir but missing " + "pragma Singleton") } } }; QTest::newRow("missingSingletonQmldir") << QStringLiteral("missingSingletonQmldir.qml") - << QStringLiteral( + << Result { { Message { QStringLiteral( "Type MissingQmldirSingleton not declared as singleton in qmldir but using " - "pragma Singleton") - << QString() << QString() << false; + "pragma Singleton") } } }; QTest::newRow("jsVarDeclarationsWriteConst") << QStringLiteral("jsVarDeclarationsWriteConst.qml") - << QStringLiteral("Cannot assign to read-only property constProp") << QString() - << QString() << false; + << Result { { Message { + QStringLiteral("Cannot assign to read-only property constProp") } } }; + QTest::newRow("shadowedSignal") + << QStringLiteral("shadowedSignal.qml") + << Result { { Message { + QStringLiteral("Signal \"pressed\" is shadowed by a property.") } } }; + QTest::newRow("shadowedSignalWithId") + << QStringLiteral("shadowedSignalWithId.qml") + << Result { { Message { + QStringLiteral("Signal \"pressed\" is shadowed by a property") } } }; + QTest::newRow("shadowedSlot") << QStringLiteral("shadowedSlot.qml") + << Result { { Message { QStringLiteral( + "Slot \"move\" is shadowed by a property") } } }; + QTest::newRow("shadowedMethod") << QStringLiteral("shadowedMethod.qml") + << Result { { Message { QStringLiteral( + "Method \"foo\" is shadowed by a property.") } } }; + QTest::newRow("callVarProp") + << QStringLiteral("callVarProp.qml") + << Result { { Message { QStringLiteral( + "Property \"foo\" is a variant property. It may or may not be a " + "method. Use a regular function instead.") } } }; + QTest::newRow("callJSValue") + << QStringLiteral("callJSValueProp.qml") + << Result { { Message { QStringLiteral( + "Property \"callLater\" is a QJSValue property. It may or may not be " + "a method. Use a regular Q_INVOKABLE instead.") } } }; } void TestQmllint::dirtyQmlCode() { QFETCH(QString, filename); - QFETCH(QString, warningMessage); - QFETCH(QString, notContained); - QFETCH(QString, replacement); - QFETCH(bool, exitsNormally); - - if (warningMessage.contains(QLatin1String("%1"))) - warningMessage = warningMessage.arg(testFile(filename)); + QFETCH(Result, result); QJsonArray warnings; @@ -881,28 +990,21 @@ void TestQmllint::dirtyQmlCode() QEXPECT_FAIL("bad tranlsation binding (qsTr)", "We currently do not check translation binding", Abort); - callQmllint(filename, exitsNormally, &warnings); - - if (!warningMessage.isEmpty()) { - // output.contains() expect fails: - QEXPECT_FAIL("BadLiteralBindingDate", - "We're currently not able to verify any non-trivial QString conversion that " - "requires QQmlStringConverters", - Abort); - - searchWarnings(warnings, warningMessage, filename); - } - - if (!notContained.isEmpty()) { - // !output.contains() expect fails: - QEXPECT_FAIL("badAttachedPropertyNested", "We cannot discern between types and instances", - Abort); - - searchWarnings(warnings, notContained, filename, StringNotContained); - } + callQmllint(filename, result.flags.testFlag(Result::ExitsNormally), &warnings); - if (!replacement.isEmpty()) - searchWarnings(warnings, replacement, filename, StringContained, DoReplacementSearch); + checkResult( + warnings, result, + [] { + QEXPECT_FAIL("BadLiteralBindingDate", + "We're currently not able to verify any non-trivial QString " + "conversion that " + "requires QQmlStringConverters", + Abort); + }, + [] { + QEXPECT_FAIL("badAttachedPropertyNested", + "We cannot discern between types and instances", Abort); + }); } void TestQmllint::cleanQmlCode_data() @@ -938,7 +1040,7 @@ void TestQmllint::cleanQmlCode_data() QTest::newRow("enumFromQtQml") << QStringLiteral("enumFromQtQml.qml"); QTest::newRow("anchors1") << QStringLiteral("anchors1.qml"); QTest::newRow("anchors2") << QStringLiteral("anchors2.qml"); - QTest::newRow("optionalImport") << QStringLiteral("optionalImport.qml"); + QTest::newRow("defaultImport") << QStringLiteral("defaultImport.qml"); QTest::newRow("goodAliasObject") << QStringLiteral("goodAliasObject.qml"); QTest::newRow("jsmoduleimport") << QStringLiteral("jsmoduleimport.qml"); QTest::newRow("overridescript") << QStringLiteral("overridescript.qml"); @@ -1021,6 +1123,7 @@ void TestQmllint::cleanQmlCode_data() QTest::newRow("unexportedCppBase") << QStringLiteral("unexportedCppBase.qml"); QTest::newRow("requiredWithRootLevelAlias") << QStringLiteral("RequiredWithRootLevelAlias.qml"); QTest::newRow("jsVarDeclarations") << QStringLiteral("jsVarDeclarations.qml"); + QTest::newRow("qmodelIndex") << QStringLiteral("qmodelIndex.qml"); } void TestQmllint::cleanQmlCode() @@ -1029,45 +1132,49 @@ void TestQmllint::cleanQmlCode() QJsonArray warnings; - callQmllint(filename, true, &warnings); - QVERIFY2(warnings.isEmpty(), qPrintable(QJsonDocument(warnings).toJson())); + runTest(filename, Result::clean()); } void TestQmllint::compilerWarnings_data() { QTest::addColumn<QString>("filename"); - QTest::addColumn<bool>("shouldSucceed"); - QTest::addColumn<QString>("warning"); + QTest::addColumn<Result>("result"); QTest::addColumn<bool>("enableCompilerWarnings"); - QTest::newRow("listIndices") << QStringLiteral("listIndices.qml") << true << QString() << true; + QTest::newRow("listIndices") << QStringLiteral("listIndices.qml") << Result::clean() << true; QTest::newRow("lazyAndDirect") - << QStringLiteral("LazyAndDirect/Lazy.qml") << true << QString() << true; - QTest::newRow("qQmlV4Function") << QStringLiteral("varargs.qml") << true << QString() << true; - QTest::newRow("multiGrouped") << QStringLiteral("multiGrouped.qml") << true << QString() - << true; - - QTest::newRow("shadowable") << QStringLiteral("shadowable.qml") << false - << QStringLiteral("with type NotSoSimple can be shadowed") << true; - QTest::newRow("tooFewParameters") << QStringLiteral("tooFewParams.qml") << false - << QStringLiteral("No matching override found") << true; + << QStringLiteral("LazyAndDirect/Lazy.qml") << Result::clean() << true; + QTest::newRow("qQmlV4Function") << QStringLiteral("varargs.qml") << Result::clean() << true; + QTest::newRow("multiGrouped") << QStringLiteral("multiGrouped.qml") << Result::clean() << true; + + QTest::newRow("shadowable") << QStringLiteral("shadowable.qml") + << Result { { Message { QStringLiteral( + "with type NotSoSimple can be shadowed") } } } + << true; + QTest::newRow("tooFewParameters") + << QStringLiteral("tooFewParams.qml") + << Result { { Message { QStringLiteral("No matching override found") } } } << true; QTest::newRow("javascriptVariableArgs") - << QStringLiteral("javascriptVariableArgs.qml") << false - << QStringLiteral("Function expects 0 arguments, but 2 were provided") << true; + << QStringLiteral("javascriptVariableArgs.qml") + << Result { { Message { + QStringLiteral("Function expects 0 arguments, but 2 were provided") } } } + << true; QTest::newRow("unknownTypeInRegister") - << QStringLiteral("unknownTypeInRegister.qml") << false - << QStringLiteral("Functions without type annotations won't be compiled") << true; - QTest::newRow("pragmaStrict") << QStringLiteral("pragmaStrict.qml") << true - << QStringLiteral( - "Functions without type annotations won't be compiled") - << false; + << QStringLiteral("unknownTypeInRegister.qml") + << Result { { Message { + QStringLiteral("Functions without type annotations won't be compiled") } } } + << true; + QTest::newRow("pragmaStrict") + << QStringLiteral("pragmaStrict.qml") + << Result { { { QStringLiteral( + "Functions without type annotations won't be compiled") } } } + << true; } void TestQmllint::compilerWarnings() { QFETCH(QString, filename); - QFETCH(bool, shouldSucceed); - QFETCH(QString, warning); + QFETCH(Result, result); QFETCH(bool, enableCompilerWarnings); QJsonArray warnings; @@ -1077,49 +1184,48 @@ void TestQmllint::compilerWarnings() if (enableCompilerWarnings) options[u"compiler"_qs].setLevel(u"warning"_qs); - callQmllint(filename, shouldSucceed, &warnings, {}, {}, {}, UseDefaultImports, &options); - - if (!warning.isEmpty()) - searchWarnings(warnings, warning, filename); + runTest(filename, result, {}, {}, {}, UseDefaultImports, &options); } void TestQmllint::controlsSanity_data() { QTest::addColumn<QString>("filename"); - QTest::addColumn<QString>("warning"); - QTest::newRow("functionDeclarations") << QStringLiteral("functionDeclarations.qml") - << QStringLiteral("Declared function \"add\""); - QTest::newRow("signalHandlers") << QStringLiteral("signalHandlers.qml") - << QStringLiteral("Declared signal handler \"onCompleted\""); + QTest::addColumn<Result>("result"); + QTest::newRow("functionDeclarations") + << QStringLiteral("functionDeclarations.qml") + << Result { { Message { QStringLiteral("Declared function \"add\"") } } }; + QTest::newRow("signalHandlers") + << QStringLiteral("signalHandlers.qml") + << Result { { Message { QStringLiteral("Declared signal handler \"onCompleted\"") } } }; QTest::newRow("anchors") << QStringLiteral("anchors.qml") - << QStringLiteral("Using anchors here"); + << Result { { Message { QStringLiteral("Using anchors here") } } }; } void TestQmllint::controlsSanity() { QFETCH(QString, filename); - QFETCH(QString, warning); + QFETCH(Result, result); QJsonArray warnings; auto options = QQmlJSLogger::options(); options[u"controls-sanity"_qs].setLevel(u"warning"_qs); - callQmllint(filename, false, &warnings, {}, {}, {}, UseDefaultImports, &options); - - if (!warning.isEmpty()) - searchWarnings(warnings, warning, filename); + runTest(filename, result, {}, {}, {}, UseDefaultImports, &options); } QString TestQmllint::runQmllint(const QString &fileToLint, std::function<void(QProcess &)> handleResult, const QStringList &extraArgs, bool ignoreSettings, - bool addImportDirs) + bool addImportDirs, bool absolutePath) { auto qmlImportDir = QLibraryInfo::path(QLibraryInfo::QmlImportsPath); QStringList args; - args << (QFileInfo(fileToLint).isAbsolute() ? fileToLint : testFile(fileToLint)); + QString absoluteFilePath = + QFileInfo(fileToLint).isAbsolute() ? fileToLint : testFile(fileToLint); + + args << QFileInfo(absoluteFilePath).fileName(); if (addImportDirs) { args << QStringLiteral("-I") << qmlImportDir @@ -1127,13 +1233,17 @@ QString TestQmllint::runQmllint(const QString &fileToLint, } if (ignoreSettings) - QStringLiteral("--ignore-settings"); + args << QStringLiteral("--ignore-settings"); + + if (absolutePath) + args << QStringLiteral("--absolute-path"); args << extraArgs; args << QStringLiteral("--silent"); QString errors; auto verify = [&](bool isSilent) { QProcess process; + process.setWorkingDirectory(QFileInfo(absoluteFilePath).absolutePath()); process.start(m_qmllintPath, args); handleResult(process); errors = process.readAllStandardError(); @@ -1175,7 +1285,7 @@ QString TestQmllint::runQmllint(const QString &fileToLint, QString TestQmllint::runQmllint(const QString &fileToLint, bool shouldSucceed, const QStringList &extraArgs, bool ignoreSettings, - bool addImportDirs) + bool addImportDirs, bool absolutePath) { return runQmllint( fileToLint, @@ -1188,7 +1298,7 @@ QString TestQmllint::runQmllint(const QString &fileToLint, bool shouldSucceed, else QVERIFY(process.exitCode() != 0); }, - extraArgs, ignoreSettings, addImportDirs); + extraArgs, ignoreSettings, addImportDirs, absolutePath); } void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJsonArray *warnings, @@ -1214,44 +1324,112 @@ void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJs QCOMPARE(success, shouldSucceed); } +void TestQmllint::runTest(const QString &testFile, const Result &result, QStringList importDirs, + QStringList qmltypesFiles, QStringList resources, + DefaultImportOption defaultImports, + QMap<QString, QQmlJSLogger::Option> *options) +{ + QJsonArray warnings; + callQmllint(testFile, result.flags.testFlag(Result::Flag::ExitsNormally), &warnings, importDirs, + qmltypesFiles, resources, defaultImports, options); + checkResult(warnings, result); +} + +template<typename ExpectedMessageFailureHandler, typename BadMessageFailureHandler> +void TestQmllint::checkResult(const QJsonArray &warnings, const Result &result, + ExpectedMessageFailureHandler onExpectedMessageFailures, + BadMessageFailureHandler onBadMessageFailures) +{ + if (result.flags.testFlag(Result::Flag::NoMessages)) + QVERIFY2(warnings.isEmpty(), qPrintable(QJsonDocument(warnings).toJson())); + + for (const Message &msg : result.expectedMessages) { + // output.contains() expect fails: + onExpectedMessageFailures(); + + searchWarnings(warnings, msg.text, msg.severity, msg.line, msg.column); + } + + for (const Message &msg : result.badMessages) { + // !output.contains() expect fails: + onBadMessageFailures(); + + searchWarnings(warnings, msg.text, msg.severity, msg.line, msg.column, StringNotContained); + } + + for (const Message &replacement : result.expectedReplacements) { + searchWarnings(warnings, replacement.text, replacement.severity, replacement.line, + replacement.column, StringContained, DoReplacementSearch); + } +} + void TestQmllint::searchWarnings(const QJsonArray &warnings, const QString &substring, - const QString &filename, ContainOption shouldContain, - ReplacementOption searchReplacements) + QtMsgType type, quint32 line, quint32 column, + ContainOption shouldContain, ReplacementOption searchReplacements) { bool contains = false; - auto toDisplayWarningType = [](const QString &warningType) { - return warningType.first(1).toUpper() + warningType.mid(1); + auto typeStringToMsgType = [](const QString &type) -> QtMsgType { + if (type == u"debug") + return QtDebugMsg; + if (type == u"info") + return QtInfoMsg; + if (type == u"warning") + return QtWarningMsg; + if (type == u"critical") + return QtCriticalMsg; + if (type == u"fatal") + return QtFatalMsg; + + Q_UNREACHABLE(); }; for (const QJsonValue &warning : warnings) { - // We're currently recreating the output that the logger would write to the console, - // so we can keep using our existing test data without modification. - // In the future our test data should be replaced with a structured representation of the - // warning it expects, possibly as a QQmlJS::DiagnosticMessage. - const QString warningMessage = - u"%1: %2:%3 %4"_qs - .arg(toDisplayWarningType(warning[u"type"].toString()), testFile(filename)) - .arg(warning[u"line"].isUndefined() - ? u""_qs - : u"%1:%2:"_qs.arg(warning[u"line"].toInt()) - .arg(warning[u"column"].toInt())) - .arg(warning[u"message"].toString()); + QString warningMessage = warning[u"message"].toString(); + quint32 warningLine = warning[u"line"].toInt(); + quint32 warningColumn = warning[u"column"].toInt(); + QtMsgType warningType = typeStringToMsgType(warning[u"type"].toString()); + if (warningMessage.contains(substring)) { + if (warningType != type) { + continue; + } + if (line != 0 || column != 0) { + if (warningLine != line || warningColumn != column) { + continue; + } + } + contains = true; break; } for (const QJsonValue &fix : warning[u"suggestions"].toArray()) { - const QString jsonFixMessage = u"Info: "_qs + fix[u"message"].toString(); - if (jsonFixMessage.contains(substring)) { + const QString fixMessage = fix[u"message"].toString(); + if (fixMessage.contains(substring)) { contains = true; break; } - if (searchReplacements == DoReplacementSearch - && fix[u"replacement"].toString().contains(substring)) { - contains = true; - break; + + if (searchReplacements == DoReplacementSearch) { + QString replacement = fix[u"replacement"].toString(); +#ifdef Q_OS_WIN + // Replacements can contain native line endings + // but we need them to be uniform in order for them to conform to our test data + replacement = replacement.replace(u"\r\n"_qs, u"\n"_qs); +#endif + + if (replacement.contains(substring)) { + quint32 fixLine = fix[u"line"].toInt(); + quint32 fixColumn = fix[u"column"].toInt(); + if (line != 0 || column != 0) { + if (fixLine != line || fixColumn != column) { + continue; + } + } + contains = true; + break; + } } } } @@ -1275,56 +1453,26 @@ void TestQmllint::searchWarnings(const QJsonArray &warnings, const QString &subs void TestQmllint::requiredProperty() { - { - QJsonArray warnings; - callQmllint("requiredProperty.qml", true, &warnings); - QVERIFY2(warnings.isEmpty(), qPrintable(QJsonDocument(warnings).toJson())); - } - - { - QJsonArray warnings; - callQmllint("requiredMissingProperty.qml", false, &warnings); - searchWarnings( - warnings, - QStringLiteral("Property \"foo\" was marked as required but does not exist."), - "requiredMissingProperty.qml"); - } - - { - QJsonArray warnings; - callQmllint("requiredPropertyBindings.qml", true, &warnings); - QVERIFY2(warnings.isEmpty(), qPrintable(QJsonDocument(warnings).toJson())); - } - - { - QJsonArray warnings; - callQmllint("requiredPropertyBindingsNow.qml", false, &warnings); - - searchWarnings( - warnings, - QStringLiteral( - "Component is missing required property required_now_string from Base"), - "requiredPropertyBindingsNow.qml"); - searchWarnings(warnings, - QStringLiteral("Component is missing required property " - "required_defined_here_string from here"), - "requiredPropertyBindingsNow.qml"); - } - - { - QJsonArray warnings; - callQmllint("requiredPropertyBindingsLater.qml", false, &warnings); - searchWarnings( - warnings, - QStringLiteral("Component is missing required property required_later_string from " - "Base (marked as required by Derived)"), - "requiredPropertyBindingsLater.qml"); - searchWarnings( - warnings, - QStringLiteral("Component is missing required property required_even_later_string " - "from Base (marked as required by here)"), - "requiredPropertyBindingsLater.qml"); - } + runTest("requiredProperty.qml", Result::clean()); + + runTest("requiredMissingProperty.qml", + Result { { Message { QStringLiteral( + "Property \"foo\" was marked as required but does not exist.") } } }); + + runTest("requiredPropertyBindings.qml", Result::clean()); + runTest("requiredPropertyBindingsNow.qml", + Result { { Message { QStringLiteral("Component is missing required property " + "required_now_string from Base") }, + Message { QStringLiteral("Component is missing required property " + "required_defined_here_string from here") } } }); + runTest("requiredPropertyBindingsLater.qml", + Result { { Message { QStringLiteral("Component is missing required property " + "required_later_string from " + "Base") }, + Message { QStringLiteral("Property marked as required in Derived") }, + Message { QStringLiteral("Component is missing required property " + "required_even_later_string " + "from Base (marked as required by here)") } } }); } void TestQmllint::settingsFile() @@ -1332,7 +1480,7 @@ void TestQmllint::settingsFile() QVERIFY(runQmllint("settings/unqualifiedSilent/unqualified.qml", true, QStringList(), false) .isEmpty()); QVERIFY(runQmllint("settings/unusedImportWarning/unused.qml", false, QStringList(), false) - .contains(QStringLiteral("Info: %1:2:1: Unused import at %1:2:1") + .contains(QStringLiteral("Warning: %1:2:1: Unused import at %1:2:1") .arg(testFile("settings/unusedImportWarning/unused.qml")))); QVERIFY(runQmllint("settings/bare/bare.qml", false, {}, false, false) .contains(QStringLiteral("Failed to find the following builtins: " @@ -1344,10 +1492,8 @@ void TestQmllint::settingsFile() void TestQmllint::additionalImplicitImport() { - QJsonArray warnings; - callQmllint("additionalImplicitImport.qml", true, &warnings, {}, {}, - { testFile("implicitImportResource.qrc") }); - QVERIFY(warnings.isEmpty()); + runTest("additionalImplicitImport.qml", Result::clean(), {}, {}, + { testFile("implicitImportResource.qrc") }); } void TestQmllint::attachedPropertyReuse() @@ -1355,24 +1501,13 @@ void TestQmllint::attachedPropertyReuse() auto options = QQmlJSLogger::options(); options[u"multiple-attached-objects"_qs].setLevel(u"warning"_qs); - { - QJsonArray warnings; - - callQmllint("attachedPropNotReused.qml", false, &warnings, {}, {}, {}, UseDefaultImports, - &options); + runTest("attachedPropNotReused.qml", + Result { { Message { QStringLiteral("Using attached type QQuickKeyNavigationAttached " + "already initialized in a parent " + "scope") } } }, + {}, {}, {}, UseDefaultImports, &options); - searchWarnings(warnings, - QStringLiteral("Using attached type QQuickKeyNavigationAttached " - "already initialized in a parent " - "scope"), - "attachedPropNotReused.qml"); - } - { - QJsonArray warnings; - callQmllint("attachedPropEnum.qml", true, &warnings, {}, {}, {}, UseDefaultImports, - &options); - QVERIFY2(warnings.isEmpty(), qPrintable(QJsonDocument(warnings).toJson())); - } + runTest("attachedPropEnum.qml", Result::clean(), {}, {}, {}, UseDefaultImports, &options); } void TestQmllint::missingBuiltinsNoCrash() @@ -1391,34 +1526,24 @@ void TestQmllint::missingBuiltinsNoCrash() QVERIFY2(jsonOutput.size() == 1, QJsonDocument(jsonOutput).toJson()); warnings = jsonOutput.at(0)[u"warnings"_qs].toArray(); - searchWarnings(warnings, - QStringLiteral("Failed to find the following builtins: " - "builtins.qmltypes, jsroot.qmltypes"), - "missingBuiltinsNoCrash.qml"); + checkResult(warnings, + Result { { Message { QStringLiteral("Failed to find the following builtins: " + "builtins.qmltypes, jsroot.qmltypes") } } }); } void TestQmllint::absolutePath() { - // We cannot use the normal linter here since we need to set a different parameter in the - // constructor - QQmlJSLinter linter(m_defaultImportPaths, true); - + QString absPathOutput = runQmllint("memberNotFound.qml", false, {}, true, true, true); + QString relPathOutput = runQmllint("memberNotFound.qml", false, {}, true, true, false); const QString absolutePath = QFileInfo(testFile("memberNotFound.qml")).absoluteFilePath(); - QJsonArray jsonOutput; - bool success = linter.lintFile(absolutePath, nullptr, true, &jsonOutput, {}, {}, {}, {}); - QVERIFY(!success); - QVERIFY2(jsonOutput.size() == 1, QJsonDocument(jsonOutput).toJson()); - QJsonArray warnings = jsonOutput.at(0)[u"warnings"_qs].toArray(); - - searchWarnings(warnings, absolutePath, absolutePath); + QVERIFY(absPathOutput.contains(absolutePath)); + QVERIFY(!relPathOutput.contains(absolutePath)); } void TestQmllint::importMultipartUri() { - QJsonArray warnings; - callQmllint("here.qml", true, &warnings, {}, { testFile("Elsewhere/qmldir") }); - QVERIFY(warnings.isEmpty()); + runTest("here.qml", Result::clean(), {}, { testFile("Elsewhere/qmldir") }); } QTEST_MAIN(TestQmllint) diff --git a/tests/auto/qml/qmltc/BLACKLIST b/tests/auto/qml/qmltc/BLACKLIST new file mode 100644 index 0000000000..f8422a806a --- /dev/null +++ b/tests/auto/qml/qmltc/BLACKLIST @@ -0,0 +1,2 @@ +[listView] +qnx ci diff --git a/tests/auto/qml/qmltc/data/CMakeLists.txt b/tests/auto/qml/qmltc/data/CMakeLists.txt index 816164022e..c58378aa3f 100644 --- a/tests/auto/qml/qmltc/data/CMakeLists.txt +++ b/tests/auto/qml/qmltc/data/CMakeLists.txt @@ -8,6 +8,8 @@ set(cpp_sources cpptypes/private/testprivateproperty_p.h cpptypes/typewithproperties.h cpptypes/typewithproperties.cpp + # deferred: + cpptypes/deferredpropertytypes.h cpptypes/deferredpropertytypes.cpp ) set(qml_sources @@ -65,6 +67,10 @@ set(qml_sources privatePropertySubclass.qml calqlatrBits.qml propertyChangeAndSignalHandlers.qml + deferredProperties.qml + deferredProperties_group.qml + deferredProperties_attached.qml + deferredProperties_complex.qml # support types: DefaultPropertySingleChild.qml @@ -108,6 +114,8 @@ qt6_add_qml_module(qmltc_test_module QML_FILES ${qml_sources} ${js_sources} + DEPENDENCIES + QtQuick ) qt_internal_target_compile_qml_to_cpp(qmltc_test_module NAMESPACE QmltcTest diff --git a/tests/auto/qml/qmltc/data/cpptypes/deferredpropertytypes.cpp b/tests/auto/qml/qmltc/data/cpptypes/deferredpropertytypes.cpp new file mode 100644 index 0000000000..4f66172cca --- /dev/null +++ b/tests/auto/qml/qmltc/data/cpptypes/deferredpropertytypes.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** 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 "deferredpropertytypes.h" + +QQuickItem *TypeWithDeferredProperty::deferredProperty() const +{ + return m_deferredProperty; +} + +void TypeWithDeferredProperty::setDeferredProperty(QQuickItem *value) +{ + if (m_deferredProperty != value) + m_deferredProperty = value; +} + +QBindable<QQuickItem *> TypeWithDeferredProperty::bindableDeferredProperty() +{ + return QBindable<QQuickItem *>(&m_deferredProperty); +} + +TestTypeAttachedWithDeferred *DeferredAttached::qmlAttachedProperties(QObject *parent) +{ + return new TestTypeAttachedWithDeferred(parent); +} diff --git a/tests/auto/qml/qmltc/data/cpptypes/deferredpropertytypes.h b/tests/auto/qml/qmltc/data/cpptypes/deferredpropertytypes.h new file mode 100644 index 0000000000..24d7d46aaa --- /dev/null +++ b/tests/auto/qml/qmltc/data/cpptypes/deferredpropertytypes.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** 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 DEFERREDPROPERTYTYPES_H +#define DEFERREDPROPERTYTYPES_H + +#include <QtCore/qobject.h> +#include <QtCore/qproperty.h> +#include <QtQml/qqmlregistration.h> +#include <QtQuick/qquickitem.h> + +#include "testgroupedtype.h" +#include "testattachedtype.h" + +// normal properties: + +class TypeWithDeferredProperty : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_CLASSINFO("DeferredPropertyNames", "deferredProperty") + + Q_PROPERTY(QQuickItem *deferredProperty READ deferredProperty WRITE setDeferredProperty BINDABLE + bindableDeferredProperty) + + QProperty<QQuickItem *> m_deferredProperty { nullptr }; + +public: + TypeWithDeferredProperty(QObject *parent = nullptr) : QObject(parent) { } + + QQuickItem *deferredProperty() const; + void setDeferredProperty(QQuickItem *); + QBindable<QQuickItem *> bindableDeferredProperty(); +}; + +// group properties: + +class TestTypeGroupedWithDeferred : public TestTypeGrouped +{ + Q_OBJECT + Q_PROPERTY(int deferred READ getDeferred WRITE setDeferred BINDABLE bindableDeferred) + QML_ANONYMOUS + Q_CLASSINFO("DeferredPropertyNames", "deferred") + + QProperty<int> m_deferred { 0 }; + +public: + int getDeferred() const { return m_deferred; } + void setDeferred(int v) { m_deferred = v; } + QBindable<int> bindableDeferred() const { return QBindable<int>(&m_deferred); } +}; + +class TypeWithDeferredGroup : public QObject +{ + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(TestTypeGroupedWithDeferred *group READ getGroup) + + TestTypeGroupedWithDeferred m_group; + +public: + TypeWithDeferredGroup(QObject *parent = nullptr) : QObject(parent) { } + + TestTypeGroupedWithDeferred *getGroup() { return &m_group; } +}; + +// attached properties: + +class TestTypeAttachedWithDeferred : public TestTypeAttached +{ + Q_OBJECT + Q_PROPERTY(int deferred READ getDeferred WRITE setDeferred BINDABLE bindableDeferred) + QML_ANONYMOUS + Q_CLASSINFO("DeferredPropertyNames", "deferred") + + QProperty<int> m_deferred { 0 }; + +public: + TestTypeAttachedWithDeferred(QObject *parent) : TestTypeAttached(parent) { } + + int getDeferred() const { return m_deferred; } + void setDeferred(int v) { m_deferred = v; } + QBindable<int> bindableDeferred() const { return QBindable<int>(&m_deferred); } +}; + +class DeferredAttached : public QObject +{ + Q_OBJECT + QML_ELEMENT + QML_ATTACHED(TestTypeAttachedWithDeferred) + +public: + DeferredAttached(QObject *parent = nullptr) : QObject(parent) { } + + static TestTypeAttachedWithDeferred *qmlAttachedProperties(QObject *); +}; + +// special: + +class TypeWithDeferredComplexProperties : public TypeWithDeferredGroup +{ + Q_OBJECT + QML_ELEMENT + Q_CLASSINFO("DeferredPropertyNames", "group,DeferredAttached") + +public: + TypeWithDeferredComplexProperties(QObject *parent = nullptr) : TypeWithDeferredGroup(parent) { } +}; + +#endif // DEFERREDPROPERTYTYPES_H diff --git a/tests/auto/qml/qmltc/data/cpptypes/typewithproperties.h b/tests/auto/qml/qmltc/data/cpptypes/typewithproperties.h index 51d7eca015..64625103f4 100644 --- a/tests/auto/qml/qmltc/data/cpptypes/typewithproperties.h +++ b/tests/auto/qml/qmltc/data/cpptypes/typewithproperties.h @@ -26,6 +26,9 @@ ** ****************************************************************************/ +#ifndef TYPEWITHPROPERTIES_H +#define TYPEWITHPROPERTIES_H + #include <QtCore/qobject.h> #include <QtCore/qproperty.h> #include <QtCore/qstring.h> @@ -70,3 +73,5 @@ Q_SIGNALS: void cWeirdSignal(QVariant); void dSignal(QString, int); }; + +#endif // TYPEWITHPROPERTIES_H diff --git a/tests/auto/qml/qmltc/data/deferredProperties.qml b/tests/auto/qml/qmltc/data/deferredProperties.qml new file mode 100644 index 0000000000..7b977744e2 --- /dev/null +++ b/tests/auto/qml/qmltc/data/deferredProperties.qml @@ -0,0 +1,16 @@ +import QtQuick +import QmltcTests 1.0 +TypeWithDeferredProperty { + id: root + property int width: 42 + + deferredProperty: Rectangle { + width: root.width * 2 + implicitHeight: 4 + height: implicitHeight + Rectangle { + width: root.width // + parent.width + height: parent.height + } + } +} diff --git a/tests/auto/qml/qmltc/data/deferredProperties_attached.qml b/tests/auto/qml/qmltc/data/deferredProperties_attached.qml new file mode 100644 index 0000000000..875deb5778 --- /dev/null +++ b/tests/auto/qml/qmltc/data/deferredProperties_attached.qml @@ -0,0 +1,6 @@ +import QtQuick +import QmltcTests 1.0 +QtObject { + DeferredAttached.attachedFormula: 43 + 10 - (5 * 2) + DeferredAttached.deferred: 42 +} diff --git a/tests/auto/qml/qmltc/data/deferredProperties_complex.qml b/tests/auto/qml/qmltc/data/deferredProperties_complex.qml new file mode 100644 index 0000000000..ce5429a095 --- /dev/null +++ b/tests/auto/qml/qmltc/data/deferredProperties_complex.qml @@ -0,0 +1,9 @@ +import QtQuick +import QmltcTests 1.0 +TypeWithDeferredComplexProperties { + group.str: "still immediate" + group.deferred: -1 + + DeferredAttached.attachedFormula: Math.abs(10 * 2) + DeferredAttached.deferred: 100 +} diff --git a/tests/auto/qml/qmltc/data/deferredProperties_group.qml b/tests/auto/qml/qmltc/data/deferredProperties_group.qml new file mode 100644 index 0000000000..a162968c86 --- /dev/null +++ b/tests/auto/qml/qmltc/data/deferredProperties_group.qml @@ -0,0 +1,6 @@ +import QtQuick +import QmltcTests 1.0 +TypeWithDeferredGroup { + group.str: "foo" + "bar" + group.deferred: 42 +} diff --git a/tests/auto/qml/qmltc/tst_qmltc.cpp b/tests/auto/qml/qmltc/tst_qmltc.cpp index e26aa06e78..ef92dd5c74 100644 --- a/tests/auto/qml/qmltc/tst_qmltc.cpp +++ b/tests/auto/qml/qmltc/tst_qmltc.cpp @@ -40,6 +40,10 @@ #include "importnamespace.h" #include "componenttype.h" #include "componenttypes.h" +#include "deferredproperties.h" +#include "deferredproperties_group.h" +#include "deferredproperties_attached.h" +#include "deferredproperties_complex.h" #include "signalhandlers.h" #include "javascriptfunctions.h" @@ -82,6 +86,8 @@ #include "calqlatrbits.h" #include "propertychangeandsignalhandlers.h" +#include "testprivateproperty.h" + // Qt: #include <QtCore/qstring.h> #include <QtCore/qbytearray.h> @@ -94,6 +100,7 @@ #include <QtTest/qsignalspy.h> +#include <QtCore/private/qobject_p.h> #include <QtTest/private/qemulationdetector_p.h> #ifndef QMLTC_TESTS_DISABLE_CACHE @@ -128,6 +135,10 @@ void tst_qmltc::initTestCase() QUrl("qrc:/QmltcTests/ObjectWithId.qml"), QUrl("qrc:/QmltcTests/documentWithIds.qml"), QUrl("qrc:/QmltcTests/importNamespace.qml"), + QUrl("qrc:/QmltcTests/deferredProperties.qml"), + QUrl("qrc:/QmltcTests/deferredProperties_group.qml"), + QUrl("qrc:/QmltcTests/deferredProperties_attached.qml"), + QUrl("qrc:/QmltcTests/deferredProperties_complex.qml"), QUrl("qrc:/QmltcTests/signalHandlers.qml"), QUrl("qrc:/QmltcTests/javaScriptFunctions.qml"), @@ -581,6 +592,73 @@ void tst_qmltc::componentTypes() } } +void tst_qmltc::deferredProperties() +{ + { + QQmlEngine e; + PREPEND_NAMESPACE(deferredProperties) created(&e); + QVERIFY(created.deferredProperty() + == nullptr); // binding is not applied since it is deferred + + qmlExecuteDeferred(&created); + + QQuickRectangle *rect = qobject_cast<QQuickRectangle *>(created.deferredProperty()); + QVERIFY(rect); + QCOMPARE(rect->width(), created.width() * 2); + QCOMPARE(rect->implicitHeight(), 4); + QCOMPARE(rect->height(), rect->implicitHeight()); + + QQmlListReference children(rect, "data"); + QCOMPARE(children.size(), 1); + QQuickRectangle *subRect = qobject_cast<QQuickRectangle *>(children.at(0)); + QVERIFY(subRect); + QCOMPARE(subRect->width(), created.width()); + QCOMPARE(subRect->height(), rect->height()); + } + { + QQmlEngine e; + PREPEND_NAMESPACE(deferredProperties_group) created(&e); + QCOMPARE(created.getGroup()->getStr(), u"foobar"_qs); + QCOMPARE(created.getGroup()->getDeferred(), 0); + // Note: we can't easily evaluate a deferred binding for a + // `group.deferred` here, so just accept the fact the the value is not + // set at all as a successful test + } + { + QQmlEngine e; + PREPEND_NAMESPACE(deferredProperties_attached) created(&e); + TestTypeAttachedWithDeferred *attached = qobject_cast<TestTypeAttachedWithDeferred *>( + qmlAttachedPropertiesObject<DeferredAttached>(&created, false)); + QVERIFY(attached); + + QCOMPARE(attached->getAttachedFormula(), 43); + QCOMPARE(attached->getDeferred(), 0); + // Note: we can't easily evaluate a deferred binding for a + // `group.deferred` here, so just accept the fact the the value is not + // set at all as a successful test + } + { + QQmlEngine e; + PREPEND_NAMESPACE(deferredProperties_complex) created(&e); + + // `group` binding is not deferred as per current behavior outside of + // PropertyChanges and friends. we defer `group.deferred` binding though + QCOMPARE(created.getGroup()->getStr(), u"still immediate"_qs); + QCOMPARE(created.getGroup()->getDeferred(), 0); + + QVERIFY(!qmlAttachedPropertiesObject<DeferredAttached>(&created, false)); + + qmlExecuteDeferred(&created); + + TestTypeAttachedWithDeferred *attached = qobject_cast<TestTypeAttachedWithDeferred *>( + qmlAttachedPropertiesObject<DeferredAttached>(&created, false)); + QVERIFY(attached); + + QCOMPARE(attached->getAttachedFormula(), 20); + QCOMPARE(attached->getDeferred(), 100); + } +} + void tst_qmltc::signalHandlers() { QQmlEngine e; @@ -1102,11 +1180,9 @@ void tst_qmltc::defaultAlias() QSKIP("Not implemented - not supported"); } -// TODO: this just doesn't work currently void tst_qmltc::attachedProperty() { QQmlEngine e; - QSKIP("Broken in many ways."); PREPEND_NAMESPACE(attachedProperty) created(&e); TestTypeAttached *attached = qobject_cast<TestTypeAttached *>( @@ -1145,8 +1221,6 @@ void tst_qmltc::attachedProperty() void tst_qmltc::groupedProperty() { QQmlEngine e; - QSKIP("Property index is wrong due to not picking QtQml dependency when creating group " - "property scope"); PREPEND_NAMESPACE(groupedProperty) created(&e); TestTypeGrouped *grouped = created.getGroup(); @@ -1708,9 +1782,22 @@ void tst_qmltc::keyEvents() void tst_qmltc::privateProperties() { - QSKIP("The same problem with poor QObject qmltypes is encountered here."); QQmlEngine e; PREPEND_NAMESPACE(privatePropertySubclass) created(&e); + QCOMPARE(created.dummy(), u"bar"_qs); + QCOMPARE(created.strAlias(), u"foobar"_qs); + QCOMPARE(created.smthAlias(), 42); + + auto privateCreated = static_cast<PrivatePropertyTypePrivate *>(QObjectPrivate::get(&created)); + QVERIFY(privateCreated); + QCOMPARE(privateCreated->foo(), u"Smth is: 42"_qs); + + ValueTypeGroup vt = privateCreated->vt(); + QCOMPARE(vt.count(), 11); + + TestTypeGrouped *group = privateCreated->getGroup(); + QCOMPARE(group->getCount(), 43); + QCOMPARE(group->getStr(), created.strAlias()); } void tst_qmltc::calqlatrBits() diff --git a/tests/auto/qml/qmltc/tst_qmltc.h b/tests/auto/qml/qmltc/tst_qmltc.h index ffffc6d9b7..5ce8a189da 100644 --- a/tests/auto/qml/qmltc/tst_qmltc.h +++ b/tests/auto/qml/qmltc/tst_qmltc.h @@ -53,6 +53,7 @@ private slots: void ids(); void importNamespace(); void componentTypes(); + void deferredProperties(); void signalHandlers(); void jsFunctions(); diff --git a/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp b/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp index 8b0db1c50a..d8c08ee5be 100644 --- a/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp +++ b/tests/auto/qml/qmltc_manual/tst_qmltc_manual.cpp @@ -49,6 +49,7 @@ #include <private/qquickitem_p.h> #include <private/qv4qmlcontext_p.h> #include <private/qqmlproperty_p.h> +#include <private/qquickanchors_p.h> #include <array> #include <memory> diff --git a/tests/auto/qml/qmltc_qprocess/CMakeLists.txt b/tests/auto/qml/qmltc_qprocess/CMakeLists.txt index 46f1d856d3..bd70d2fae7 100644 --- a/tests/auto/qml/qmltc_qprocess/CMakeLists.txt +++ b/tests/auto/qml/qmltc_qprocess/CMakeLists.txt @@ -12,7 +12,12 @@ qt6_add_qml_module(tst_qmltc_qprocess QML_FILES data/dummy.qml data/inlineComponent.qml + data/SingletonThing.qml + data/erroneousFile.qml ) +# special setup for singleton files: +set_source_files_properties(SingletonThing.qml PROPERTIES QT_QML_SINGLETON_TYPE true) + add_dependencies(tst_qmltc_qprocess Qt::qmltc) # fetch --resource arguments manually (mimics the logic of qmltc compilation diff --git a/tests/auto/qml/qmltc_qprocess/data/SingletonThing.qml b/tests/auto/qml/qmltc_qprocess/data/SingletonThing.qml new file mode 100644 index 0000000000..599b54eddd --- /dev/null +++ b/tests/auto/qml/qmltc_qprocess/data/SingletonThing.qml @@ -0,0 +1,7 @@ +pragma Singleton +import QtQml + +QtObject { + property int integerProperty: 42 + property string stringProperty: "hello" +} diff --git a/tests/auto/qml/qmltc_qprocess/data/erroneousFile.qml b/tests/auto/qml/qmltc_qprocess/data/erroneousFile.qml new file mode 100644 index 0000000000..a7316ed78b --- /dev/null +++ b/tests/auto/qml/qmltc_qprocess/data/erroneousFile.qml @@ -0,0 +1,4 @@ +import QtQml +QtObject { + property NonExistent type: QtObject {} +} diff --git a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp index 50abcc8332..09c77ce37a 100644 --- a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp +++ b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp @@ -68,6 +68,8 @@ private slots: void noBuiltins(); void noQtQml(); void inlineComponent(); + void singleton(); + void warningsAsErrors(); }; #ifndef TST_QMLTC_QPROCESS_RESOURCES @@ -200,5 +202,18 @@ void tst_qmltc_qprocess::inlineComponent() QVERIFY(!errors.contains(u"Inline components are not supported"_qs)); } +void tst_qmltc_qprocess::singleton() +{ + const auto errors = runQmltc(u"SingletonThing.qml"_qs, false); + QEXPECT_FAIL("", "qmltc does not support singletons at the moment", Continue); + QVERIFY(!errors.contains(u"Singleton types are not supported"_qs)); +} + +void tst_qmltc_qprocess::warningsAsErrors() +{ + const auto errors = runQmltc(u"erroneousFile.qml"_qs, false); + QVERIFY2(errors.contains(u"Error:"_qs), qPrintable(errors)); // Note: not a warning! +} + QTEST_MAIN(tst_qmltc_qprocess) #include "tst_qmltc_qprocess.moc" diff --git a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt index c7326c30c4..ad22f51cd9 100644 --- a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt +++ b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt @@ -62,3 +62,13 @@ set_target_properties(tst_qmltyperegistrar PROPERTIES # yet, so we have to call it directly to test that code path for now. _qt_internal_qml_type_registration(tst_qmltyperegistrar MANUAL_MOC_JSON_FILES ${json_list}) add_subdirectory(foreign) + +qt_add_library(tst-qmltyperegistrar-with-dashes) +target_link_libraries(tst-qmltyperegistrar-with-dashes PRIVATE Qt::Core Qt::Qml) +qt_enable_autogen_tool(tst-qmltyperegistrar-with-dashes "moc" ON) +qt_add_qml_module(tst-qmltyperegistrar-with-dashes + URI Module-With-Dashes + VERSION 1.0 + SOURCES + foo.cpp foo.h +) diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp index 5619c2aa0f..3fc746552e 100644 --- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp @@ -339,6 +339,46 @@ void tst_qmltyperegistrar::methodReturnType() QVERIFY(qmltypesData.contains("type: \"QQmlComponent\"")); } +void tst_qmltyperegistrar::addRemoveVersion_data() +{ + QTest::addColumn<QTypeRevision>("importVersion"); + for (int i = 0; i < 20; ++i) + QTest::addRow("v1.%d.qml", i) << QTypeRevision::fromVersion(1, i); +} + +void tst_qmltyperegistrar::addRemoveVersion() +{ + QFETCH(QTypeRevision, importVersion); + + const bool creatable + = importVersion > QTypeRevision::fromVersion(1, 2) + && importVersion < QTypeRevision::fromVersion(1, 18); + const bool thingAccessible = importVersion > QTypeRevision::fromVersion(1, 3); + + QQmlEngine engine; + QQmlComponent c(&engine); + c.setData(QStringLiteral("import QmlTypeRegistrarTest %1.%2\n" + "Versioned {\n" + " property int thing: revisioned\n" + "}") + .arg(importVersion.majorVersion()).arg(importVersion.minorVersion()).toUtf8(), + QUrl(QTest::currentDataTag())); + if (!creatable) { + QVERIFY(c.isError()); + return; + } + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + if (!thingAccessible) { + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(QStringLiteral("%1:3: ReferenceError: revisioned is not defined") + .arg(QTest::currentDataTag()))); + } + QScopedPointer o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("thing").toInt(), thingAccessible ? 24 : 0); +} + #ifdef QT_QUICK_LIB void tst_qmltyperegistrar::foreignRevisionedProperty() { diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h index d57053a051..385d3b6666 100644 --- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h @@ -469,6 +469,28 @@ public: }; #endif +class AddedInLateVersion : public QObject +{ + Q_OBJECT + QML_NAMED_ELEMENT(Versioned) + QML_ADDED_IN_VERSION(1, 8) + Q_PROPERTY(int revisioned READ revisioned CONSTANT REVISION(1, 4)) + Q_PROPERTY(int insane READ revisioned CONSTANT REVISION 17) +public: + AddedInLateVersion(QObject *parent = nullptr) : QObject(parent) {} + int revisioned() const { return 24; } +}; + +class RemovedInEarlyVersion : public AddedInLateVersion +{ + Q_OBJECT + QML_NAMED_ELEMENT(Versioned) + QML_ADDED_IN_VERSION(1, 3) + QML_REMOVED_IN_VERSION(1, 8) +public: + RemovedInEarlyVersion(QObject *parent = nullptr) : AddedInLateVersion(parent) {} +}; + class tst_qmltyperegistrar : public QObject { Q_OBJECT @@ -508,6 +530,9 @@ private slots: void foreignRevisionedProperty(); #endif + void addRemoveVersion_data(); + void addRemoveVersion(); + private: QByteArray qmltypesData; }; diff --git a/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt b/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt index 7f05a759b6..d9fbf6517d 100644 --- a/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt +++ b/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt @@ -35,6 +35,9 @@ qt_internal_add_resource(tst_qqmlapplicationengine "tst_qqmlapplicationengine" ${tst_qqmlapplicationengine_resource_files} ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qqmlapplicationengine) +endif() #### Keys ignored in scope 2:.:.:tst_qqmlapplicationengine.pro:<TRUE>: # TRANSLATIONS = "data/i18n/qml_ja.ts" diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp index 67c75ccce7..c948ad5b9c 100644 --- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp @@ -101,6 +101,10 @@ void tst_qqmlapplicationengine::basicLoading() // will break. void tst_qqmlapplicationengine::testNonResolvedPath() { +#if defined(Q_OS_INTEGRITY) + QSKIP("INTEGRITY stores QML files in resources, and the path to a resource cannot be relative in this case"); +#endif + #ifdef Q_OS_ANDROID QSKIP("Android stores QML files in resources, and the path to a resource cannot be relative in this case"); #endif diff --git a/tests/auto/qml/qqmlcomponent/data/jsmodule/module.mjs b/tests/auto/qml/qqmlcomponent/data/jsmodule/module.mjs index 3dd3507d45..9175ba0a67 100644 --- a/tests/auto/qml/qqmlcomponent/data/jsmodule/module.mjs +++ b/tests/auto/qml/qqmlcomponent/data/jsmodule/module.mjs @@ -1,5 +1,6 @@ export function withProp(root) { - const component = Qt.createComponent("data/jsmodule/Dynamic.qml"); + const prefix = Qt.platform.os == "android" ? "qrc:" : ""; + const component = Qt.createComponent(prefix + "data/jsmodule/Dynamic.qml"); const el = component.createObject(root, { value: 42 }); return el.value; } diff --git a/tests/auto/qml/qqmlconsole/CMakeLists.txt b/tests/auto/qml/qqmlconsole/CMakeLists.txt index 171d1ad578..5096503c15 100644 --- a/tests/auto/qml/qqmlconsole/CMakeLists.txt +++ b/tests/auto/qml/qqmlconsole/CMakeLists.txt @@ -21,6 +21,10 @@ qt_internal_add_test(tst_qqmlconsole TESTDATA ${test_data} ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qqmlconsole) +endif() + ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qqmlengine/CMakeLists.txt b/tests/auto/qml/qqmlengine/CMakeLists.txt index 01e9999f79..43fec7cb90 100644 --- a/tests/auto/qml/qqmlengine/CMakeLists.txt +++ b/tests/auto/qml/qqmlengine/CMakeLists.txt @@ -36,6 +36,9 @@ qt_internal_add_resource(tst_qqmlengine "qmake_immediate" ${qmake_immediate_resource_files} ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qqmlengine) +endif() ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qqmlfileselector/CMakeLists.txt b/tests/auto/qml/qqmlfileselector/CMakeLists.txt index d010280a9d..4d7c4f840b 100644 --- a/tests/auto/qml/qqmlfileselector/CMakeLists.txt +++ b/tests/auto/qml/qqmlfileselector/CMakeLists.txt @@ -22,6 +22,10 @@ qt_internal_add_test(tst_qqmlfileselector TESTDATA ${test_data} ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qqmlfileselector) +endif() + ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qqmlimport/data/absoluteImport.qml b/tests/auto/qml/qqmlimport/data/absoluteImport.qml new file mode 100644 index 0000000000..d182fe5b71 --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/absoluteImport.qml @@ -0,0 +1,4 @@ +import "/foo/bar/baz" + +QtObject {} + diff --git a/tests/auto/qml/qqmlimport/data/absoluteResourceImport.qml b/tests/auto/qml/qqmlimport/data/absoluteResourceImport.qml new file mode 100644 index 0000000000..74b98664aa --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/absoluteResourceImport.qml @@ -0,0 +1,4 @@ +import ":/absolute/resource/path" + +QtObject {} + diff --git a/tests/auto/qml/qqmlimport/data/relativeResourceImport.qml b/tests/auto/qml/qqmlimport/data/relativeResourceImport.qml new file mode 100644 index 0000000000..6a7e2b7ea5 --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/relativeResourceImport.qml @@ -0,0 +1,4 @@ +import ":relative/resource/path" + +QtObject {} + diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp index f52b01cf04..a2662d5a67 100644 --- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp +++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp @@ -35,6 +35,7 @@ #include <private/qqmlimport_p.h> #include <private/qqmlengine_p.h> #include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QQmlComponent> class tst_QQmlImport : public QQmlDataTest { @@ -59,6 +60,8 @@ private slots: void cleanup(); void envResourceImportPath(); void preferResourcePath(); + void invalidFileImport_data(); + void invalidFileImport(); }; void tst_QQmlImport::cleanup() @@ -104,6 +107,36 @@ void tst_QQmlImport::preferResourcePath() QCOMPARE(o->objectName(), "right"); } +void tst_QQmlImport::invalidFileImport_data() +{ + QTest::addColumn<QString>("file"); + QTest::addColumn<QString>("import"); + QTest::addRow("file absolute") + << QStringLiteral("absoluteImport.qml") + << QStringLiteral("/foo/bar/baz"); + QTest::addRow("resource absolute") + << QStringLiteral("absoluteResourceImport.qml") + << QStringLiteral(":/absolute/resource/path"); + QTest::addRow("resource relative") + << QStringLiteral("relativeResourceImport.qml") + << QStringLiteral(":relative/resource/path"); +} + +void tst_QQmlImport::invalidFileImport() +{ + QFETCH(QString, file); + QFETCH(QString, import); + + QQmlEngine engine; + + QQmlComponent component(&engine, testFileUrl(file)); + QVERIFY(component.isError()); + QVERIFY(component.errorString().contains( + QStringLiteral("\"%1\" is not a valid import URL. " + "You can pass relative paths or URLs with schema, " + "but not absolute paths or resource paths.").arg(import))); +} + void tst_QQmlImport::testDesignerSupported() { QQuickView *window = new QQuickView(); diff --git a/tests/auto/qml/qqmlincubator/CMakeLists.txt b/tests/auto/qml/qqmlincubator/CMakeLists.txt index 934199a66e..b2ade30c39 100644 --- a/tests/auto/qml/qqmlincubator/CMakeLists.txt +++ b/tests/auto/qml/qqmlincubator/CMakeLists.txt @@ -24,6 +24,10 @@ qt_internal_add_test(tst_qqmlincubator TESTDATA ${test_data} ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qqmlincubator) +endif() + ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qqmlinfo/CMakeLists.txt b/tests/auto/qml/qqmlinfo/CMakeLists.txt index 9ba8ca4c4c..3020b84c59 100644 --- a/tests/auto/qml/qqmlinfo/CMakeLists.txt +++ b/tests/auto/qml/qqmlinfo/CMakeLists.txt @@ -23,6 +23,10 @@ qt_internal_add_test(tst_qqmlinfo TESTDATA ${test_data} ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qqmlinfo) +endif() + #### Keys ignored in scope 1:.:.:qqmlinfo.pro:<TRUE>: # QML_IMPORT_MAJOR_VERSION = "1" # QML_IMPORT_NAME = "org.qtproject.Test" diff --git a/tests/auto/qml/qqmlitemmodels/data/modelindex.qml b/tests/auto/qml/qqmlitemmodels/data/modelindex.qml index 0d6e3624cb..2756f04120 100644 --- a/tests/auto/qml/qqmlitemmodels/data/modelindex.qml +++ b/tests/auto/qml/qqmlitemmodels/data/modelindex.qml @@ -1,3 +1,4 @@ +import QtQml 2.0 import Test 1.0 ItemModelsTest { diff --git a/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml b/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml index 13037065a6..85987bdcac 100644 --- a/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml +++ b/tests/auto/qml/qqmlitemmodels/data/persistentmodelindex.qml @@ -1,3 +1,4 @@ +import QtQml 2.0 import Test 1.0 ItemModelsTest { diff --git a/tests/auto/qml/qqmllanguage/data/foreignExtended.qml b/tests/auto/qml/qqmllanguage/data/foreignExtended.qml index 4863e0d567..b01af6d229 100644 --- a/tests/auto/qml/qqmllanguage/data/foreignExtended.qml +++ b/tests/auto/qml/qqmllanguage/data/foreignExtended.qml @@ -5,12 +5,17 @@ QtObject { property Foreign foreign: Foreign { objectName: "foreign" } - property Extended extended: Extended {} + property Extended extended: Extended { + objectName: "extended" + property int changeCount: 0 + onExtensionChanged: ++changeCount + } property ForeignExtended foreignExtended: ForeignExtended { objectName: "foreignExtended" } property int extendedBase: extended.base + property int extendedChangeCount: extended.changeCount property int extendedInvokable: extended.invokable() property int extendedSlot: extended.slot() diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 57d3fe8860..b09840606b 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -1470,13 +1470,23 @@ public: class Extension : public QObject { Q_OBJECT - Q_PROPERTY(int extension READ extension CONSTANT) + Q_PROPERTY(int extension READ extension WRITE setExtension NOTIFY extensionChanged FINAL) public: Extension(QObject *parent = nullptr) : QObject(parent) {} - int extension() const { return 42; } + int extension() const { return ext; } + void setExtension(int e) { + if (e != ext) { + ext = e; + emit extensionChanged(); + } + } Q_INVOKABLE int invokable() { return 123; } +Q_SIGNALS: + void extensionChanged(); public slots: int slot() { return 456; } +private: + int ext = 42; }; class Extended : public QObject diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 17d7ff6f7e..cf35ad0a6a 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -283,6 +283,7 @@ private slots: void lazyDeferredSubObject(); void deferredProperties(); void executeDeferredPropertiesOnce(); + void deferredProperties_extra(); void noChildEvents(); @@ -5134,6 +5135,82 @@ void tst_qqmllanguage::executeDeferredPropertiesOnce() QCOMPARE(listProperty.at(&listProperty, 1)->property("wasCompleted"), QVariant(true)); } +class GroupType : public QObject +{ + Q_OBJECT + Q_PROPERTY(int foo READ getFoo WRITE setFoo BINDABLE bindableFoo) + Q_PROPERTY(int bar READ getBar WRITE setBar BINDABLE bindableBar) + Q_CLASSINFO("DeferredPropertyNames", "bar") + + QProperty<int> m_foo { 0 }; + QProperty<int> m_bar { 0 }; + +public: + int getFoo() const { return m_foo; } + void setFoo(int v) { m_foo = v; } + QBindable<int> bindableFoo() const { return QBindable<int>(&m_foo); } + + int getBar() const { return m_bar; } + void setBar(int v) { m_bar = v; } + QBindable<int> bindableBar() const { return QBindable<int>(&m_bar); } +}; + +class ExtraDeferredProperties : public QObject +{ + Q_OBJECT + Q_PROPERTY(GroupType *group READ getGroup) + Q_CLASSINFO("DeferredPropertyNames", "group,MyQmlObject") + + GroupType m_group; + +public: + ExtraDeferredProperties(QObject *parent = nullptr) : QObject(parent) { } + + GroupType *getGroup() { return &m_group; } +}; + +void tst_qqmllanguage::deferredProperties_extra() +{ + // Note: because ExtraDeferredProperties defers only a `group` property, the + // deferral does not actually work. + QTest::ignoreMessage( + QtMsgType::QtWarningMsg, + "Binding on group is not deferred as requested by the DeferredPropertyNames class info " + "because it constitutes a group property."); + + qmlRegisterType<GroupType>("deferred.properties.extra", 1, 0, "GroupType"); + qmlRegisterType<ExtraDeferredProperties>("deferred.properties.extra", 1, 0, + "ExtraDeferredProperties"); + QQmlComponent component(&engine); + component.setData(R"( + import QtQuick + import Test 1.0 + import deferred.properties.extra 1.0 + + ExtraDeferredProperties { + group.foo: 4 + group.bar: 4 + MyQmlObject.value: 1 + } + )", QUrl()); + + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<ExtraDeferredProperties> object( + qobject_cast<ExtraDeferredProperties *>(component.create())); + QVERIFY(object); + + QCOMPARE(object->getGroup()->getFoo(), 4); // not deferred (as group itself is not deferred) + QCOMPARE(object->getGroup()->getBar(), 0); // deferred, as per group's own deferred names + // but attached property is deferred: + QVERIFY(!qmlAttachedPropertiesObject<MyQmlObject>(object.get(), false)); + + qmlExecuteDeferred(object.get()); + + auto attached = qmlAttachedPropertiesObject<MyQmlObject>(object.get(), false); + QVERIFY(attached); + QCOMPARE(attached->property("value").toInt(), 1); +} + void tst_qqmllanguage::noChildEvents() { QQmlComponent component(&engine); @@ -5559,9 +5636,20 @@ void tst_qqmllanguage::extendedForeignTypes() QScopedPointer<QObject> o(component.create()); QVERIFY(!o.isNull()); + QObject *extended = o->property("extended").value<QObject *>(); + QVERIFY(extended); + QSignalSpy extensionChangedSpy(extended, SIGNAL(extensionChanged())); + QCOMPARE(o->property("extendedBase").toInt(), 43); QCOMPARE(o->property("extendedExtension").toInt(), 42); QCOMPARE(o->property("foreignExtendedExtension").toInt(), 42); + + QCOMPARE(extensionChangedSpy.count(), 0); + extended->setProperty("extension", 44); + QCOMPARE(extensionChangedSpy.count(), 1); + QCOMPARE(o->property("extendedChangeCount").toInt(), 1); + QCOMPARE(o->property("extendedExtension").toInt(), 44); + QCOMPARE(o->property("foreignObjectName").toString(), QLatin1String("foreign")); QCOMPARE(o->property("foreignExtendedObjectName").toString(), QLatin1String("foreignExtended")); QCOMPARE(o->property("extendedInvokable").toInt(), 123); diff --git a/tests/auto/qml/qqmllistreference/CMakeLists.txt b/tests/auto/qml/qqmllistreference/CMakeLists.txt index b620873c54..9d4b3cac7a 100644 --- a/tests/auto/qml/qqmllistreference/CMakeLists.txt +++ b/tests/auto/qml/qqmllistreference/CMakeLists.txt @@ -23,6 +23,10 @@ qt_internal_add_test(tst_qqmllistreference TESTDATA ${test_data} ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qqmllistreference) +endif() + ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qqmlmetaobject/CMakeLists.txt b/tests/auto/qml/qqmlmetaobject/CMakeLists.txt index a46b58f9ab..93edb03527 100644 --- a/tests/auto/qml/qqmlmetaobject/CMakeLists.txt +++ b/tests/auto/qml/qqmlmetaobject/CMakeLists.txt @@ -21,6 +21,10 @@ qt_internal_add_test(tst_qqmlmetaobject TESTDATA ${test_data} ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qqmlmetaobject) +endif() + ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp b/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp index 81c6b0d2a1..fa54d440b8 100644 --- a/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp +++ b/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp @@ -27,6 +27,7 @@ ****************************************************************************/ #include <QtTest/QtTest> +#include <QtCore/QScopedPointer> #include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlengine.h> #include <QtQuickTestUtils/private/qmlutils_p.h> @@ -188,8 +189,9 @@ void tst_QQmlMetaObject::property() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl(testFile)); - QObject *object = component.create(); - QVERIFY(object != nullptr); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj); + QObject *object = obj.get(); const QMetaObject *mo = object->metaObject(); QVERIFY(mo->superClass() != nullptr); @@ -263,8 +265,6 @@ void tst_QQmlMetaObject::property() QVERIFY(!prop.write(object, prop.read(object))); QCOMPARE(changedSpy.count(), 0); } - - delete object; } void tst_QQmlMetaObject::method_data() @@ -359,8 +359,9 @@ void tst_QQmlMetaObject::method() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl(testFile)); - QObject *object = component.create(); - QVERIFY(object != nullptr); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj); + QObject *object = obj.get(); const QMetaObject *mo = object->metaObject(); QVERIFY(mo->superClass() != nullptr); @@ -384,8 +385,6 @@ void tst_QQmlMetaObject::method() QCOMPARE(QString::fromUtf8(method.typeName()), returnTypeName); QCOMPARE(method.returnType(), returnType); - - delete object; } QTEST_MAIN(tst_QQmlMetaObject) diff --git a/tests/auto/qml/qqmlmetatype/CMakeLists.txt b/tests/auto/qml/qqmlmetatype/CMakeLists.txt index 86b1fa6afb..74146bb49b 100644 --- a/tests/auto/qml/qqmlmetatype/CMakeLists.txt +++ b/tests/auto/qml/qqmlmetatype/CMakeLists.txt @@ -46,6 +46,9 @@ qt_internal_add_resource(tst_qqmlmetatype "qmake_qmldirresource" ${qmake_qmldirresource_resource_files} ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qqmlmetatype) +endif() ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qqmlnotifier/CMakeLists.txt b/tests/auto/qml/qqmlnotifier/CMakeLists.txt index dfd9d625bc..4056f06458 100644 --- a/tests/auto/qml/qqmlnotifier/CMakeLists.txt +++ b/tests/auto/qml/qqmlnotifier/CMakeLists.txt @@ -20,6 +20,10 @@ qt_internal_add_test(tst_qqmlnotifier TESTDATA ${test_data} ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qqmlnotifier) +endif() + ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qqmlpromise/CMakeLists.txt b/tests/auto/qml/qqmlpromise/CMakeLists.txt index 00c3dcf6b4..ac0ffebabe 100644 --- a/tests/auto/qml/qqmlpromise/CMakeLists.txt +++ b/tests/auto/qml/qqmlpromise/CMakeLists.txt @@ -24,6 +24,10 @@ qt_internal_add_test(tst_qqmlpromise TESTDATA ${test_data} ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qqmlpromise) +endif() + #### Keys ignored in scope 1:.:.:qqmlpromise.pro:<TRUE>: # DISTFILES = "data/then-fulfilled-non-callable.qml" "data/then-reject-non-callable.qml" "data/then-resolve-multiple-then.qml" "data/then-resolve-chaining.qml" "data/then-reject-chaining.qml" "data/promise-resolve-with-value.qml" "data/promise-resolve-with-promise.qml" "data/promise-reject-with-value.qml" "data/promise-executor-resolve.qml" "data/promise-get-length.qml" "data/promise-executor-reject.qml" "data/promise-reject-catch.qml" "data/promise-async-resolve-with-value.qml" "data/promise-async-reject-with-value.qml" "data/promise-all-resolve.qml" "data/promise-all-empty-input.qml" "data/promise-resolve-with-array.qml" "data/promise-all-reject-reject-is-last.qml" "data/promise-all-reject-reject-is-mid.qml" "data/promise-race-resolve-1st.qml" "data/promise-race-empty-input.qml" "data/promise-race-resolve-2nd.qml" "data/promise-race-resolve-1st-in-executor-function.qml" "data/promise-resolve-is-a-function.qml" "data/promise-resolve-function-length.qml" "data/promise-all-invoke-then-method.qml" "data/promise-resolve-with-empty.qml" "data/promise-executor-throw-exception.qml" "data/promise-executor-function-extensible.qml" "data/promise-all-noniterable-input.qml" # OTHER_FILES = <EMPTY> diff --git a/tests/auto/qml/qqmlpropertymap/dummy_imports.qml b/tests/auto/qml/qqmlpropertymap/dummy_imports.qml index 4ae9d3f2cf..63d33cbea6 100644 --- a/tests/auto/qml/qqmlpropertymap/dummy_imports.qml +++ b/tests/auto/qml/qqmlpropertymap/dummy_imports.qml @@ -1,8 +1,7 @@ // This file exists for the sole purpose for qmlimportscanner to find // which modules it needs to extract for deployment. -// Otherwise, it fails to find the imports that are expressed in the -// C++ code belonging to the test. +// Otherwise, it fails to find the imports that are expressed in C++. -import QtQuick 2.0 +import QtQuick -QtObject { } // This is needed in order to keep importscanner happy +QtObject { } diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp index 8e4849e2d6..e97ab543d1 100644 --- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp +++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp @@ -637,7 +637,10 @@ void tst_qqmlqt::openUrlExternally() const QUrl htmlTestFile = testFileUrl("test.html"); QDesktopServices::setUrlHandler("test", &handler, "noteCall"); QDesktopServices::setUrlHandler(htmlTestFile.scheme(), &handler, "noteCall"); - + const auto unset = qScopeGuard([&] { + QDesktopServices::unsetUrlHandler(htmlTestFile.scheme()); + QDesktopServices::unsetUrlHandler("test"); + }); QQmlComponent component(&engine, testFileUrl("openUrlExternally.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); @@ -648,9 +651,6 @@ void tst_qqmlqt::openUrlExternally() QCOMPARE(handler.called,2); QCOMPARE(handler.last, htmlTestFile); - - QDesktopServices::unsetUrlHandler("test"); - QDesktopServices::unsetUrlHandler(htmlTestFile.scheme()); } void tst_qqmlqt::openUrlExternally_pragmaLibrary() @@ -660,6 +660,10 @@ void tst_qqmlqt::openUrlExternally_pragmaLibrary() const QUrl htmlTestFile = testFileUrl("test.html"); QDesktopServices::setUrlHandler("test", &handler, "noteCall"); QDesktopServices::setUrlHandler(htmlTestFile.scheme(), &handler, "noteCall"); + const auto unset = qScopeGuard([&] { + QDesktopServices::unsetUrlHandler(htmlTestFile.scheme()); + QDesktopServices::unsetUrlHandler("test"); + }); QQmlComponent component(&engine, testFileUrl("openUrlExternally_lib.qml")); QScopedPointer<QObject> object(component.create()); @@ -671,9 +675,6 @@ void tst_qqmlqt::openUrlExternally_pragmaLibrary() QCOMPARE(handler.called,2); QCOMPARE(handler.last, htmlTestFile); - - QDesktopServices::unsetUrlHandler("test"); - QDesktopServices::unsetUrlHandler(htmlTestFile.scheme()); } void tst_qqmlqt::md5() diff --git a/tests/auto/qml/qqmlsqldatabase/dummy_imports.qml b/tests/auto/qml/qqmlsqldatabase/dummy_imports.qml index 4ae9d3f2cf..63d33cbea6 100644 --- a/tests/auto/qml/qqmlsqldatabase/dummy_imports.qml +++ b/tests/auto/qml/qqmlsqldatabase/dummy_imports.qml @@ -1,8 +1,7 @@ // This file exists for the sole purpose for qmlimportscanner to find // which modules it needs to extract for deployment. -// Otherwise, it fails to find the imports that are expressed in the -// C++ code belonging to the test. +// Otherwise, it fails to find the imports that are expressed in C++. -import QtQuick 2.0 +import QtQuick -QtObject { } // This is needed in order to keep importscanner happy +QtObject { } diff --git a/tests/auto/qml/qqmltimer/dummy_imports.qml b/tests/auto/qml/qqmltimer/dummy_imports.qml index f78e04d489..e189da6689 100644 --- a/tests/auto/qml/qqmltimer/dummy_imports.qml +++ b/tests/auto/qml/qqmltimer/dummy_imports.qml @@ -1,9 +1,8 @@ // This file exists for the sole purpose for qmlimportscanner to find // which modules it needs to extract for deployment. -// Otherwise, it fails to find the imports that are expressed in the -// C++ code belonging to the test. +// Otherwise, it fails to find the imports that are expressed in C++. -import QtQml 2.0 -import QtQuick 2.0 +import QtQml +import QtQuick -QtObject { } // This is needed in order to keep importscanner happy +QtObject { } diff --git a/tests/auto/qml/qqmltypeloader/dummy_imports.qml b/tests/auto/qml/qqmltypeloader/dummy_imports.qml index a4684b2007..e189da6689 100644 --- a/tests/auto/qml/qqmltypeloader/dummy_imports.qml +++ b/tests/auto/qml/qqmltypeloader/dummy_imports.qml @@ -1,9 +1,8 @@ // This file exists for the sole purpose for qmlimportscanner to find // which modules it needs to extract for deployment. -// Otherwise, it fails to find the imports that are expressed in the -// C++ code belonging to the test. +// Otherwise, it fails to find the imports that are expressed in C++. -import QtQml 2.0 -import QtQuick 2.6 +import QtQml +import QtQuick -QtObject { } // This is needed in order to keep importscanner happy +QtObject { } diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp index a030e56b20..2463af827a 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp @@ -42,6 +42,7 @@ #include <QtQml/private/qqmlirloader_p.h> #include <QtQuickTestUtils/private/testhttpserver_p.h> #include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QQmlComponent> class tst_QQMLTypeLoader : public QQmlDataTest { diff --git a/tests/auto/qml/qqmlvaluetypes/data/locale_read_singleton.qml b/tests/auto/qml/qqmlvaluetypes/data/locale_read_singleton.qml new file mode 100644 index 0000000000..f56fe3f422 --- /dev/null +++ b/tests/auto/qml/qqmlvaluetypes/data/locale_read_singleton.qml @@ -0,0 +1,21 @@ +import QtQuick + +Item { + property string amText: InputMethod.locale.amText + property string decimalPoint: InputMethod.locale.decimalPoint + property string exponential: InputMethod.locale.exponential + property int firstDayOfWeek: InputMethod.locale.firstDayOfWeek + property string groupSeparator: InputMethod.locale.groupSeparator + property int measurementSystem: InputMethod.locale.measurementSystem + property string name: InputMethod.locale.name + property string nativeCountryName: InputMethod.locale.nativeCountryName + property string nativeLanguageName: InputMethod.locale.nativeLanguageName + property string negativeSign: InputMethod.locale.negativeSign + property string percent: InputMethod.locale.percent + property string pmText: InputMethod.locale.pmText + property string positiveSign: InputMethod.locale.positiveSign + property int textDirection: InputMethod.locale.textDirection + property var uiLanguages: InputMethod.locale.uiLanguages + property var weekDays: InputMethod.locale.weekDays + property string zeroDigit: InputMethod.locale.zeroDigit +} diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp index 38e1e585ce..a61a632d24 100644 --- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp +++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp @@ -295,8 +295,9 @@ void tst_qqmlvaluetypes::sizef() void tst_qqmlvaluetypes::locale() { - { - QQmlComponent component(&engine, testFileUrl("locale_read.qml")); + for (const QUrl &testFile : + { testFileUrl("locale_read.qml"), testFileUrl("locale_read_singleton.qml") }) { + QQmlComponent component(&engine, testFile); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); diff --git a/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt b/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt index 19450aaef7..ea244fcbe4 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt +++ b/tests/auto/qml/qqmlxmlhttprequest/CMakeLists.txt @@ -23,6 +23,10 @@ qt_internal_add_test(tst_qqmlxmlhttprequest TESTDATA ${test_data} ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qqmlxmlhttprequest) +endif() + ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp index 3353d8d5e1..d1dcf3829a 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp +++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp @@ -1093,6 +1093,10 @@ static const QString testString = QStringLiteral("Test-String"); void tst_qqmlxmlhttprequest::doFileRequest(std::function<void(QObject *component, QTemporaryFile &writeFile)> verifyFunction) { +#if defined(Q_OS_INTEGRITY) + QSKIP("There's no mounted filesystem on INTEGRITY."); +#endif + // Create test files QTemporaryFile writeFile; QTemporaryFile readFile; diff --git a/tests/auto/qml/qtqmlmodules/CMakeLists.txt b/tests/auto/qml/qtqmlmodules/CMakeLists.txt index 2bff111bc7..6ff0cc01ae 100644 --- a/tests/auto/qml/qtqmlmodules/CMakeLists.txt +++ b/tests/auto/qml/qtqmlmodules/CMakeLists.txt @@ -22,6 +22,10 @@ qt_internal_add_test(tst_qtqmlmodules TESTDATA ${test_data} ) +if(QT_BUILD_STANDALONE_TESTS) + qt_import_qml_plugins(tst_qtqmlmodules) +endif() + ## Scopes: ##################################################################### diff --git a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp index e51cca9be4..d3cf585bb9 100644 --- a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp +++ b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp @@ -30,6 +30,7 @@ #include <qtest.h> #include <QQmlEngine> #include <private/qv4identifiertable_p.h> +#include <private/qv4engine_p.h> class tst_qv4identifiertable : public QObject { diff --git a/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml index f7e708e7aa..163aaa66f7 100644 --- a/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml +++ b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml @@ -40,8 +40,7 @@ Item { when: imageOnDisk.ready && imageOnDiskSmall.ready function test_endresult_disk() { - if ((Qt.platform.pluginName === "offscreen") - || (Qt.platform.pluginName === "minimal")) + if (Qt.platform.pluginName === "minimal") skip("grabImage does not work on offscreen/minimal platforms"); var image = grabImage(root); @@ -77,8 +76,7 @@ Item { } function test_endresult_cache(data) { - if ((Qt.platform.pluginName === "offscreen") - || (Qt.platform.pluginName === "minimal")) + if (Qt.platform.pluginName === "minimal") skip("grabImage does not work on offscreen/minimal platforms"); imageInCache.cache = data.cache; diff --git a/tests/auto/quick/CMakeLists.txt b/tests/auto/quick/CMakeLists.txt index de701f909d..03b9829b68 100644 --- a/tests/auto/quick/CMakeLists.txt +++ b/tests/auto/quick/CMakeLists.txt @@ -80,6 +80,7 @@ if(QT_FEATURE_private_tests) add_subdirectory(qquickshadereffect) add_subdirectory(qquickanimatedsprite) add_subdirectory(qquickspritesequence) + add_subdirectory(qquickitemrhiintegration) if(QT_FEATURE_opengl) add_subdirectory(qquickframebufferobject) diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp index b26a055e60..99e429c369 100644 --- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp @@ -38,6 +38,7 @@ #include <QtQml/qqmlengine.h> #include <QtQml/qqmlproperty.h> +#include <QQmlComponent> #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/viewtestutils_p.h> diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp index 6296eb4da8..2120c0041d 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp @@ -37,6 +37,7 @@ #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/viewtestutils_p.h> +#include <QQmlComponent> Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") diff --git a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp index 136703d517..8f2ba0021c 100644 --- a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp +++ b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp @@ -48,6 +48,8 @@ #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/visualtestutils_p.h> +#include <QQmlComponent> + #define EXPECT(cond) \ do { \ if (!errorAt && !(cond)) { \ @@ -147,17 +149,17 @@ void tst_QQuickAccessible::commonTests() qDebug() << "testing" << accessibleRoleFileName; - QQuickView *view = new QQuickView(); + auto view = std::make_unique<QQuickView>(); // view->setFixedSize(240,320); view->setSource(testFileUrl(accessibleRoleFileName)); view->show(); // view->setFocus(); QVERIFY(view->rootObject() != nullptr); - QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(view); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(view.get()); QVERIFY(iface); - delete view; + view.reset(); QTestAccessibility::clearEvents(); } @@ -168,12 +170,11 @@ void tst_QQuickAccessible::quickAttachedProperties() QQmlComponent component(&engine); component.setData("import QtQuick 2.0\nItem {\n" "}", QUrl()); - QObject *object = component.create(); + auto object = std::unique_ptr<QObject>(component.create()); QVERIFY(object != nullptr); - QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(object); + QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(object.get()); QCOMPARE(attachedObject, static_cast<QObject*>(nullptr)); - delete object; } // Attaching to non-item @@ -202,11 +203,11 @@ void tst_QQuickAccessible::quickAttachedProperties() component.setData("import QtQuick 2.0\nItem {\n" "Accessible.role: Accessible.Button\n" "}", QUrl()); - QObject *object = component.create(); + auto object = std::unique_ptr<QObject>(component.create()); QVERIFY(object != nullptr); const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>( - QQuickAccessibleAttached::attachedProperties(object)); + QQuickAccessibleAttached::attachedProperties(object.get())); QVERIFY(attachedObject); if (attachedObject) { QVariant p = attachedObject->property("role"); @@ -220,7 +221,6 @@ void tst_QQuickAccessible::quickAttachedProperties() QVERIFY2(p.value<QString>().isEmpty(), QTest::toString(p)); QCOMPARE(attachedObject->wasNameExplicitlySet(), false); } - delete object; } // Attached property @@ -232,11 +232,11 @@ void tst_QQuickAccessible::quickAttachedProperties() "Accessible.name: \"Donald\"\n" "Accessible.description: \"Duck\"\n" "}", QUrl()); - QObject *object = component.create(); + auto object = std::unique_ptr<QObject>(component.create()); QVERIFY(object != nullptr); const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>( - QQuickAccessibleAttached::attachedProperties(object)); + QQuickAccessibleAttached::attachedProperties(object.get())); QVERIFY(attachedObject); if (attachedObject) { QVariant p = attachedObject->property("role"); @@ -250,7 +250,6 @@ void tst_QQuickAccessible::quickAttachedProperties() QCOMPARE(p.toString(), QLatin1String("Duck")); QCOMPARE(attachedObject->wasNameExplicitlySet(), true); } - delete object; } // Check overriding of attached role for Text @@ -262,10 +261,10 @@ void tst_QQuickAccessible::quickAttachedProperties() "Accessible.name: \"TextButton\"\n" "Accessible.description: \"Text Button\"\n" "}", QUrl()); - QObject *object = component.create(); + auto object = std::unique_ptr<QObject>(component.create()); QVERIFY(object != nullptr); - QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(object); + QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(object.get()); QVERIFY(attachedObject); if (attachedObject) { QVariant p = attachedObject->property("role"); @@ -278,7 +277,6 @@ void tst_QQuickAccessible::quickAttachedProperties() QCOMPARE(p.isNull(), false); QCOMPARE(p.toString(), QLatin1String("Text Button")); } - delete object; } // Check overriding of attached role for Text { @@ -294,10 +292,10 @@ void tst_QQuickAccessible::quickAttachedProperties() "Accessible.description: \"Text Button\"\n" "}\n" "}", QUrl()); - QObject *object = component.create(); + auto object = std::unique_ptr<QObject>(component.create()); QVERIFY(object != nullptr); - QQuickListView *listview = qobject_cast<QQuickListView *>(object); + QQuickListView *listview = qobject_cast<QQuickListView *>(object.get()); QVERIFY(listview != nullptr); QQuickItem *contentItem = listview->contentItem(); QQuickText *childItem = QQuickVisualTestUtils::findItem<QQuickText>(contentItem, "acc_text"); @@ -316,7 +314,6 @@ void tst_QQuickAccessible::quickAttachedProperties() QCOMPARE(p.isNull(), false); QCOMPARE(p.toString(), QLatin1String("Text Button")); } - delete object; } // Check that a name can be implicitly set. { @@ -328,11 +325,11 @@ void tst_QQuickAccessible::quickAttachedProperties() Accessible.role: Accessible.Button Accessible.description: "Text Button" })", QUrl()); - QScopedPointer<QObject> object(component.create()); + auto object = std::unique_ptr<QObject>(component.create()); QVERIFY(object); const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>( - QQuickAccessibleAttached::attachedProperties(object.data())); + QQuickAccessibleAttached::attachedProperties(object.get())); QVERIFY(attachedObject); QVERIFY(!attachedObject->wasNameExplicitlySet()); @@ -353,12 +350,12 @@ void tst_QQuickAccessible::basicPropertiesTest() QAccessibleInterface *app = QAccessible::queryAccessibleInterface(qApp); QCOMPARE(app->childCount(), 0); - QQuickView *window = new QQuickView(); + auto window = std::make_unique<QQuickView>(); window->setSource(testFileUrl("text.qml")); window->show(); QCOMPARE(app->childCount(), 1); - QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.get()); QVERIFY(iface); QCOMPARE(iface->childCount(), 1); @@ -392,6 +389,7 @@ void tst_QQuickAccessible::basicPropertiesTest() QCOMPARE(item->indexOfChild(text2), 1); QVERIFY(!text2->state().editable); QVERIFY(text2->state().readOnly); + QVERIFY(text2->state().focusable); QCOMPARE(iface->indexOfChild(text2), -1); QCOMPARE(text2->indexOfChild(item), -1); @@ -483,7 +481,7 @@ void tst_QQuickAccessible::basicPropertiesTest() attached->setRole(QAccessible::StaticText); QVERIFY(!text3->state().readOnly); - delete window; + window.reset(); QTestAccessibility::clearEvents(); } @@ -502,14 +500,15 @@ QAccessibleInterface *topLevelChildAt(QAccessibleInterface *iface, int x, int y) void tst_QQuickAccessible::hitTest() { - QQuickView *window = new QQuickView; + auto window = std::make_unique<QQuickView>(); window->setSource(testFileUrl("hittest.qml")); window->show(); - QAccessibleInterface *windowIface = QAccessible::queryAccessibleInterface(window); + QAccessibleInterface *windowIface = QAccessible::queryAccessibleInterface(window.get()); QVERIFY(windowIface); QAccessibleInterface *rootItem = windowIface->child(0); - QRect rootRect = rootItem->rect(); + // on Android the main window is always shown fullscreen + QRect rootRect = QRect(window->x(), window->y(), window->width(), window->height()); // check the root item from app QAccessibleInterface *appIface = QAccessible::queryAccessibleInterface(qApp); @@ -555,13 +554,13 @@ void tst_QQuickAccessible::hitTest() } } - delete window; + window.reset(); QTestAccessibility::clearEvents(); } void tst_QQuickAccessible::checkableTest() { - QScopedPointer<QQuickView> window(new QQuickView()); + auto window = std::make_unique<QQuickView>(); window->setSource(testFileUrl("checkbuttons.qml")); window->show(); @@ -574,7 +573,7 @@ void tst_QQuickAccessible::checkableTest() QAccessible::State activatedChange; activatedChange.active = true; - QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.data()); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.get()); QVERIFY(iface); QAccessibleInterface *root = iface->child(0); @@ -638,7 +637,7 @@ void tst_QQuickAccessible::checkableTest() void tst_QQuickAccessible::ignoredTest() { - QScopedPointer<QQuickView> window(new QQuickView()); + auto window = std::make_unique<QQuickView>(); window->setSource(testFileUrl("ignored.qml")); window->show(); @@ -651,7 +650,7 @@ void tst_QQuickAccessible::ignoredTest() QAccessible::State activatedChange; activatedChange.active = true; - QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.data()); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.get()); QVERIFY(iface); QAccessibleInterface *rectangleA = iface->child(0); diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp index e2bac5ee39..2820762114 100644 --- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp +++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp @@ -1863,9 +1863,8 @@ void tst_qquickanimations::fastFlickingBug() void tst_qquickanimations::opacityAnimationFromZero() { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); // not easy to verify this in threaded render loop // since it's difficult to capture the first frame when scene graph diff --git a/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp b/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp index f1cfa05d38..72bb774a74 100644 --- a/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp +++ b/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp @@ -39,6 +39,7 @@ #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/viewtestutils_p.h> +#include <QQmlComponent> using namespace QQuickViewTestUtils; diff --git a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp index 133583e72c..020097b9ef 100644 --- a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp +++ b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp @@ -37,6 +37,7 @@ #include <qpa/qplatformintegration.h> #include <private/qguiapplication_p.h> #include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QFont> class tst_qquickapplication : public QQmlDataTest { diff --git a/tests/auto/quick/qquickbehaviors/bindable.h b/tests/auto/quick/qquickbehaviors/bindable.h index ebdf8711a3..9bc01c4568 100644 --- a/tests/auto/quick/qquickbehaviors/bindable.h +++ b/tests/auto/quick/qquickbehaviors/bindable.h @@ -31,6 +31,8 @@ #include <QObject> #include <QQuickItem> #include <qqmlregistration.h> +#include <QBindable> +#include <qproperty.h> class TestBindable : public QQuickItem { diff --git a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp index e963082a1a..7ce44ee9dc 100644 --- a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp +++ b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp @@ -591,9 +591,8 @@ void tst_qquickborderimage::progressAndStatusChanges() #if QT_CONFIG(opengl) void tst_qquickborderimage::borderImageMesh() { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QQuickView *window = new QQuickView; @@ -625,9 +624,8 @@ void tst_qquickborderimage::multiFrame_data() void tst_qquickborderimage::multiFrame() { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QFETCH(QString, qmlfile); QFETCH(bool, asynchronous); diff --git a/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp b/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp index 3e9069d522..348948f5f6 100644 --- a/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp +++ b/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp @@ -33,6 +33,7 @@ #include <QtQml/qqmlcontext.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlexpression.h> +#include <QQmlComponent> template <typename T> static T evaluate(QObject *scope, const QString &expression) { diff --git a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp index de7e3f1629..b7a7010a57 100644 --- a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp +++ b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp @@ -31,6 +31,7 @@ #include <QtGui/qstylehints.h> #include <QtQuick/qquickitem.h> #include <QtQuick/qquickview.h> +#include <QtQml/QQmlComponent> #include <QtQml/qqmlcontext.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlexpression.h> diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 315b6d4fca..2e790dad08 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -1663,7 +1663,8 @@ void tst_qquickflickable::cancelOnMouseGrab() QQuickItem *item = window->rootObject()->findChild<QQuickItem*>("row"); auto mouse = QPointingDevice::primaryPointingDevice(); auto mousePriv = QPointingDevicePrivate::get(const_cast<QPointingDevice *>(mouse)); - QMouseEvent fakeMouseEv(QEvent::MouseMove, QPoint(130, 100), Qt::NoButton, Qt::LeftButton, Qt::NoModifier, mouse); + QMouseEvent fakeMouseEv(QEvent::MouseMove, QPoint(130, 100), QPoint(130, 100), + Qt::NoButton, Qt::LeftButton, Qt::NoModifier, mouse); mousePriv->setExclusiveGrabber(&fakeMouseEv, fakeMouseEv.points().first(), item); QTRY_COMPARE(flickable->contentX(), 0.); diff --git a/tests/auto/quick/qquickframebufferobject/BLACKLIST b/tests/auto/quick/qquickframebufferobject/BLACKLIST deleted file mode 100644 index de1bb7ad6d..0000000000 --- a/tests/auto/quick/qquickframebufferobject/BLACKLIST +++ /dev/null @@ -1,8 +0,0 @@ -[testInvalidate) -# QTBUG-65614 -b2qt -qnx - -[testThatStuffWorks] -b2qt -qnx diff --git a/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp b/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp index 19eeb5c60d..b69c3f7b96 100644 --- a/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp +++ b/tests/auto/quick/qquickframebufferobject/tst_qquickframebufferobject.cpp @@ -28,6 +28,7 @@ #include <qtest.h> +#include <QGuiApplication> #include <QtQuick/qquickitem.h> #include <QtQuick/qquickview.h> #include <qopenglcontext.h> @@ -209,6 +210,10 @@ void tst_QQuickFramebufferObject::testThatStuffWorks() view.requestActivate(); QVERIFY(QTest::qWaitForWindowExposed(&view)); + if (QGuiApplication::platformName() == "offscreen" && + view.rendererInterface()->graphicsApi() == QSGRendererInterface::Software) + QSKIP("offscreen software rendering doesn't work with FBOs"); + QImage result = view.grabWindow(); QCOMPARE(frameInfo.renderCount, 1); @@ -249,6 +254,10 @@ void tst_QQuickFramebufferObject::testInvalidate() view.requestActivate(); QVERIFY(QTest::qWaitForWindowExposed(&view)); + if (QGuiApplication::platformName() == "offscreen" && + view.rendererInterface()->graphicsApi() == QSGRendererInterface::Software) + QSKIP("offscreen software rendering doesn't work with FBOs"); + QCOMPARE(frameInfo.fboSize, QSize(200, 200)); frameInfo.createFBOCount = 0; diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp index 36c4b60718..37b3100e78 100644 --- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp +++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp @@ -340,9 +340,8 @@ void tst_qquickimage::smooth() void tst_qquickimage::mirror() { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QMap<QQuickImage::FillMode, QImage> screenshots; QList<QQuickImage::FillMode> fillModes; @@ -555,9 +554,8 @@ void tst_qquickimage::big() void tst_qquickimage::tiling_QTBUG_6716() { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QFETCH(QString, source); @@ -935,8 +933,7 @@ void tst_qquickimage::sourceClipRect() QCOMPARE(image->implicitWidth(), sourceClipRect.isNull() ? 300 : sourceClipRect.width()); QCOMPARE(image->implicitHeight(), sourceClipRect.isNull() ? 300 : sourceClipRect.height()); - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) + if (QGuiApplication::platformName() == QLatin1String("minimal")) QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); QImage contents = toUnscaledImage(window->grabWindow()); if (contents.width() < sourceClipRect.width()) @@ -1164,9 +1161,8 @@ void tst_qquickimage::highDpiFillModesAndSizes() void tst_qquickimage::hugeImages() { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QQuickView view; view.setSource(testFileUrl("hugeImages.qml")); @@ -1223,9 +1219,8 @@ void tst_qquickimage::multiFrame_data() void tst_qquickimage::multiFrame() { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QFETCH(QString, qmlfile); QFETCH(bool, asynchronous); diff --git a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp index 1a479f7c0c..e0e2c9daac 100644 --- a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp +++ b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp @@ -34,6 +34,7 @@ #include <QWaitCondition> #include <QThreadPool> #include <private/qqmlengine_p.h> +#include <QQmlComponent> Q_DECLARE_METATYPE(QQuickImageProvider*); diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp index e4fe6df888..490933b06f 100644 --- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp @@ -28,6 +28,7 @@ #include <qtest.h> +#include <QtQml/QQmlComponent> #include <QtQuick/qquickitem.h> #include <QtQuick/qquickwindow.h> #include <QtQuick/qquickview.h> diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index 91ec08e854..623eb26dda 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -38,6 +38,7 @@ #include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/private/qquicktextinput_p.h> #include <QtQuick/private/qquickitemchangelistener_p.h> +#include <QtQuick/private/qquickanchors_p.h> #include <QtGui/qstylehints.h> #include <private/qquickitem_p.h> #include <QtQuickTestUtils/private/qmlutils_p.h> @@ -3675,9 +3676,8 @@ void tst_QQuickItem::childAt() void tst_QQuickItem::grab() { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabToImage not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabToImage not functional on minimal platforms"); QQuickView view; view.setSource(testFileUrl("grabToImage.qml")); diff --git a/tests/auto/quick/qquickitemrhiintegration/CMakeLists.txt b/tests/auto/quick/qquickitemrhiintegration/CMakeLists.txt new file mode 100644 index 0000000000..0076d7f82b --- /dev/null +++ b/tests/auto/quick/qquickitemrhiintegration/CMakeLists.txt @@ -0,0 +1,36 @@ +qt_internal_add_test(tst_qquickitemrhiintegration + SOURCES + tst_qquickitemrhiintegration.cpp + rhiitem.cpp rhiitem.h rhiitem_p.h + testrhiitem.cpp testrhiitem.h + PUBLIC_LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + Qt::QmlPrivate + Qt::QuickPrivate + Qt::QuickTestUtilsPrivate +) + +qt_internal_extend_target(tst_qquickitemrhiintegration CONDITION ANDROID OR IOS + DEFINES + QT_QMLTEST_DATADIR=\\\":/data\\\" +) + +qt_internal_extend_target(tst_qquickitemrhiintegration CONDITION NOT ANDROID AND NOT IOS + DEFINES + QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" +) + +qt_internal_add_shaders(tst_qquickitemrhiintegration "shaders" + PREFIX + "/" + FILES + "texture.vert" + "texture.frag" +) + +qt_add_qml_module(tst_qquickitemrhiintegration + URI Test + VERSION 1.0 +) diff --git a/tests/auto/quick/qquickitemrhiintegration/data/test.qml b/tests/auto/quick/qquickitemrhiintegration/data/test.qml new file mode 100644 index 0000000000..1262936761 --- /dev/null +++ b/tests/auto/quick/qquickitemrhiintegration/data/test.qml @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** 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 +import Test + +Item { + width: 640 + height: 480 + + Text { + id: apiInfo + color: "black" + font.pixelSize: 16 + property int api: GraphicsInfo.api + text: { + if (GraphicsInfo.api === GraphicsInfo.OpenGLRhi) + "OpenGL on QRhi"; + else if (GraphicsInfo.api === GraphicsInfo.Direct3D11Rhi) + "D3D11 on QRhi"; + else if (GraphicsInfo.api === GraphicsInfo.VulkanRhi) + "Vulkan on QRhi"; + else if (GraphicsInfo.api === GraphicsInfo.MetalRhi) + "Metal on QRhi"; + else if (GraphicsInfo.api === GraphicsInfo.Null) + "Null on QRhi"; + else + "Unknown API"; + } + } + + TestRhiItem { + anchors.centerIn: parent + width: 400 + height: 400 + color: "red" + } +} diff --git a/tests/auto/quick/qquickitemrhiintegration/rhiitem.cpp b/tests/auto/quick/qquickitemrhiintegration/rhiitem.cpp new file mode 100644 index 0000000000..5bfd3a850d --- /dev/null +++ b/tests/auto/quick/qquickitemrhiintegration/rhiitem.cpp @@ -0,0 +1,377 @@ +/**************************************************************************** +** +** 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 "rhiitem_p.h" + +RhiItemNode::RhiItemNode(RhiItem *item) + : m_item(item) +{ + m_window = m_item->window(); + Q_ASSERT(m_window); + connect(m_window, &QQuickWindow::beforeRendering, this, &RhiItemNode::render); + connect(m_window, &QQuickWindow::screenChanged, this, [this]() { + if (m_window->effectiveDevicePixelRatio() != m_dpr) + m_item->update(); + }); +} + +RhiItemNode::~RhiItemNode() +{ + delete m_renderer; + delete m_sgWrapperTexture; + releaseNativeTexture(); +} + +QSGTexture *RhiItemNode::texture() const +{ + return m_sgWrapperTexture; +} + +void RhiItemNode::createNativeTexture() +{ + Q_ASSERT(!m_texture); + Q_ASSERT(!m_pixelSize.isEmpty()); + + m_texture = m_rhi->newTexture(QRhiTexture::RGBA8, m_pixelSize, 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource); + if (!m_texture->create()) { + qWarning("Failed to create RhiItem texture of size %dx%d", m_pixelSize.width(), m_pixelSize.height()); + delete m_texture; + m_texture = nullptr; + } +} + +void RhiItemNode::releaseNativeTexture() +{ + if (m_texture) { + m_texture->deleteLater(); + m_texture = nullptr; + } +} + +void RhiItemNode::sync() +{ + if (!m_rhi) { + QSGRendererInterface *rif = m_window->rendererInterface(); + m_rhi = static_cast<QRhi *>(rif->getResource(m_window, QSGRendererInterface::RhiResource)); + if (!m_rhi) { + qWarning("No QRhi found for window %p, RhiItem will not be functional", m_window); + return; + } + } + + QSize newSize(m_item->explicitTextureWidth(), m_item->explicitTextureHeight()); + if (newSize.isEmpty()) { + m_dpr = m_window->effectiveDevicePixelRatio(); + const int minTexSize = m_rhi->resourceLimit(QRhi::TextureSizeMin); + newSize = QSize(qMax<int>(minTexSize, m_item->width()), + qMax<int>(minTexSize, m_item->height())) * m_dpr; + } + + bool needsNew = !m_sgWrapperTexture; + if (newSize != m_pixelSize) { + needsNew = true; + m_pixelSize = newSize; + } + + if (needsNew) { + if (m_texture && m_sgWrapperTexture) { + m_texture->setPixelSize(m_pixelSize); + if (m_texture->create()) + m_sgWrapperTexture->setTextureSize(m_pixelSize); + else + qWarning("Failed to recreate RhiItem texture of size %dx%d", m_pixelSize.width(), m_pixelSize.height()); + } else { + delete m_sgWrapperTexture; + m_sgWrapperTexture = nullptr; + releaseNativeTexture(); + createNativeTexture(); + if (m_texture) { + m_sgWrapperTexture = new QSGPlainTexture; + m_sgWrapperTexture->setOwnsTexture(false); + m_sgWrapperTexture->setTexture(m_texture); + m_sgWrapperTexture->setTextureSize(m_pixelSize); + m_sgWrapperTexture->setHasAlphaChannel(m_item->alphaBlending()); + setTexture(m_sgWrapperTexture); + } + } + RhiItemPrivate::get(m_item)->effectiveTextureSize = m_pixelSize; + emit m_item->effectiveTextureSizeChanged(); + if (m_texture) + m_renderer->initialize(m_rhi, m_texture); + } + + if (m_sgWrapperTexture && m_sgWrapperTexture->hasAlphaChannel() != m_item->alphaBlending()) { + m_sgWrapperTexture->setHasAlphaChannel(m_item->alphaBlending()); + // hasAlphaChannel is mapped to QSGMaterial::Blending in setTexture() so that has to be called again + setTexture(m_sgWrapperTexture); + } + + m_renderer->synchronize(m_item); +} + +void RhiItemNode::render() +{ + // called before Qt Quick starts recording its main render pass + + if (!m_rhi || !m_texture || !m_renderer) + return; + + if (!m_renderPending) + return; + + QSGRendererInterface *rif = m_window->rendererInterface(); + QRhiCommandBuffer *cb = nullptr; + QRhiSwapChain *swapchain = static_cast<QRhiSwapChain *>( + rif->getResource(m_window, QSGRendererInterface::RhiSwapchainResource)); + cb = swapchain ? swapchain->currentFrameCommandBuffer() + : static_cast<QRhiCommandBuffer *>(rif->getResource(m_window, QSGRendererInterface::RhiRedirectCommandBuffer)); + if (!cb) { + qWarning("Neither swapchain nor redirected command buffer are available."); + return; + } + + m_renderPending = false; + m_renderer->render(cb); + + markDirty(QSGNode::DirtyMaterial); + emit textureChanged(); +} + +void RhiItemNode::scheduleUpdate() +{ + m_renderPending = true; + m_window->update(); // ensure getting to beforeRendering() at some point +} + +void RhiItemNode::setMirrorVertically(bool b) +{ + if (m_rhi->isYUpInFramebuffer()) + b = !b; + + setTextureCoordinatesTransform(b ? QSGSimpleTextureNode::MirrorVertically : QSGSimpleTextureNode::NoTransform); +} + +RhiItem::RhiItem(QQuickItem *parent) + : QQuickItem(*new RhiItemPrivate, parent) +{ + setFlag(ItemHasContents); +} + +QSGNode *RhiItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) +{ + Q_D(RhiItem); + RhiItemNode *n = static_cast<RhiItemNode *>(node); + + // Changing to an empty size should not involve destroying and then later + // recreating the node, because we do not know how expensive the user's + // renderer setup is. Rather, keep the node if it already exist, and clamp + // all accesses to width and height. Hence the unusual !n condition here. + if (!n && (width() <= 0 || height() <= 0)) + return nullptr; + + if (!n) { + if (!d->node) + d->node = new RhiItemNode(this); + n = d->node; + } + + if (!n->hasRenderer()) { + RhiItemRenderer *r = createRenderer(); + if (r) { + r->data = n; + n->setRenderer(r); + } else { + qWarning("No RhiItemRenderer was created; the item will not render"); + delete n; + d->node = nullptr; + return nullptr; + } + } + + n->sync(); + + if (!n->isValid()) { + delete n; + d->node = nullptr; + return nullptr; + } + + n->setMirrorVertically(d->mirrorVertically); + n->setFiltering(QSGTexture::Linear); + n->setRect(0, 0, qMax<int>(0, width()), qMax<int>(0, height())); + + n->scheduleUpdate(); + + return n; +} + +void RhiItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + QQuickItem::geometryChange(newGeometry, oldGeometry); + if (newGeometry.size() != oldGeometry.size()) + update(); +} + +void RhiItem::releaseResources() +{ + // called on the gui thread if the item is removed from scene + + Q_D(RhiItem); + d->node = nullptr; +} + +void RhiItem::invalidateSceneGraph() +{ + // called on the render thread when the scenegraph is invalidated + + Q_D(RhiItem); + d->node = nullptr; +} + +bool RhiItem::isTextureProvider() const +{ + return true; +} + +QSGTextureProvider *RhiItem::textureProvider() const +{ + if (QQuickItem::isTextureProvider()) // e.g. if Item::layer::enabled == true + return QQuickItem::textureProvider(); + + QQuickWindow *w = window(); + if (!w || !w->isSceneGraphInitialized() || QThread::currentThread() != QQuickWindowPrivate::get(w)->context->thread()) { + qWarning("RhiItem::textureProvider: can only be queried on the rendering thread of an exposed window"); + return nullptr; + } + + Q_D(const RhiItem); + if (!d->node) // create a node to have a provider, the texture will be null but that's ok + d->node = new RhiItemNode(const_cast<RhiItem *>(this)); + + return d->node; +} + +int RhiItem::explicitTextureWidth() const +{ + Q_D(const RhiItem); + return d->explicitTextureWidth; +} + +void RhiItem::setExplicitTextureWidth(int w) +{ + Q_D(RhiItem); + if (d->explicitTextureWidth == w) + return; + + d->explicitTextureWidth = w; + emit explicitTextureWidthChanged(); + update(); +} + +int RhiItem::explicitTextureHeight() const +{ + Q_D(const RhiItem); + return d->explicitTextureHeight; +} + +void RhiItem::setExplicitTextureHeight(int h) +{ + Q_D(RhiItem); + if (d->explicitTextureHeight == h) + return; + + d->explicitTextureHeight = h; + emit explicitTextureHeightChanged(); + update(); +} + +QSize RhiItem::effectiveTextureSize() const +{ + Q_D(const RhiItem); + return d->effectiveTextureSize; +} + +bool RhiItem::alphaBlending() const +{ + Q_D(const RhiItem); + return d->blend; +} + +void RhiItem::setAlphaBlending(bool enable) +{ + Q_D(RhiItem); + if (d->blend == enable) + return; + + d->blend = enable; + emit alphaBlendingChanged(); + update(); +} + +bool RhiItem::mirrorVertically() const +{ + Q_D(const RhiItem); + return d->mirrorVertically; +} + +void RhiItem::setMirrorVertically(bool enable) +{ + Q_D(RhiItem); + if (d->mirrorVertically == enable) + return; + + d->mirrorVertically = enable; + emit mirrorVerticallyChanged(); + update(); +} + +void RhiItemRenderer::update() +{ + if (data) + static_cast<RhiItemNode *>(data)->scheduleUpdate(); +} + +RhiItemRenderer::~RhiItemRenderer() +{ +} + +void RhiItemRenderer::initialize(QRhi *rhi, QRhiTexture *outputTexture) +{ + Q_UNUSED(rhi); + Q_UNUSED(outputTexture); +} + +void RhiItemRenderer::synchronize(RhiItem *item) +{ + Q_UNUSED(item); +} + +void RhiItemRenderer::render(QRhiCommandBuffer *cb) +{ + Q_UNUSED(cb); +} diff --git a/tests/auto/quick/qquickitemrhiintegration/rhiitem.h b/tests/auto/quick/qquickitemrhiintegration/rhiitem.h new file mode 100644 index 0000000000..32de7922a0 --- /dev/null +++ b/tests/auto/quick/qquickitemrhiintegration/rhiitem.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** 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 RHIITEM_H +#define RHIITEM_H + +#include <QQuickItem> +#include <QtGui/private/qrhi_p.h> + +class RhiItem; +class RhiItemPrivate; + +class RhiItemRenderer +{ +public: + virtual ~RhiItemRenderer(); + virtual void initialize(QRhi *rhi, QRhiTexture *outputTexture); + virtual void synchronize(RhiItem *item); + virtual void render(QRhiCommandBuffer *cb); + + void update(); + +private: + void *data; + friend class RhiItem; +}; + +class RhiItem : public QQuickItem +{ + Q_OBJECT + Q_DECLARE_PRIVATE(RhiItem) + + Q_PROPERTY(int explicitTextureWidth READ explicitTextureWidth WRITE setExplicitTextureWidth NOTIFY explicitTextureWidthChanged) + Q_PROPERTY(int explicitTextureHeight READ explicitTextureHeight WRITE setExplicitTextureHeight NOTIFY explicitTextureHeightChanged) + Q_PROPERTY(QSize effectiveTextureSize READ effectiveTextureSize NOTIFY effectiveTextureSizeChanged) + Q_PROPERTY(bool alphaBlending READ alphaBlending WRITE setAlphaBlending NOTIFY alphaBlendingChanged) + Q_PROPERTY(bool mirrorVertically READ mirrorVertically WRITE setMirrorVertically NOTIFY mirrorVerticallyChanged) + +public: + RhiItem(QQuickItem *parent = nullptr); + + virtual RhiItemRenderer *createRenderer() = 0; + + int explicitTextureWidth() const; + void setExplicitTextureWidth(int w); + int explicitTextureHeight() const; + void setExplicitTextureHeight(int h); + + QSize effectiveTextureSize() const; + + bool alphaBlending() const; + void setAlphaBlending(bool enable); + + bool mirrorVertically() const; + void setMirrorVertically(bool enable); + +protected: + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override; + void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void releaseResources() override; + bool isTextureProvider() const override; + QSGTextureProvider *textureProvider() const override; + +Q_SIGNALS: + void explicitTextureWidthChanged(); + void explicitTextureHeightChanged(); + void effectiveTextureSizeChanged(); + void alphaBlendingChanged(); + void mirrorVerticallyChanged(); + +private Q_SLOTS: + void invalidateSceneGraph(); +}; + +#endif diff --git a/tests/auto/quick/qquickitemrhiintegration/rhiitem_p.h b/tests/auto/quick/qquickitemrhiintegration/rhiitem_p.h new file mode 100644 index 0000000000..00ffe9cb59 --- /dev/null +++ b/tests/auto/quick/qquickitemrhiintegration/rhiitem_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** 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 RHIITEM_P_H +#define RHIITEM_P_H + +#include "rhiitem.h" +#include <QSGSimpleTextureNode> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qsgplaintexture_p.h> + +class RhiItemNode : public QSGTextureProvider, public QSGSimpleTextureNode +{ + Q_OBJECT + +public: + RhiItemNode(RhiItem *item); + ~RhiItemNode(); + + QSGTexture *texture() const override; + + void sync(); + bool isValid() const { return m_rhi && m_texture && m_sgWrapperTexture; } + void scheduleUpdate(); + bool hasRenderer() const { return m_renderer; } + void setRenderer(RhiItemRenderer *r) { m_renderer = r; } + void setMirrorVertically(bool b); + +private slots: + void render(); + +private: + void createNativeTexture(); + void releaseNativeTexture(); + + RhiItem *m_item; + QQuickWindow *m_window; + QSize m_pixelSize; + qreal m_dpr = 0.0f; + QRhi *m_rhi = nullptr; + QRhiTexture *m_texture = nullptr; + QSGPlainTexture *m_sgWrapperTexture = nullptr; + bool m_renderPending = true; + RhiItemRenderer *m_renderer = nullptr; +}; + +class RhiItemPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(RhiItem) +public: + static RhiItemPrivate *get(RhiItem *item) { return item->d_func(); } + mutable RhiItemNode *node = nullptr; + int explicitTextureWidth = 0; + int explicitTextureHeight = 0; + bool blend = true; + bool mirrorVertically = false; + QSize effectiveTextureSize; +}; + +#endif diff --git a/tests/auto/quick/qquickitemrhiintegration/testrhiitem.cpp b/tests/auto/quick/qquickitemrhiintegration/testrhiitem.cpp new file mode 100644 index 0000000000..c5a83f3f65 --- /dev/null +++ b/tests/auto/quick/qquickitemrhiintegration/testrhiitem.cpp @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** 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 "testrhiitem.h" +#include <QFile> + +static const QSize TEX_SIZE(512, 512); + +void TestRenderer::initialize(QRhi *rhi, QRhiTexture *outputTexture) +{ + m_rhi = rhi; + m_output = outputTexture; + + if (!m_ds) { + m_ds.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, m_output->pixelSize())); + m_ds->create(); + } else if (m_ds->pixelSize() != m_output->pixelSize()) { + m_ds->setPixelSize(m_output->pixelSize()); + m_ds->create(); + } + + if (!m_rt) { + m_rt.reset(m_rhi->newTextureRenderTarget({ { m_output }, m_ds.data() })); + m_rp.reset(m_rt->newCompatibleRenderPassDescriptor()); + m_rt->setRenderPassDescriptor(m_rp.data()); + m_rt->create(); + } + + if (!scene.vbuf) + initScene(); + + const QSize outputSize = m_output->pixelSize(); + scene.mvp = m_rhi->clipSpaceCorrMatrix(); + scene.mvp.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 1000.0f); + scene.mvp.translate(0, 0, -4); + if (!scene.resourceUpdates) + scene.resourceUpdates = m_rhi->nextResourceUpdateBatch(); + scene.resourceUpdates->updateDynamicBuffer(scene.ubuf.data(), 0, 64, scene.mvp.constData()); +} + +static QShader getShader(const QString &name) +{ + QFile f(name); + if (f.open(QIODevice::ReadOnly)) + return QShader::fromSerialized(f.readAll()); + + return QShader(); +} + +void TestRenderer::updateTexture() +{ + QImage img(TEX_SIZE, QImage::Format_RGBA8888); + img.fill(itemData.color); + + if (!scene.resourceUpdates) + scene.resourceUpdates = m_rhi->nextResourceUpdateBatch(); + + scene.resourceUpdates->uploadTexture(scene.tex.data(), img); +} + +void TestRenderer::initScene() +{ + static const float tri[] = { + 0.0f, 0.5f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.0f, 0.0f, + 0.5f, -0.5f, 1.0f, 0.0f + }; + + scene.vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(tri))); + scene.vbuf->create(); + + scene.resourceUpdates = m_rhi->nextResourceUpdateBatch(); + scene.resourceUpdates->uploadStaticBuffer(scene.vbuf.data(), tri); + + scene.ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68)); + scene.ubuf->create(); + + const qint32 flip = m_rhi->isYUpInFramebuffer() ? 1 : 0; + scene.resourceUpdates->updateDynamicBuffer(scene.ubuf.data(), 64, 4, &flip); + + scene.tex.reset(m_rhi->newTexture(QRhiTexture::RGBA8, TEX_SIZE)); + scene.tex->create(); + + scene.sampler.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, + QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge)); + scene.sampler->create(); + + scene.srb.reset(m_rhi->newShaderResourceBindings()); + scene.srb->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, scene.ubuf.data()), + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, scene.tex.data(), scene.sampler.data()) + }); + scene.srb->create(); + + scene.ps.reset(m_rhi->newGraphicsPipeline()); + scene.ps->setDepthTest(true); + scene.ps->setDepthWrite(true); + scene.ps->setDepthOp(QRhiGraphicsPipeline::Less); + scene.ps->setCullMode(QRhiGraphicsPipeline::Back); + scene.ps->setFrontFace(QRhiGraphicsPipeline::CCW); + QShader vs = getShader(QLatin1String(":/texture.vert.qsb")); + Q_ASSERT(vs.isValid()); + QShader fs = getShader(QLatin1String(":/texture.frag.qsb")); + Q_ASSERT(fs.isValid()); + scene.ps->setShaderStages({ + { QRhiShaderStage::Vertex, vs }, + { QRhiShaderStage::Fragment, fs } + }); + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ + { 4 * sizeof(float) }, + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float2, 0 }, + { 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) } + }); + scene.ps->setVertexInputLayout(inputLayout); + scene.ps->setShaderResourceBindings(scene.srb.data()); + scene.ps->setRenderPassDescriptor(m_rp.data()); + scene.ps->create(); +} + +void TestRenderer::synchronize(RhiItem *rhiItem) +{ + TestRhiItem *item = static_cast<TestRhiItem *>(rhiItem); + if (item->color() != itemData.color) { + itemData.color = item->color(); + updateTexture(); + } +} + +void TestRenderer::render(QRhiCommandBuffer *cb) +{ + QRhiResourceUpdateBatch *rub = scene.resourceUpdates; + if (rub) + scene.resourceUpdates = nullptr; + + const QColor clearColor = QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f); + cb->beginPass(m_rt.data(), clearColor, { 1.0f, 0 }, rub); + + cb->setGraphicsPipeline(scene.ps.data()); + const QSize outputSize = m_output->pixelSize(); + cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height())); + cb->setShaderResources(); + const QRhiCommandBuffer::VertexInput vbufBindings[] = { + { scene.vbuf.data(), 0 }, + }; + cb->setVertexInput(0, 1, vbufBindings); + cb->draw(3); + + cb->endPass(); +} + +void TestRhiItem::setColor(QColor c) +{ + if (m_color == c) + return; + + m_color = c; + emit colorChanged(); + update(); +} diff --git a/tests/auto/quick/qquickitemrhiintegration/testrhiitem.h b/tests/auto/quick/qquickitemrhiintegration/testrhiitem.h new file mode 100644 index 0000000000..735c8e5355 --- /dev/null +++ b/tests/auto/quick/qquickitemrhiintegration/testrhiitem.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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 TESTRHIITEM_H +#define TESTRHIITEM_H + +#include "rhiitem.h" + +class TestRenderer : public RhiItemRenderer +{ +public: + void initialize(QRhi *rhi, QRhiTexture *outputTexture) override; + void synchronize(RhiItem *item) override; + void render(QRhiCommandBuffer *cb) override; + +private: + QRhi *m_rhi = nullptr; + QRhiTexture *m_output = nullptr; + QScopedPointer<QRhiRenderBuffer> m_ds; + QScopedPointer<QRhiTextureRenderTarget> m_rt; + QScopedPointer<QRhiRenderPassDescriptor> m_rp; + + struct { + QRhiResourceUpdateBatch *resourceUpdates = nullptr; + QScopedPointer<QRhiBuffer> vbuf; + QScopedPointer<QRhiBuffer> ubuf; + QScopedPointer<QRhiShaderResourceBindings> srb; + QScopedPointer<QRhiGraphicsPipeline> ps; + QScopedPointer<QRhiSampler> sampler; + QScopedPointer<QRhiTexture> tex; + QMatrix4x4 mvp; + } scene; + + struct { + QColor color; + } itemData; + + void initScene(); + void updateTexture(); +}; + +class TestRhiItem : public RhiItem +{ + Q_OBJECT + QML_NAMED_ELEMENT(TestRhiItem) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + +public: + RhiItemRenderer *createRenderer() override { return new TestRenderer; } + + QColor color() const { return m_color; } + void setColor(QColor c); + +signals: + void colorChanged(); + +private: + QColor m_color; +}; + +#endif diff --git a/tests/auto/quick/qquickitemrhiintegration/texture.frag b/tests/auto/quick/qquickitemrhiintegration/texture.frag new file mode 100644 index 0000000000..a7b8bce0ad --- /dev/null +++ b/tests/auto/quick/qquickitemrhiintegration/texture.frag @@ -0,0 +1,18 @@ +#version 440 + +layout(location = 0) in vec2 v_texcoord; + +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 mvp; + int flip; +}; + +layout(binding = 1) uniform sampler2D tex; + +void main() +{ + vec4 c = texture(tex, v_texcoord); + fragColor = vec4(c.rgb * c.a, c.a); +} diff --git a/tests/auto/quick/qquickitemrhiintegration/texture.vert b/tests/auto/quick/qquickitemrhiintegration/texture.vert new file mode 100644 index 0000000000..bf678d1e9b --- /dev/null +++ b/tests/auto/quick/qquickitemrhiintegration/texture.vert @@ -0,0 +1,19 @@ +#version 440 + +layout(location = 0) in vec4 position; +layout(location = 1) in vec2 texcoord; + +layout(location = 0) out vec2 v_texcoord; + +layout(std140, binding = 0) uniform buf { + mat4 mvp; + int flip; +}; + +void main() +{ + v_texcoord = texcoord; + if (flip != 0) + v_texcoord.y = 1.0 - v_texcoord.y; + gl_Position = mvp * position; +} diff --git a/tests/auto/quick/qquickitemrhiintegration/tst_qquickitemrhiintegration.cpp b/tests/auto/quick/qquickitemrhiintegration/tst_qquickitemrhiintegration.cpp new file mode 100644 index 0000000000..0c8581d907 --- /dev/null +++ b/tests/auto/quick/qquickitemrhiintegration/tst_qquickitemrhiintegration.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** 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 <qtest.h> +#include <QtQuickTestUtils/private/qmlutils_p.h> + +#include <QtGui/private/qrhi_p.h> + +#include <QQuickView> +#include <QSGRendererInterface> +#include <private/qsgrhisupport_p.h> + +class tst_QQuickItemRhiIntegration : public QQmlDataTest +{ + Q_OBJECT + +public: + tst_QQuickItemRhiIntegration(); + +private slots: + void initTestCase() override; + void cleanupTestCase(); + void rhiItem(); +}; + +tst_QQuickItemRhiIntegration::tst_QQuickItemRhiIntegration() + : QQmlDataTest(QT_QMLTEST_DATADIR) +{ +} + +void tst_QQuickItemRhiIntegration::initTestCase() +{ + QQmlDataTest::initTestCase(); + qDebug() << "RHI backend:" << QSGRhiSupport::instance()->rhiBackendName(); +} + +void tst_QQuickItemRhiIntegration::cleanupTestCase() +{ +} + +void tst_QQuickItemRhiIntegration::rhiItem() +{ + if (QGuiApplication::platformName() == QLatin1String("offscreen") + || QGuiApplication::platformName() == QLatin1String("minimal")) + { + QSKIP("Skipping on offscreen/minimal"); + } + + // Load up a scene that instantiates a TestRhiItem, which in turn + // renders a triangle with QRhi into a QRhiTexture and then draws + // a quad textured with it. + + QQuickView view; + view.setSource(testFileUrl(QLatin1String("test.qml"))); + view.setResizeMode(QQuickView::SizeViewToRootObject); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + if (!QSGRendererInterface::isApiRhiBased(view.rendererInterface()->graphicsApi())) + QSKIP("Scenegraph does not use QRhi, test is pointless"); + + QImage result = view.grabWindow(); + QVERIFY(!result.isNull()); + + const int tolerance = 5; + + // The result is a red triangle in the center of the view, confirm at least one pixel. + QRgb a = result.pixel(result.width() / 2, result.height() / 2); + QRgb b = QColor(Qt::red).rgb(); + QVERIFY(qAbs(qRed(a) - qRed(b)) <= tolerance + && qAbs(qGreen(a) - qGreen(b)) <= tolerance + && qAbs(qBlue(a) - qBlue(b)) <= tolerance); +} + +#include "tst_qquickitemrhiintegration.moc" + +QTEST_MAIN(tst_QQuickItemRhiIntegration) diff --git a/tests/auto/quick/qquicklayouts/BLACKLIST b/tests/auto/quick/qquicklayouts/BLACKLIST index a9e6a444af..5859d0f7cd 100644 --- a/tests/auto/quick/qquicklayouts/BLACKLIST +++ b/tests/auto/quick/qquicklayouts/BLACKLIST @@ -1,2 +1,11 @@ [Tests_GridLayout::test_spacings] qnx +#QTBUG-101327 +[tst_stacklayout::compile] +qnx +#QTBUG-101327 +[tst_gridlayout::compile] +qnx +#QTBUG-101327 +[tst_rowlayout::compile] +qnx diff --git a/tests/auto/quick/qquicklistview/data/qtbug48044.qml b/tests/auto/quick/qquicklistview/data/qtbug48044.qml index d318643c1c..368b6bd4bd 100644 --- a/tests/auto/quick/qquicklistview/data/qtbug48044.qml +++ b/tests/auto/quick/qquicklistview/data/qtbug48044.qml @@ -116,7 +116,7 @@ Item { color: header ? "yellow" : "cyan" border.color: "black" height: 50 - width: parent.width + width: listView.width Text { anchors.centerIn: parent diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 6975fa2dbd..d3ecdaae68 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -37,11 +37,13 @@ #include <QtQml/qqmlcontext.h> #include <QtQml/qqmlexpression.h> #include <QtQml/qqmlincubator.h> +#include <QtQml/qqmlcomponent.h> #include <QtQuick/private/qquickitemview_p_p.h> #include <QtQuick/private/qquicklistview_p.h> #include <QtQuick/private/qquickmousearea_p.h> #include <QtQuick/private/qquicktext_p.h> #include <QtQuick/private/qquickrectangle_p.h> +#include <QtQuick/private/qquicktransition_p.h> #include <QtQmlModels/private/qqmlobjectmodel_p.h> #include <QtQmlModels/private/qqmllistmodel_p.h> #include <QtQmlModels/private/qqmldelegatemodel_p.h> diff --git a/tests/auto/quick/qquickloader/data/noEngine.qml b/tests/auto/quick/qquickloader/data/noEngine.qml new file mode 100644 index 0000000000..19e619f32e --- /dev/null +++ b/tests/auto/quick/qquickloader/data/noEngine.qml @@ -0,0 +1,32 @@ +import QtQuick 2 + +Item { + id: root + property bool a: false + property int changes: 0 + onAChanged: { + m.model = 0 + m.model = 1 + ++changes; + } + + Repeater { + id: m + model: 1 + + Item { + Timer { + onTriggered: { + root.a = true + l.source = "loaded.qml" + } + interval: 0 + running: true + } + + Loader { + id: l + } + } + } +} diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp index cf1c4fcce0..e990e94bc3 100644 --- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp +++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp @@ -134,6 +134,7 @@ private slots: void setSourceAndCheckStatus(); void asyncLoaderRace(); + void noEngine(); }; Q_DECLARE_METATYPE(QList<QQmlError>) @@ -1533,6 +1534,20 @@ void tst_QQuickLoader::asyncLoaderRace() QCOMPARE(loader->item(), nullptr); } +void tst_QQuickLoader::noEngine() +{ + QQmlEngine engine; + const QUrl url = testFileUrl("noEngine.qml"); + QQmlComponent component(&engine, url); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + + const QString message = url.toString() + + QStringLiteral(":27:13: QML Loader: createComponent: Cannot find a QML engine."); + QTest::ignoreMessage(QtWarningMsg, qPrintable(message)); + QTRY_COMPARE(o->property("changes").toInt(), 1); +} + QTEST_MAIN(tst_QQuickLoader) #include "tst_qquickloader.moc" diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 0cbb047048..3d1f967b24 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -41,6 +41,8 @@ #include <QtGui/qstylehints.h> #include <QtGui/QCursor> #include <QtGui/QScreen> +#include <QEvent> +#include <QQmlComponent> #include <qpa/qwindowsysteminterface.h> Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests") @@ -741,7 +743,9 @@ void tst_QQuickMouseArea::updateMouseAreaPosOnResize() QCOMPARE(mouseRegion->mouseX(), 0.0); QCOMPARE(mouseRegion->mouseY(), 0.0); - QMouseEvent event(QEvent::MouseButtonPress, rect->position().toPoint(), Qt::LeftButton, Qt::LeftButton, {}); + QMouseEvent event(QEvent::MouseButtonPress, rect->position().toPoint(), + window.mapToGlobal(rect->position().toPoint()), + Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &event); QVERIFY(!mouseRegion->property("emitPositionChanged").toBool()); @@ -854,8 +858,10 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate() QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease); QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks); - QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); - QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); + QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), + window.mapToGlobal(QPoint(100, 100)), Qt::LeftButton, Qt::LeftButton, {}); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), + window.mapToGlobal(QPoint(100, 100)), Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &pressEvent); @@ -872,7 +878,8 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate() QCOMPARE(window.rootObject()->property("clicked").toInt(), ++expectedClicks); QGuiApplication::sendEvent(&window, &pressEvent); - QMouseEvent pressEvent2(QEvent::MouseButtonDblClick, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {}); + QMouseEvent pressEvent2(QEvent::MouseButtonDblClick, QPoint(100, 100), + window.mapToGlobal(QPoint(100, 100)), Qt::LeftButton, Qt::LeftButton, {}); QGuiApplication::sendEvent(&window, &pressEvent2); QTRY_VERIFY(window.rootObject()->property("pressed").toBool()); @@ -918,16 +925,19 @@ void tst_QQuickMouseArea::doubleClick() // The sequence for a double click is: // press, release, (click), press, double click, release - QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, {}); + QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), + window.mapToGlobal(QPoint(100, 100)), button, button, {}); QGuiApplication::sendEvent(&window, &pressEvent); - QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, {}); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), + window.mapToGlobal(QPoint(100, 100)), button, button, {}); QGuiApplication::sendEvent(&window, &releaseEvent); QCOMPARE(window.rootObject()->property("released").toInt(), 1); QGuiApplication::sendEvent(&window, &pressEvent); - QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, {}); + QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), + window.mapToGlobal(QPoint(100, 100)), button, button, {}); QGuiApplication::sendEvent(&window, &pressEvent2); QGuiApplication::sendEvent(&window, &releaseEvent); @@ -949,10 +959,12 @@ void tst_QQuickMouseArea::clickTwice() QVERIFY(mouseArea); mouseArea->setAcceptedButtons(acceptedButtons); - QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, {}); + QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), + window.mapToGlobal(QPoint(100, 100)), button, button, {}); QGuiApplication::sendEvent(&window, &pressEvent); - QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, {}); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), + window.mapToGlobal(QPoint(100, 100)), button, button, {}); QGuiApplication::sendEvent(&window, &releaseEvent); QCOMPARE(window.rootObject()->property("pressed").toInt(), 1); @@ -961,7 +973,8 @@ void tst_QQuickMouseArea::clickTwice() QGuiApplication::sendEvent(&window, &pressEvent); - QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, {}); + QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), + window.mapToGlobal(QPoint(100, 100)), button, button, {}); QGuiApplication::sendEvent(&window, &pressEvent2); QGuiApplication::sendEvent(&window, &releaseEvent); @@ -984,16 +997,19 @@ void tst_QQuickMouseArea::invalidClick() // The sequence for a double click is: // press, release, (click), press, double click, release - QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, {}); + QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), + window.mapToGlobal(QPoint(100, 100)), button, button, {}); QGuiApplication::sendEvent(&window, &pressEvent); - QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, {}); + QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), + window.mapToGlobal(QPoint(100, 100)), button, button, {}); QGuiApplication::sendEvent(&window, &releaseEvent); QCOMPARE(window.rootObject()->property("released").toInt(), 0); QGuiApplication::sendEvent(&window, &pressEvent); - QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, {}); + QMouseEvent pressEvent2 = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), + window.mapToGlobal(QPoint(100, 100)), button, button, {}); QGuiApplication::sendEvent(&window, &pressEvent2); QGuiApplication::sendEvent(&window, &releaseEvent); @@ -1273,9 +1289,8 @@ void tst_QQuickMouseArea::hoverPropagation() void tst_QQuickMouseArea::hoverVisible() { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QQuickView window; QVERIFY(QQuickTest::showView(window, testFileUrl("hoverVisible.qml"))); diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index 8f45368f1c..2a0e1e5b6d 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -44,6 +44,7 @@ #include <QtGui/qstandarditemmodel.h> #include <QStringListModel> #include <QFile> +#include <QEvent> #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/viewtestutils_p.h> @@ -1539,7 +1540,8 @@ void tst_QQuickPathView::mouseDrag() QTest::qWait(100); { - QMouseEvent mv(QEvent::MouseMove, QPoint(50,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); + QMouseEvent mv(QEvent::MouseMove, QPoint(50,100), window->mapToGlobal(QPoint(50,100)), + Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); QGuiApplication::sendEvent(window.data(), &mv); } // first move beyond threshold does not trigger drag @@ -1553,7 +1555,8 @@ void tst_QQuickPathView::mouseDrag() QCOMPARE(dragEndedSpy.count(), 0); { - QMouseEvent mv(QEvent::MouseMove, QPoint(90,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); + QMouseEvent mv(QEvent::MouseMove, QPoint(90,100), window->mapToGlobal(QPoint(90,100)), + Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); QGuiApplication::sendEvent(window.data(), &mv); } // next move beyond threshold does trigger drag @@ -1933,7 +1936,8 @@ void tst_QQuickPathView::cancelDrag() // steal mouse grab - cancels PathView dragging auto mouse = QPointingDevice::primaryPointingDevice(); auto mousePriv = QPointingDevicePrivate::get(const_cast<QPointingDevice *>(mouse)); - QMouseEvent fakeMouseEv(QEvent::MouseMove, QPoint(130, 100), Qt::NoButton, Qt::LeftButton, Qt::NoModifier, mouse); + QMouseEvent fakeMouseEv(QEvent::MouseMove, QPoint(130, 100), QPoint(130, 100), + Qt::NoButton, Qt::LeftButton, Qt::NoModifier, mouse); mousePriv->setExclusiveGrabber(&fakeMouseEv, fakeMouseEv.points().first(), nullptr); // returns to a snap point. diff --git a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp index e8a46e41ce..0cdbeaf17a 100644 --- a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp +++ b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp @@ -28,6 +28,7 @@ #include <QtTest/QtTest> #include <QtQuick/qquickview.h> #include <qqmlengine.h> +#include <QtQml/qqmlcomponent.h> #include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/private/qquickpositioners_p.h> #include <QtQuick/private/qquicktransition_p.h> diff --git a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp index e28bfffe6f..e07e97ddd8 100644 --- a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp +++ b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp @@ -72,8 +72,7 @@ void tst_qquickrectangle::color() QVERIFY(QTest::qWaitForWindowExposed(&view)); - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) + if (QGuiApplication::platformName() == QLatin1String("minimal")) QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); QImage image = view.grabWindow(); diff --git a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp index 0b12aa551f..e8b53d046f 100644 --- a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp +++ b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp @@ -30,6 +30,7 @@ #include <QtTest/QSignalSpy> #include <QtQml/qqmlengine.h> #include <QtQuick/qquickview.h> +#include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlcontext.h> #include <QtQml/qqmlexpression.h> #include <QtQml/qqmlincubator.h> diff --git a/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp b/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp index a77839afa4..8d95a6781e 100644 --- a/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp +++ b/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp @@ -29,6 +29,7 @@ #include <qtest.h> #include <QDebug> #include <QQmlEngine> +#include <QQmlComponent> #include <QtQuick/QQuickItem> #include <QtQuick/QQuickView> #include <QtGui/QScreen> diff --git a/tests/auto/quick/qquickshape/tst_qquickshape.cpp b/tests/auto/quick/qquickshape/tst_qquickshape.cpp index d72a3d265e..0d2debcf1f 100644 --- a/tests/auto/quick/qquickshape/tst_qquickshape.cpp +++ b/tests/auto/quick/qquickshape/tst_qquickshape.cpp @@ -282,9 +282,8 @@ void tst_QQuickShape::render() window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QImage img = window->grabWindow(); QVERIFY(!img.isNull()); @@ -306,9 +305,8 @@ void tst_QQuickShape::renderWithMultipleSp() window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QImage img = window->grabWindow(); QVERIFY(!img.isNull()); @@ -330,9 +328,8 @@ void tst_QQuickShape::radialGrad() window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QImage img = window->grabWindow(); QVERIFY(!img.isNull()); @@ -354,9 +351,8 @@ void tst_QQuickShape::conicalGrad() window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QImage img = window->grabWindow(); QVERIFY(!img.isNull()); @@ -378,9 +374,8 @@ void tst_QQuickShape::renderPolyline() window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QImage img = window->grabWindow(); QVERIFY(!img.isNull()); @@ -407,9 +402,8 @@ void tst_QQuickShape::renderMultiline() window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QImage img = window->grabWindow(); QVERIFY(!img.isNull()); @@ -473,9 +467,8 @@ void tst_QQuickShape::polylineDataTypes() window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QImage img = window->grabWindow(); QVERIFY(!img.isNull()); @@ -624,9 +617,8 @@ void tst_QQuickShape::multilineDataTypes() window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QImage img = window->grabWindow(); QVERIFY(!img.isNull()); @@ -672,9 +664,8 @@ void tst_QQuickShape::multilineStronglyTyped() QVERIFY(QTest::qWaitForWindowExposed(window.data())); provider->setPaths(m_lowPolyLogo); - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QImage img = window->grabWindow(); QVERIFY(!img.isNull()); diff --git a/tests/auto/quick/qquickstates/data/anchorRewind.qml b/tests/auto/quick/qquickstates/data/anchorRewind.qml new file mode 100644 index 0000000000..740c94cf42 --- /dev/null +++ b/tests/auto/quick/qquickstates/data/anchorRewind.qml @@ -0,0 +1,31 @@ +import QtQuick + +Item { + width: 400 + height: 400 + Item { + id: outer + anchors.fill: parent + Item { + id: inner + width: parent.width / 2 + height: parent.height / 2 + anchors.left: parent.right + anchors.top: parent.bottom + } + states: [ + State { + when: true + AnchorChanges { + target: inner + anchors.left: outer.left + anchors.top: outer.top + } + } + ] + transitions: Transition { + AnchorAnimation {} + } + } +} + diff --git a/tests/auto/quick/qquickstates/data/noStateOsciallation.qml b/tests/auto/quick/qquickstates/data/noStateOsciallation.qml new file mode 100644 index 0000000000..f0d7aeeb6d --- /dev/null +++ b/tests/auto/quick/qquickstates/data/noStateOsciallation.qml @@ -0,0 +1,22 @@ +import QtQuick 2.15 + +Item { + id: root + property int number: 2 + property int stateChangeCounter: 0 + + Item { + id: item + onStateChanged: ++stateChangeCounter + states: [ + State { + name: "n1" + when: root.number === 1 + }, + State { + name: "n2" + when: root.number === 2 + } + ] + } +} diff --git a/tests/auto/quick/qquickstates/data/parentChangeInvolvingBindings.qml b/tests/auto/quick/qquickstates/data/parentChangeInvolvingBindings.qml index 9680e806b8..d3873883cd 100644 --- a/tests/auto/quick/qquickstates/data/parentChangeInvolvingBindings.qml +++ b/tests/auto/quick/qquickstates/data/parentChangeInvolvingBindings.qml @@ -3,25 +3,41 @@ import QtQuick Item { id: root property alias childWidth: firstChild.width + property alias childX: firstChild.x property alias childRotation: firstChild.rotation property double myrotation: 100 property double myrotation2: 200 height: 400 + y: 40 Item { id: firstChild height: parent.height width: height + y: parent.y + x: y rotation: root.myrotation + + Item { + id: inner + anchors.fill: parent + } } states: State { name: "reparented" - ParentChange { target: firstChild; parent: otherChild; width: 2*height; rotation: root.myrotation2} + ParentChange { + target: firstChild + parent: otherChild + width: 2 *height + x: 2 * y + rotation: root.myrotation2 + } } Item { height: parent.height + y: parent.y id: otherChild } } diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp index 176a377ed4..d6814bd057 100644 --- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp +++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp @@ -38,6 +38,7 @@ #include <private/qquickitem_p.h> #include <private/qqmlproperty_p.h> #include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtTest/qsignalspy.h> class MyAttached : public QObject { @@ -199,11 +200,13 @@ private slots: void revertListMemoryLeak(); void duplicateStateName(); void trivialWhen(); + void noStateOsciallation(); void parentChangeCorrectReversal(); void revertNullObjectBinding(); void bindableProperties(); void parentChangeInvolvingBindings(); void deferredProperties(); + void rewindAnchorChange(); }; void tst_qquickstates::initTestCase() @@ -1715,6 +1718,20 @@ void tst_qquickstates::trivialWhen() QVERIFY(root); } +void tst_qquickstates::noStateOsciallation() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("noStateOsciallation.qml")); + QScopedPointer<QObject> root {component.create()}; + QVERIFY(root); + // set to 1 on initial transition from "" to "n2" + QCOMPARE(root->property("stateChangeCounter").toInt(), 1); + root->setProperty("number", 1); + // setting number to 1 changes directly from "n2" to "n1" + // without any intermediate transition to "" + QCOMPARE(root->property("stateChangeCounter").toInt(), 2); +} + void tst_qquickstates::parentChangeCorrectReversal() { QQmlEngine engine; @@ -1781,33 +1798,125 @@ void tst_qquickstates::bindableProperties() } } +struct Listener : QQuickItemChangeListener +{ + // We want to get notified about all the states. + constexpr static const QRectF expectations[] = { + QRectF(40, 40, 400, 400), + QRectF(40, 0, 400, 400), + QRectF(0, 0, 400, 400), + QRectF(0, 0, 800, 400), + QRectF(0, 0, 800, 200), + QRectF(0, 0, 400, 200), + QRectF(0, 20, 400, 200), + QRectF(40, 20, 400, 200), + QRectF(84, 42, 400, 200), + QRectF(84, 42, 86, 43), + QRectF(40, 40, 86, 43), + QRectF(40, 40, 400, 400), + QRectF(40, 20, 400, 400), + QRectF(40, 20, 400, 200), + QRectF(20, 20, 400, 200), + QRectF(20, 20, 200, 200), + QRectF(20, 20, 200, 300), + QRectF(20, 20, 300, 300), + QRectF(20, 30, 300, 300), + QRectF(30, 30, 300, 300), + }; + + int position = 0; + bool ok = true; + + void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &rect) override + { + if (rect != expectations[position]) { + qDebug() << position << rect; + ok = false; + } + ++position; + } +}; + void tst_qquickstates::parentChangeInvolvingBindings() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("parentChangeInvolvingBindings.qml")); QScopedPointer<QQuickItem> root { qobject_cast<QQuickItem *>(c.create()) }; QVERIFY2(root, qPrintable(c.errorString())); + + QObject *child = qmlContext(root.data())->objectForName(QStringLiteral("firstChild")); + QVERIFY(child); + QQuickItem *childItem = qobject_cast<QQuickItem *>(child); + QVERIFY(childItem); + Listener listener; + QQuickItemPrivate::get(childItem)->addItemChangeListener(&listener, QQuickItemPrivate::Geometry); + + QCOMPARE(root->property("childWidth").toInt(), 400); + QCOMPARE(root->property("childX").toInt(), 40); QCOMPARE(root->property("childRotation").toInt(), 100); root->setState("reparented"); QCOMPARE(root->property("childWidth").toInt(), 800); + QCOMPARE(root->property("childX").toInt(), 0); // x gets zeroed here, from unrelated place. QCOMPARE(root->property("childRotation").toInt(), 200); root->setProperty("myrotation2", 300); root->setHeight(200); + root->setY(20); QCOMPARE(root->property("childRotation").toInt(), 300); QCOMPARE(root->property("childWidth").toInt(), 400); + QCOMPARE(root->property("childX").toInt(), 40); + + QObject *inner = qmlContext(root.data())->objectForName(QStringLiteral("inner")); + QVERIFY(inner); + QQuickItem *innerItem = qobject_cast<QQuickItem *>(inner); + QVERIFY(innerItem); + + QCOMPARE(innerItem->size(), childItem->size()); + + // Does not break bindings and does not survive the state change. + // However, since the binding between x and y stays intact, we don't know + // whether x is set another time from the new y. We pass a pair of numbers that + // matches the binding. + childItem->setPosition(QPointF(84, 42)); + QCOMPARE(root->property("childX").toInt(), 84); + QVERIFY(listener.ok); + childItem->setSize(QSizeF(86, 43)); + QCOMPARE(root->property("childWidth").toInt(), 86); + QVERIFY(listener.ok); + + QCOMPARE(innerItem->size(), childItem->size()); + + QSignalSpy xSpy(childItem, SIGNAL(xChanged())); + QSignalSpy widthSpy(childItem, SIGNAL(widthChanged())); root->setState(""); + + QVERIFY(listener.ok); QCOMPARE(root->property("childRotation").toInt(), 100); - // QCOMPARE(root->property("childWidth").toInt(), 200); + // First change to 40 via reverse(), then to 20 via binding. + QCOMPARE(xSpy.count(), 2); + + // First change to 400 via reverse(), then to 200 via binding. + QCOMPARE(widthSpy.count(), 2); + + QCOMPARE(root->property("childX").toInt(), 20); + QCOMPARE(root->property("childWidth").toInt(), 200); + + QCOMPARE(innerItem->size(), childItem->size()); root->setProperty("myrotation", 50); root->setHeight(300); + QVERIFY(listener.ok); + root->setY(30); + QVERIFY(listener.ok); QCOMPARE(root->property("childWidth").toInt(), 300); + QCOMPARE(root->property("childX").toInt(), 30); QCOMPARE(root->property("childRotation").toInt(), 50); + + QCOMPARE(innerItem->size(), childItem->size()); } void tst_qquickstates::deferredProperties() @@ -1846,6 +1955,29 @@ void tst_qquickstates::deferredProperties() QCOMPARE(root->height(), 100.0); } +void tst_qquickstates::rewindAnchorChange() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("anchorRewind.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> root(c.create()); + QVERIFY(root); + + QQmlContext *context = qmlContext(root.data()); + QVERIFY(context); + + QObject *inner = context->objectForName(QStringLiteral("inner")); + QVERIFY(inner); + + QQuickItem *innerRect = qobject_cast<QQuickItem *>(inner); + QVERIFY(innerRect); + + QTRY_COMPARE(innerRect->x(), 0); + QTRY_COMPARE(innerRect->y(), 0); + QTRY_COMPARE(innerRect->width(), 200); + QTRY_COMPARE(innerRect->height(), 200); +} + QTEST_MAIN(tst_qquickstates) #include "tst_qquickstates.moc" diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index 1ecc3faa3a..cdcd704f98 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -38,6 +38,7 @@ #include <QtQml/qqmlcontext.h> #include <QtQml/qqmlexpression.h> #include <QtQml/qqmlincubator.h> +#include <QtQml/qqmlcomponent.h> #include <QtQmlModels/private/qqmlobjectmodel_p.h> #include <QtQmlModels/private/qqmllistmodel_p.h> @@ -192,6 +193,10 @@ private slots: void positionViewAtRow(); void positionViewAtColumn_data(); void positionViewAtColumn(); + void positionViewAtRowClamped_data(); + void positionViewAtRowClamped(); + void positionViewAtColumnClamped_data(); + void positionViewAtColumnClamped(); void itemAtCell_data(); void itemAtCell(); void leftRightTopBottomProperties_data(); @@ -3065,9 +3070,10 @@ void tst_QQuickTableView::replaceModel() void tst_QQuickTableView::cellAtPos_data() { QTest::addColumn<QPointF>("contentStartPos"); - QTest::addColumn<QPointF>("position"); + QTest::addColumn<QPointF>("localPos"); QTest::addColumn<bool>("includeSpacing"); QTest::addColumn<QPoint>("expectedCell"); + QTest::addColumn<QSizeF>("margins"); const int spacing = 10; const QPointF cellSize(100, 50); @@ -3080,49 +3086,62 @@ void tst_QQuickTableView::cellAtPos_data() return QPointF(x, y); }; - QTest::newRow("1") << QPointF(0, 0) << cellStart(0, 0) << false << QPoint(0, 0); - QTest::newRow("2") << QPointF(0, 0) << cellStart(1, 0) << false << QPoint(1, 0); - QTest::newRow("3") << QPointF(0, 0) << cellStart(0, 1) << false << QPoint(0, 1); - QTest::newRow("4") << QPointF(0, 0) << cellStart(1, 1) << false << QPoint(1, 1); + QTest::newRow("1") << QPointF(0, 0) << cellStart(0, 0) << false << QPoint(0, 0) << QSizeF(0, 0); + QTest::newRow("2") << QPointF(0, 0) << cellStart(1, 0) << false << QPoint(1, 0) << QSizeF(0, 0); + QTest::newRow("3") << QPointF(0, 0) << cellStart(0, 1) << false << QPoint(0, 1) << QSizeF(0, 0); + QTest::newRow("4") << QPointF(0, 0) << cellStart(1, 1) << false << QPoint(1, 1) << QSizeF(0, 0); - QTest::newRow("5") << QPointF(0, 0) << cellStart(1, 1) - quadSpace << false << QPoint(-1, -1); - QTest::newRow("6") << QPointF(0, 0) << cellStart(0, 0) + cellSize + quadSpace << false << QPoint(-1, -1); - QTest::newRow("7") << QPointF(0, 0) << cellStart(0, 1) + cellSize + quadSpace << false << QPoint(-1, -1); + QTest::newRow("5") << QPointF(0, 0) << cellStart(1, 1) - quadSpace << false << QPoint(-1, -1) << QSizeF(0, 0); + QTest::newRow("6") << QPointF(0, 0) << cellStart(0, 0) + cellSize + quadSpace << false << QPoint(-1, -1) << QSizeF(0, 0); + QTest::newRow("7") << QPointF(0, 0) << cellStart(0, 1) + cellSize + quadSpace << false << QPoint(-1, -1) << QSizeF(0, 0); - QTest::newRow("8") << QPointF(0, 0) << cellStart(1, 1) - quadSpace << true << QPoint(1, 1); - QTest::newRow("9") << QPointF(0, 0) << cellStart(0, 0) + cellSize + quadSpace << true << QPoint(0, 0); - QTest::newRow("10") << QPointF(0, 0) << cellStart(0, 1) + cellSize + quadSpace << true << QPoint(0, 1); + QTest::newRow("8") << QPointF(0, 0) << cellStart(1, 1) - quadSpace << true << QPoint(1, 1) << QSizeF(0, 0); + QTest::newRow("9") << QPointF(0, 0) << cellStart(0, 0) + cellSize + quadSpace << true << QPoint(0, 0) << QSizeF(0, 0); + QTest::newRow("10") << QPointF(0, 0) << cellStart(0, 1) + cellSize + quadSpace << true << QPoint(0, 1) << QSizeF(0, 0); - QTest::newRow("11") << cellStart(50, 50) << cellStart(0, 0) << false << QPoint(50, 50); - QTest::newRow("12") << cellStart(50, 50) << cellStart(4, 4) << false << QPoint(54, 54); - QTest::newRow("13") << cellStart(50, 50) << cellStart(4, 4) - quadSpace << false << QPoint(-1, -1); - QTest::newRow("14") << cellStart(50, 50) << cellStart(4, 4) + cellSize + quadSpace << false << QPoint(-1, -1); - QTest::newRow("15") << cellStart(50, 50) << cellStart(4, 4) - quadSpace << true << QPoint(54, 54); - QTest::newRow("16") << cellStart(50, 50) << cellStart(4, 4) + cellSize + quadSpace << true << QPoint(54, 54); + QTest::newRow("11") << cellStart(50, 50) << cellStart(50, 50) << false << QPoint(50, 50) << QSizeF(0, 0); + QTest::newRow("12") << cellStart(50, 50) << cellStart(54, 54) << false << QPoint(54, 54) << QSizeF(0, 0); + QTest::newRow("13") << cellStart(50, 50) << cellStart(54, 54) - quadSpace << false << QPoint(-1, -1) << QSizeF(0, 0); + QTest::newRow("14") << cellStart(50, 50) << cellStart(54, 54) + cellSize + quadSpace << false << QPoint(-1, -1) << QSizeF(0, 0); + QTest::newRow("15") << cellStart(50, 50) << cellStart(54, 54) - quadSpace << true << QPoint(54, 54) << QSizeF(0, 0); + QTest::newRow("16") << cellStart(50, 50) << cellStart(54, 54) + cellSize + quadSpace << true << QPoint(54, 54) << QSizeF(0, 0); - QTest::newRow("17") << cellStart(50, 50) + halfCell << cellStart(0, 0) << false << QPoint(50, 50); - QTest::newRow("18") << cellStart(50, 50) + halfCell << cellStart(1, 1) << false << QPoint(51, 51); - QTest::newRow("19") << cellStart(50, 50) + halfCell << cellStart(4, 4) << false << QPoint(54, 54); + QTest::newRow("17") << cellStart(50, 50) + halfCell << cellStart(50, 50) << false << QPoint(50, 50) << QSizeF(0, 0); + QTest::newRow("18") << cellStart(50, 50) + halfCell << cellStart(51, 51) << false << QPoint(51, 51) << QSizeF(0, 0); + QTest::newRow("19") << cellStart(50, 50) + halfCell << cellStart(54, 54) << false << QPoint(54, 54) << QSizeF(0, 0); + + QTest::newRow("20") << QPointF(0, 0) << cellStart(0, 0) << false << QPoint(0, 0) << QSizeF(150, 150); + QTest::newRow("20") << QPointF(0, 0) << cellStart(5, 5) << false << QPoint(5, 5) << QSizeF(150, 150); + + QTest::newRow("20") << QPointF(-150, -150) << cellStart(0, 0) << false << QPoint(0, 0) << QSizeF(150, 150); + QTest::newRow("21") << QPointF(-150, -150) << cellStart(4, 0) + halfCell << false << QPoint(4, 0) << QSizeF(150, 150); + QTest::newRow("22") << QPointF(-150, -150) << cellStart(0, 4) + halfCell << false << QPoint(0, 4) << QSizeF(150, 150); + QTest::newRow("23") << QPointF(-150, -150) << cellStart(4, 4) + halfCell << false << QPoint(4, 4) << QSizeF(150, 150); } void tst_QQuickTableView::cellAtPos() { QFETCH(QPointF, contentStartPos); - QFETCH(QPointF, position); + QFETCH(QPointF, localPos); QFETCH(bool, includeSpacing); QFETCH(QPoint, expectedCell); + QFETCH(QSizeF, margins); LOAD_TABLEVIEW("plaintableview.qml"); auto model = TestModelAsVariant(100, 100); tableView->setModel(model); tableView->setRowSpacing(10); tableView->setColumnSpacing(10); + tableView->setLeftMargin(margins.width()); + tableView->setLeftMargin(margins.height()); + tableView->setTopMargin(margins.height()); tableView->setContentX(contentStartPos.x()); tableView->setContentY(contentStartPos.y()); WAIT_UNTIL_POLISHED; - QPoint cell = tableView->cellAtPos(position, includeSpacing); + const QPointF posInView = tableView->mapFromItem(tableView->contentItem(), localPos); + QPoint cell = tableView->cellAtPos(posInView, includeSpacing); QCOMPARE(cell, expectedCell); } @@ -3138,48 +3157,30 @@ void tst_QQuickTableView::positionViewAtRow_data() QTest::newRow("AlignTop 1") << 1 << Qt::AlignTop << 0. << 50.; QTest::newRow("AlignTop 50") << 50 << Qt::AlignTop << 0. << -1.; QTest::newRow("AlignTop 0") << 0 << Qt::AlignTop << 0. << -1.; - QTest::newRow("AlignTop 99") << 99 << Qt::AlignTop << 0. << -1.; - - QTest::newRow("AlignTop 0") << 0 << Qt::AlignTop << -10. << 0.; QTest::newRow("AlignTop 1") << 1 << Qt::AlignTop << -10. << 0.; QTest::newRow("AlignTop 1") << 1 << Qt::AlignTop << -10. << 50.; QTest::newRow("AlignTop 50") << 50 << Qt::AlignTop << -10. << -1.; - QTest::newRow("AlignTop 0") << 0 << Qt::AlignTop << -10. << -1.; - QTest::newRow("AlignTop 99") << 99 << Qt::AlignTop << -10. << -1.; - QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 0. << 0.; - QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 0. << 0.; - QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 0. << 50.; QTest::newRow("AlignBottom 50") << 50 << Qt::AlignBottom << 0. << -1.; - QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 0. << -1.; + QTest::newRow("AlignBottom 98") << 98 << Qt::AlignBottom << 0. << -1.; QTest::newRow("AlignBottom 99") << 99 << Qt::AlignBottom << 0. << -1.; + QTest::newRow("AlignBottom 50") << 40 << Qt::AlignBottom << 10. << -1.; + QTest::newRow("AlignBottom 40") << 50 << Qt::AlignBottom << -10. << -1.; + QTest::newRow("AlignBottom 98") << 98 << Qt::AlignBottom << 10. << -1.; + QTest::newRow("AlignBottom 99") << 99 << Qt::AlignBottom << -10. << -1.; - QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 10. << 0.; - QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 10. << 0.; - QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 10. << 50.; - QTest::newRow("AlignBottom 50") << 50 << Qt::AlignBottom << 10. << -1.; - QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 10. << -1.; - QTest::newRow("AlignBottom 99") << 99 << Qt::AlignBottom << 10. << -1.; - - QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << 0.; - QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 0.; - QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 50.; + QTest::newRow("AlignCenter 40") << 40 << Qt::AlignCenter << 0. << -1.; QTest::newRow("AlignCenter 50") << 50 << Qt::AlignCenter << 0. << -1.; - QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << -1.; - QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << 0. << -1.; - - QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << 0.; - QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 0.; - QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 50.; + QTest::newRow("AlignCenter 40") << 40 << Qt::AlignCenter << 10. << -1.; QTest::newRow("AlignCenter 50") << 50 << Qt::AlignCenter << -10. << -1.; - QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << -1.; - QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << -10. << -1.; } void tst_QQuickTableView::positionViewAtRow() { // Check that positionViewAtRow actually flicks the view - // to the right position so that the row becomes visible + // to the right position so that the row becomes visible. + // For this test, we only check cells that can be placed exactly + // according to the given alignment. QFETCH(int, row); QFETCH(Qt::AlignmentFlag, alignment); QFETCH(qreal, offset); @@ -3229,48 +3230,27 @@ void tst_QQuickTableView::positionViewAtColumn_data() QTest::newRow("AlignLeft 1") << 1 << Qt::AlignLeft << 0. << 50.; QTest::newRow("AlignLeft 50") << 50 << Qt::AlignLeft << 0. << -1.; QTest::newRow("AlignLeft 0") << 0 << Qt::AlignLeft << 0. << -1.; - QTest::newRow("AlignLeft 99") << 99 << Qt::AlignLeft << 0. << -1.; - - QTest::newRow("AlignLeft 0") << 0 << Qt::AlignLeft << -10. << 0.; QTest::newRow("AlignLeft 1") << 1 << Qt::AlignLeft << -10. << 0.; QTest::newRow("AlignLeft 1") << 1 << Qt::AlignLeft << -10. << 50.; QTest::newRow("AlignLeft 50") << 50 << Qt::AlignLeft << -10. << -1.; - QTest::newRow("AlignLeft 0") << 0 << Qt::AlignLeft << -10. << -1.; - QTest::newRow("AlignLeft 99") << 99 << Qt::AlignLeft << -10. << -1.; - QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 0. << 0.; - QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 0. << 0.; - QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 0. << 50.; QTest::newRow("AlignRight 50") << 50 << Qt::AlignRight << 0. << -1.; - QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 0. << -1.; QTest::newRow("AlignRight 99") << 99 << Qt::AlignRight << 0. << -1.; - - QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 10. << 0.; - QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 10. << 0.; - QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 10. << 50.; QTest::newRow("AlignRight 50") << 50 << Qt::AlignRight << 10. << -1.; - QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 10. << -1.; - QTest::newRow("AlignRight 99") << 99 << Qt::AlignRight << 10. << -1.; + QTest::newRow("AlignRight 99") << 99 << Qt::AlignRight << -10. << -1.; - QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << 0.; - QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 0.; - QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 50.; + QTest::newRow("AlignCenter 40") << 50 << Qt::AlignCenter << 0. << -1.; QTest::newRow("AlignCenter 50") << 50 << Qt::AlignCenter << 0. << -1.; - QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << -1.; - QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << 0. << -1.; - - QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << 0.; - QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 0.; - QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 50.; + QTest::newRow("AlignCenter 40") << 50 << Qt::AlignCenter << 10. << -1.; QTest::newRow("AlignCenter 50") << 50 << Qt::AlignCenter << -10. << -1.; - QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << -1.; - QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << -10. << -1.; } void tst_QQuickTableView::positionViewAtColumn() { // Check that positionViewAtColumn actually flicks the view - // to the right position so that the row becomes visible + // to the right position so that the row becomes visible. + // For this test, we only check cells that can be placed exactly + // according to the given alignment. QFETCH(int, column); QFETCH(Qt::AlignmentFlag, alignment); QFETCH(qreal, offset); @@ -3308,6 +3288,134 @@ void tst_QQuickTableView::positionViewAtColumn() } } +void tst_QQuickTableView::positionViewAtRowClamped_data() +{ + QTest::addColumn<int>("row"); + QTest::addColumn<Qt::AlignmentFlag>("alignment"); + QTest::addColumn<qreal>("offset"); + QTest::addColumn<qreal>("contentYStartPos"); + + QTest::newRow("AlignTop 0") << 0 << Qt::AlignTop << -10. << 0.; + QTest::newRow("AlignTop 0") << 0 << Qt::AlignTop << -10. << -1.; + QTest::newRow("AlignTop 99") << 99 << Qt::AlignTop << 0. << -1.; + QTest::newRow("AlignTop 99") << 99 << Qt::AlignTop << -10. << -1.; + + QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 0. << 0.; + QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 0. << 0.; + QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 0. << 50.; + QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 0. << -1.; + + QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 10. << 0.; + QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 10. << 0.; + QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 10. << 50.; + QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 10. << -1.; + QTest::newRow("AlignBottom 99") << 99 << Qt::AlignBottom << 10. << -1.; + + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 50.; + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << -1.; + QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << 0. << -1.; + + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 50.; + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << -1.; + QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << -10. << -1.; +} + +void tst_QQuickTableView::positionViewAtRowClamped() +{ + // Check that positionViewAtRow actually flicks the table to the + // right position so that the row becomes visible. For this test, we + // only test cells that cannot be placed exactly at the given alignment, + // because it would cause the table to overshoot. Instead the + // table should be flicked to the edge of the viewport, close to the + // requested alignment. + QFETCH(int, row); + QFETCH(Qt::AlignmentFlag, alignment); + QFETCH(qreal, offset); + QFETCH(qreal, contentYStartPos); + + LOAD_TABLEVIEW("plaintableview.qml"); + auto model = TestModelAsVariant(100, 100); + tableView->setModel(model); + if (contentYStartPos >= 0) + tableView->setContentY(contentYStartPos); + + WAIT_UNTIL_POLISHED; + + tableView->positionViewAtRow(row, alignment, offset); + + WAIT_UNTIL_POLISHED; + + QCOMPARE(tableView->contentY(), row < 50 ? 0 : tableView->contentHeight() - tableView->height()); +} + +void tst_QQuickTableView::positionViewAtColumnClamped_data() +{ + QTest::addColumn<int>("column"); + QTest::addColumn<Qt::AlignmentFlag>("alignment"); + QTest::addColumn<qreal>("offset"); + QTest::addColumn<qreal>("contentXStartPos"); + + QTest::newRow("AlignLeft 0") << 0 << Qt::AlignLeft << -10. << 0.; + QTest::newRow("AlignLeft 0") << 0 << Qt::AlignLeft << -10. << -1.; + QTest::newRow("AlignLeft 99") << 99 << Qt::AlignLeft << 0. << -1.; + QTest::newRow("AlignLeft 99") << 99 << Qt::AlignLeft << -10. << -1.; + + QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 0. << 0.; + QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 0. << 0.; + QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 0. << 50.; + QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 0. << -1.; + + QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 10. << 0.; + QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 10. << 0.; + QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 10. << 50.; + QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 10. << -1.; + QTest::newRow("AlignRight 99") << 99 << Qt::AlignRight << 10. << -1.; + + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 50.; + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << -1.; + QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << 0. << -1.; + + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 50.; + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << -1.; + QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << -10. << -1.; +} + +void tst_QQuickTableView::positionViewAtColumnClamped() +{ + // Check that positionViewAtColumn actually flicks the table to the + // right position so that the column becomes visible. For this test, we + // only test cells that cannot be placed exactly at the given alignment, + // because it would cause the table to overshoot. Instead the + // table should be flicked to the edge of the viewport, close to the + // requested alignment. + QFETCH(int, column); + QFETCH(Qt::AlignmentFlag, alignment); + QFETCH(qreal, offset); + QFETCH(qreal, contentXStartPos); + + LOAD_TABLEVIEW("plaintableview.qml"); + auto model = TestModelAsVariant(100, 100); + tableView->setModel(model); + if (contentXStartPos >= 0) + tableView->setContentX(contentXStartPos); + + WAIT_UNTIL_POLISHED; + + tableView->positionViewAtColumn(column, alignment, offset); + + WAIT_UNTIL_POLISHED; + + QCOMPARE(tableView->contentX(), column < 50 ? 0 : tableView->contentWidth() - tableView->width()); +} + void tst_QQuickTableView::itemAtCell_data() { QTest::addColumn<QPoint>("cell"); diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index bf90a8efd4..c31e698527 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -1028,9 +1028,6 @@ static inline QByteArray msgNotLessThan(int n1, int n2) void tst_qquicktext::hAlignImplicitWidth() { -#ifdef Q_OS_MACOS - QSKIP("this test currently crashes on MacOS. See QTBUG-68047"); -#endif QQuickView view(testFileUrl("hAlignImplicitWidth.qml")); view.setFlags(view.flags() | Qt::WindowStaysOnTopHint); // Prevent being obscured by other windows. view.show(); @@ -1054,9 +1051,8 @@ void tst_qquicktext::hAlignImplicitWidth() const int centeredSection3End = centeredSection3 + sectionWidth; { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); // Left Align QImage image = view.grabWindow(); @@ -4049,7 +4045,11 @@ static qreal expectedBaselineScaled(QQuickText *item) { QFont font = item->font(); QTextLayout layout(item->text().replace(QLatin1Char('\n'), QChar::LineSeparator)); - do { + + qreal low = 0; + qreal high = 10000; + + while (low < high) { layout.setFont(font); qreal width = 0; layout.beginLayout(); @@ -4059,12 +4059,23 @@ static qreal expectedBaselineScaled(QQuickText *item) } layout.endLayout(); - if (width < item->width()) { - QFontMetricsF fm(layout.font()); - return fm.ascent() + item->topPadding(); + if (width > item->width()) { + high = font.pointSizeF(); + font.setPointSizeF((high + low) / 2); + } else { + low = font.pointSizeF(); + + // When fontSizeMode != FixedSize, the font size will be scaled to a value + // The goal is to find a pointSize that uses as much space as possible while + // still fitting inside the available space. 0.01 is chosen as the threshold. + if ((high - low) < qreal(0.01)) { + QFontMetricsF fm(layout.font()); + return fm.ascent() + item->topPadding(); + } + + font.setPointSizeF((high + low) / 2); } - font.setPointSize(font.pointSize() - 1); - } while (font.pointSize() > 0); + } return item->topPadding(); } @@ -4682,9 +4693,8 @@ void tst_qquicktext::verticallyAlignedImageInTable() void tst_qquicktext::transparentBackground() { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabToImage not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("transparentBackground.qml")); @@ -4703,9 +4713,8 @@ void tst_qquicktext::transparentBackground() void tst_qquicktext::displaySuperscriptedTag() { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabToImage not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("displaySuperscriptedTag.qml")); diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index 0fc3db86ae..c15a626d33 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -945,9 +945,8 @@ void tst_qquicktextedit::hAlignVisual() const int centeredSection3End = centeredSection3 + sectionWidth; { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); // Left Align QImage image = view.grabWindow(); @@ -2729,7 +2728,8 @@ void tst_qquicktextedit::cursorDelegate() const QPoint point2 = textEditObject->positionToRectangle(10).center().toPoint(); QTest::qWait(400); //ensure this isn't treated as a double-click QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, point1); - QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); + QMouseEvent mv(QEvent::MouseMove, point2, view.mapToGlobal(point2), + Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); QGuiApplication::sendEvent(&view, &mv); QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, point2); QTest::qWait(50); @@ -3766,8 +3766,7 @@ void tst_qquicktextedit::largeTextObservesViewport() { if ((QGuiApplication::platformName() == QLatin1String("offscreen")) || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to few fonts installed on offscreen/minimal platforms"); - + QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); QFETCH(QString, text); QFETCH(QQuickTextEdit::TextFormat, textFormat); QFETCH(bool, parentIsViewport); @@ -6124,9 +6123,8 @@ void tst_qquicktextedit::keys_shortcutoverride() void tst_qquicktextedit::transparentSelectionColor() { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabToImage not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); QQuickView view; view.setSource(testFileUrl("transparentSelectionColor.qml")); diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 669f94ed48..42099762f0 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -31,6 +31,7 @@ #include <QtQuickTestUtils/private/testhttpserver_p.h> #include <private/qinputmethod_p.h> #include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlexpression.h> #include <QFile> #include <QtQuick/qquickview.h> @@ -2896,7 +2897,8 @@ void tst_qquicktextinput::cursorDelegate() const QPoint point2 = textInputObject->positionToRectangle(10).center().toPoint(); QTest::qWait(400); //ensure this isn't treated as a double-click QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, point1); - QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); + QMouseEvent mv(QEvent::MouseMove, point2, textInputObject->mapToGlobal(point2), + Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); QGuiApplication::sendEvent(&view, &mv); QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, point2); QTest::qWait(50); diff --git a/tests/auto/quick/qquicktreeview/testmodel.cpp b/tests/auto/quick/qquicktreeview/testmodel.cpp index a562b3274a..83c7ead4a8 100644 --- a/tests/auto/quick/qquicktreeview/testmodel.cpp +++ b/tests/auto/quick/qquicktreeview/testmodel.cpp @@ -61,18 +61,21 @@ TestModel::TestModel(QObject *parent) m_rootItem.reset(new TreeItem()); for (int col = 0; col < m_columnCount; ++col) m_rootItem.data()->m_entries << QVariant(QString("0, %1").arg(col)); - createTreeRecursive(m_rootItem.data(), 4, 0, 4); + createTreeRecursive(m_rootItem.data(), 4, 1); } -void TestModel::createTreeRecursive(TreeItem *item, int childCount, int currentDepth, int maxDepth) +void TestModel::createTreeRecursive(TreeItem *item, int childCount, int currentDepth) { + if (currentDepth > maxDepth()) + return; + for (int row = 0; row < childCount; ++row) { auto childItem = new TreeItem(item); for (int col = 0; col < m_columnCount; ++col) childItem->m_entries << QVariant(QString("%1, %2").arg(row).arg(col)); item->m_childItems.append(childItem); - if (currentDepth < maxDepth && row == childCount - 1) - createTreeRecursive(childItem, childCount, currentDepth + 1, maxDepth); + if (row == childCount - 1) + createTreeRecursive(childItem, childCount, currentDepth + 1); } } diff --git a/tests/auto/quick/qquicktreeview/testmodel.h b/tests/auto/quick/qquicktreeview/testmodel.h index 4d9006d112..fc4272af8b 100644 --- a/tests/auto/quick/qquicktreeview/testmodel.h +++ b/tests/auto/quick/qquicktreeview/testmodel.h @@ -64,7 +64,7 @@ class TestModel : public QAbstractItemModel public: explicit TestModel(QObject *parent = nullptr); - void createTreeRecursive(TreeItem *item, int childCount, int currentDepth, int maxDepth); + void createTreeRecursive(TreeItem *item, int childCount, int currentDepth); TreeItem *treeItem(const QModelIndex &index) const; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex & = QModelIndex()) const override; @@ -72,6 +72,7 @@ public: bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; + int maxDepth() { return 4; } bool insertRows(int position, int rows, const QModelIndex &parent) override; diff --git a/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp b/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp index abba5fced6..712ccdc6cc 100644 --- a/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp +++ b/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp @@ -90,10 +90,20 @@ private: private slots: void initTestCase() override; void showTreeView(); + void invalidArguments(); void expandAndCollapseRoot(); void toggleExpanded(); void expandAndCollapseChildren(); void expandChildPendingToBeVisible(); + void expandRecursivelyRoot_data(); + void expandRecursivelyRoot(); + void expandRecursivelyChild_data(); + void expandRecursivelyChild(); + void expandRecursivelyWholeTree(); + void collapseRecursivelyRoot(); + void collapseRecursivelyChild(); + void collapseRecursivelyWholeTree(); + void expandToIndex(); void requiredPropertiesRoot(); void requiredPropertiesChildren(); void emptyModel(); @@ -120,14 +130,47 @@ void tst_qquicktreeview::showTreeView() QCOMPARE(treeViewPrivate->loadedRows.count(), 1); } + +void tst_qquicktreeview::invalidArguments() +{ + // Check that we handle gracefully invalid arguments + LOAD_TREEVIEW("normaltreeview.qml"); + + treeView->expand(-2); + QCOMPARE(treeView->rows(), 1); + treeView->expandRecursively(200); + QCOMPARE(treeView->rows(), 1); + treeView->expandRecursively(-2); + QCOMPARE(treeView->rows(), 1); + treeView->expandRecursively(200); + QCOMPARE(treeView->rows(), 1); + + treeView->collapse(-2); + QCOMPARE(treeView->rows(), 1); + treeView->collapseRecursively(200); + QCOMPARE(treeView->rows(), 1); + treeView->collapseRecursively(-2); + QCOMPARE(treeView->rows(), 1); + treeView->collapseRecursively(200); + QCOMPARE(treeView->rows(), 1); +} + void tst_qquicktreeview::expandAndCollapseRoot() { LOAD_TREEVIEW("normaltreeview.qml"); // Check that the view only has one row loaded so far (the root of the tree) QCOMPARE(treeViewPrivate->loadedRows.count(), 1); + QSignalSpy expandedSpy(treeView, SIGNAL(expanded(int, int))); + // Expand the root treeView->expand(0); + + QCOMPARE(expandedSpy.count(), 1); + auto signalArgs = expandedSpy.takeFirst(); + QVERIFY(signalArgs.at(0).toInt() == 0); + QVERIFY(signalArgs.at(1).toInt() == 1); + WAIT_UNTIL_POLISHED; // We now expect 5 rows, the root pluss it's 4 children QCOMPARE(treeViewPrivate->loadedRows.count(), 5); @@ -163,14 +206,23 @@ void tst_qquicktreeview::expandAndCollapseChildren() LOAD_TREEVIEW("normaltreeview.qml"); const int childCount = 4; + QSignalSpy expandedSpy(treeView, SIGNAL(expanded(int, int))); // Expand the last child of a parent recursively four times for (int level = 0; level < 4; ++level) { const int nodeToExpand = level * childCount; const int firstChildRow = nodeToExpand + 1; // (+ 1 for the root) const int lastChildRow = firstChildRow + 4; + treeView->expand(nodeToExpand); + + QCOMPARE(expandedSpy.count(), 1); + auto signalArgs = expandedSpy.takeFirst(); + QVERIFY(signalArgs.at(0).toInt() == nodeToExpand); + QVERIFY(signalArgs.at(1).toInt() == 1); + WAIT_UNTIL_POLISHED; + QCOMPARE(treeView->rows(), lastChildRow); auto childItem1 = treeViewPrivate->loadedTableItem(QPoint(0, firstChildRow))->item; @@ -381,9 +433,305 @@ void tst_qquicktreeview::expandChildPendingToBeVisible() // Now the view have updated to show // all the rows that has been expanded. - QCOMPARE(treeView->rows(), 9); + QCOMPARE(treeView->rows(), 9); +} + +void tst_qquicktreeview::expandRecursivelyRoot_data() +{ + QTest::addColumn<int>("rowToExpand"); + QTest::addColumn<int>("depth"); + + QTest::newRow("0, -1") << 0 << -1; + QTest::newRow("0, 0") << 0 << 0; + QTest::newRow("0, 1") << 0 << 1; + QTest::newRow("0, 2") << 0 << 2; +} + +void tst_qquicktreeview::expandRecursivelyRoot() +{ + // Check that we can expand the root node (row 0), and that + // all its children are expanded recursively down to the + // given depth. + QFETCH(int, rowToExpand); + QFETCH(int, depth); + + LOAD_TREEVIEW("normaltreeview.qml"); + QSignalSpy spy(treeView, SIGNAL(expanded(int, int))); + + treeView->expandRecursively(rowToExpand, depth); + + if (depth == 0) { + QCOMPARE(spy.count(), 0); + } else { + + QCOMPARE(spy.count(), 1); + const auto signalArgs = spy.takeFirst(); + QVERIFY(signalArgs.at(0).toInt() == rowToExpand); + QVERIFY(signalArgs.at(1).toInt() == depth); + } + + WAIT_UNTIL_POLISHED; + + const int rowToExpandDepth = treeView->depth(rowToExpand); + const int effectiveMaxDepth = depth != -1 ? rowToExpandDepth + depth : model->maxDepth(); + + if (depth > 0 || depth == -1) + QVERIFY(treeView->isExpanded(rowToExpand)); + else + QVERIFY(!treeView->isExpanded(rowToExpand)); + + // Check that all rows after rowToExpand, that are also + // children of that row, is expanded (down to depth) + for (int currentRow = rowToExpand + 1; currentRow < treeView->rows(); ++currentRow) { + const auto modelIndex = treeView->modelIndex(currentRow, 0); + const int currentDepth = treeView->depth(currentRow); + const bool isChild = currentDepth > rowToExpandDepth; + const bool isExpandable = model->rowCount(modelIndex) > 0; + const bool shouldBeExpanded = isChild && isExpandable && currentDepth < effectiveMaxDepth; + QCOMPARE(treeView->isExpanded(currentRow), shouldBeExpanded); + } +} + +void tst_qquicktreeview::expandRecursivelyChild_data() +{ + QTest::addColumn<int>("rowToExpand"); + QTest::addColumn<int>("depth"); + + QTest::newRow("5, -1") << 4 << -1; + QTest::newRow("5, 0") << 4 << 0; + QTest::newRow("5, 1") << 4 << 1; + QTest::newRow("5, 2") << 4 << 2; + QTest::newRow("5, 3") << 4 << 3; +} + +void tst_qquicktreeview::expandRecursivelyChild() +{ + // Check that we can first expand the root node, and the expand + // recursive the first child node with children (row 4), and that all + // its children of that node are expanded recursively according to depth. + QFETCH(int, rowToExpand); + QFETCH(int, depth); + + LOAD_TREEVIEW("normaltreeview.qml"); + QSignalSpy spy(treeView, SIGNAL(expanded(int, int))); + + treeView->expand(0); + + QCOMPARE(spy.count(), 1); + auto signalArgs = spy.takeFirst(); + QVERIFY(signalArgs.at(0).toInt() == 0); + QVERIFY(signalArgs.at(1).toInt() == 1); + + treeView->expandRecursively(rowToExpand, depth); + + if (depth == 0) { + QCOMPARE(spy.count(), 0); + } else { + QCOMPARE(spy.count(), 1); + signalArgs = spy.takeFirst(); + QVERIFY(signalArgs.at(0).toInt() == rowToExpand); + QVERIFY(signalArgs.at(1).toInt() == depth); + } + + WAIT_UNTIL_POLISHED; + + const bool rowToExpandDepth = treeView->depth(rowToExpand); + const int effectiveMaxDepth = depth != -1 ? rowToExpandDepth + depth : model->maxDepth(); + + // Check that all rows before rowToExpand is not expanded + // (except the root node) + for (int currentRow = 1; currentRow < rowToExpand; ++currentRow) + QVERIFY(!treeView->isExpanded(currentRow)); + + // Check if rowToExpand is expanded + if (depth > 0 || depth == -1) + QVERIFY(treeView->isExpanded(rowToExpand)); + else + QVERIFY(!treeView->isExpanded(rowToExpand)); + + // Check that all rows after rowToExpand that is also + // children of that row is expanded (down to depth) + for (int currentRow = rowToExpand + 1; currentRow < treeView->rows(); ++currentRow) { + const int currentDepth = treeView->depth(currentRow); + const bool isChild = currentDepth > rowToExpandDepth; + const auto modelIndex = treeView->modelIndex(currentRow, 0); + const bool isExpandable = model->rowCount(modelIndex) > 0; + const bool shouldBeExpanded = isChild && isExpandable && currentDepth < effectiveMaxDepth; + QCOMPARE(treeView->isExpanded(currentRow), shouldBeExpanded); + } } +void tst_qquicktreeview::expandRecursivelyWholeTree() +{ + // Check that we expand the whole tree recursively by passing -1, -1 + LOAD_TREEVIEW("normaltreeview.qml"); + QSignalSpy spy(treeView, SIGNAL(expanded(int, int))); + treeView->expandRecursively(-1, -1); + + QCOMPARE(spy.count(), 1); + auto signalArgs = spy.takeFirst(); + QVERIFY(signalArgs.at(0).toInt() == -1); + QVERIFY(signalArgs.at(1).toInt() == -1); + + WAIT_UNTIL_POLISHED; + + // Check that all rows that have children are expanded + for (int currentRow = 0; currentRow < treeView->rows(); ++currentRow) { + const auto modelIndex = treeView->modelIndex(currentRow, 0); + const bool isExpandable = model->rowCount(modelIndex) > 0; + QCOMPARE(treeView->isExpanded(currentRow), isExpandable); + } +} + +void tst_qquicktreeview::collapseRecursivelyRoot() +{ + // Check that we can collapse the root node (row 0), and that + // all its children are collapsed recursively down to the leaves. + LOAD_TREEVIEW("normaltreeview.qml"); + treeView->expandRecursively(); + WAIT_UNTIL_POLISHED; + + // Verify that the tree is now fully expanded + const int expectedRowCount = 1 + (model->maxDepth() * 4); // root + 4 children per level + QCOMPARE(treeView->rows(), expectedRowCount); + + QSignalSpy spy(treeView, SIGNAL(collapsed(int, bool))); + + // Collapse the whole tree again. This time, only the root should end up visible + treeView->collapseRecursively(); + + QCOMPARE(spy.count(), 1); + const auto signalArgs = spy.takeFirst(); + QVERIFY(signalArgs.at(0).toInt() == -1); + QVERIFY(signalArgs.at(1).toBool() == true); + + WAIT_UNTIL_POLISHED; + + QCOMPARE(treeView->rows(), 1); + + // We need to check that all descendants are collapsed as well. But since they're + // now no longer visible in the view, we need to expand each parent one by one again to make + // them visible, and check that the child inside that has children is still collapsed. + // We can do that by simply iterate over the rows in the view as we expand. + int currentRow = 0; + while (currentRow < treeView->rows()) { + const QModelIndex currentIndex = treeView->modelIndex(currentRow, 0); + if (model->hasChildren(currentIndex)) { + QVERIFY(!treeView->isExpanded(currentRow)); + treeView->expand(currentRow); + WAIT_UNTIL_POLISHED; + } + currentRow++; + } + + // Sanity check that we ended up with all rows expanded again + QCOMPARE(currentRow, expectedRowCount); +} + +void tst_qquicktreeview::collapseRecursivelyChild() +{ + // Check that we can collapse a child node (row 4), and that all its children + // are collapsed recursively down to the leaves (without touching the root). + LOAD_TREEVIEW("normaltreeview.qml"); + treeView->expandRecursively(); + WAIT_UNTIL_POLISHED; + + // Verify that the tree is now fully expanded + const int expectedRowCount = 1 + (model->maxDepth() * 4); // root + 4 children per level + QCOMPARE(treeView->rows(), expectedRowCount); + + QSignalSpy spy(treeView, SIGNAL(collapsed(int, bool))); + + // Collapse the 4th child recursive + const int rowToCollapse = 4; + QCOMPARE(model->data(treeView->modelIndex(rowToCollapse, 0), Qt::DisplayRole), QStringLiteral("3, 0")); + treeView->collapseRecursively(rowToCollapse); + + QCOMPARE(spy.count(), 1); + const auto signalArgs = spy.takeFirst(); + QVERIFY(signalArgs.at(0).toInt() == rowToCollapse); + QVERIFY(signalArgs.at(1).toBool() == true); + + WAIT_UNTIL_POLISHED; + + QCOMPARE(treeView->rows(), 5); // root + 4 children + + // We need to check that all descendants are collapsed as well. But since they're + // now no longer visible in the view, we need to expand each parent one by one again to make + // them visible, and check that the child inside that has children is still collapsed. + // We can do that by simply iterate over the rows in the view as we expand. + int currentRow = 1; // start at first child + while (currentRow < treeView->rows()) { + const QModelIndex currentIndex = treeView->modelIndex(currentRow, 0); + if (model->hasChildren(currentIndex)) { + QVERIFY(!treeView->isExpanded(currentRow)); + treeView->expand(currentRow); + WAIT_UNTIL_POLISHED; + } + currentRow++; + } + + // Sanity check that we ended up with all rows expanded again + QCOMPARE(currentRow, expectedRowCount); +} + +void tst_qquicktreeview::collapseRecursivelyWholeTree() +{ + // Check that we collapse the whole tree recursively by passing -1 + LOAD_TREEVIEW("normaltreeview.qml"); + QSignalSpy spy(treeView, SIGNAL(collapsed(int, bool))); + treeView->expandRecursively(); + treeView->collapseRecursively(); + + QCOMPARE(spy.count(), 1); + auto signalArgs = spy.takeFirst(); + QVERIFY(signalArgs.at(0).toInt() == -1); + QVERIFY(signalArgs.at(1).toBool() == true); + + WAIT_UNTIL_POLISHED; + + QCOMPARE(treeView->rows(), 1); // root +} + +void tst_qquicktreeview::expandToIndex() +{ + // Check that expandToIndex(index) expands the tree so + // that index becomes visible in the view + LOAD_TREEVIEW("normaltreeview.qml"); + QSignalSpy spy(treeView, SIGNAL(expanded(int, int))); + + const QModelIndex root = model->index(0, 0); + const QModelIndex child1 = model->index(3, 0, root); + const QModelIndex child2 = model->index(3, 0, child1); + + QVERIFY(model->hasChildren(root)); + QVERIFY(model->hasChildren(child1)); + QVERIFY(model->hasChildren(child2)); + + QVERIFY(!treeView->isExpanded(treeView->rowAtIndex(root))); + QVERIFY(!treeView->isExpanded(treeView->rowAtIndex(child1))); + QVERIFY(!treeView->isExpanded(treeView->rowAtIndex(child2))); + + const QModelIndex childToExpand = model->index(1, 0, child2); + treeView->expandToIndex(childToExpand); + + QVERIFY(treeView->isExpanded(treeView->rowAtIndex(root))); + QVERIFY(treeView->isExpanded(treeView->rowAtIndex(child1))); + QVERIFY(treeView->isExpanded(treeView->rowAtIndex(child2))); + + QCOMPARE(spy.count(), 1); + auto signalArgs = spy.takeFirst(); + QVERIFY(signalArgs.at(0).toInt() == 0); + QVERIFY(signalArgs.at(1).toInt() == 3); + + WAIT_UNTIL_POLISHED; + + // The view should now have 13 rows: + // root + 3 expanded nodes that each have 4 children + QCOMPARE(treeView->rows(), 13); +} + + QTEST_MAIN(tst_qquicktreeview) #include "tst_qquicktreeview.moc" diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index aca156f911..ba09401545 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -28,6 +28,7 @@ #include <qtest.h> #include <QDebug> +#include <QEvent> #include <QMimeData> #include <QTouchEvent> #include <QtQuick/QQuickItem> @@ -1618,9 +1619,8 @@ public: void tst_qquickwindow::earlyGrab() { - if ((QGuiApplication::platformName() == QLatin1String("offscreen")) - || (QGuiApplication::platformName() == QLatin1String("minimal"))) - QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + if (QGuiApplication::platformName() == QLatin1String("minimal")) + QSKIP("Skipping due to grabWindow not functional on minimal platforms"); qmlRegisterType<Grabber>("Test", 1, 0, "Grabber"); QQmlEngine engine; @@ -1867,24 +1867,24 @@ void tst_qquickwindow::ignoreUnhandledMouseEvents() item->setParentItem(window->contentItem()); { - QMouseEvent me(QEvent::MouseButtonPress, QPointF(50, 50), Qt::LeftButton, Qt::LeftButton, - Qt::NoModifier); + QMouseEvent me(QEvent::MouseButtonPress, QPointF(50, 50), window->mapToGlobal(QPointF(50, 50)), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); me.setAccepted(true); QVERIFY(QCoreApplication::sendEvent(window, &me)); QVERIFY(!me.isAccepted()); } { - QMouseEvent me(QEvent::MouseMove, QPointF(51, 51), Qt::LeftButton, Qt::LeftButton, - Qt::NoModifier); + QMouseEvent me(QEvent::MouseMove, QPointF(51, 51), window->mapToGlobal(QPointF(51, 51)), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); me.setAccepted(true); QVERIFY(QCoreApplication::sendEvent(window, &me)); QVERIFY(!me.isAccepted()); } { - QMouseEvent me(QEvent::MouseButtonRelease, QPointF(51, 51), Qt::LeftButton, Qt::LeftButton, - Qt::NoModifier); + QMouseEvent me(QEvent::MouseButtonRelease, QPointF(51, 51), window->mapToGlobal(QPointF(51, 51)), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); me.setAccepted(true); QVERIFY(QCoreApplication::sendEvent(window, &me)); QVERIFY(!me.isAccepted()); diff --git a/tests/auto/quickcontrols2/CMakeLists.txt b/tests/auto/quickcontrols2/CMakeLists.txt index 06eecf6714..b3b655f2cd 100644 --- a/tests/auto/quickcontrols2/CMakeLists.txt +++ b/tests/auto/quickcontrols2/CMakeLists.txt @@ -1,7 +1,9 @@ # Generated from auto.pro. add_subdirectory(accessibility) +if(NOT ANDROID) # temporarily disabled until QTBUG-100991 is fixed add_subdirectory(controls) +endif() add_subdirectory(cursor) add_subdirectory(customization) add_subdirectory(designer) @@ -24,10 +26,14 @@ add_subdirectory(qquickiconlabel) add_subdirectory(qquickimaginestyle) add_subdirectory(qquickmaterialstyle) add_subdirectory(qquickmaterialstyleconf) +if(NOT ANDROID) # QTBUG-101005 add_subdirectory(qquickmenu) +endif() add_subdirectory(qquickmenubar) add_subdirectory(qquickninepatchimage) +if(NOT ANDROID) # QTBUG-101005 add_subdirectory(qquickpopup) +endif() add_subdirectory(qquickstyle) add_subdirectory(qquickuniversalstyle) add_subdirectory(qquickuniversalstyleconf) diff --git a/tests/auto/quickcontrols2/controls/basic/CMakeLists.txt b/tests/auto/quickcontrols2/controls/basic/CMakeLists.txt index 844f0eade5..6ae45f1460 100644 --- a/tests/auto/quickcontrols2/controls/basic/CMakeLists.txt +++ b/tests/auto/quickcontrols2/controls/basic/CMakeLists.txt @@ -20,9 +20,21 @@ qt_internal_add_test(tst_basic PUBLIC_LIBRARIES Qt::Gui Qt::QuickControls2 + Qt::QuickTemplates2 TESTDATA ${test_data} ) +# Make the QML files available to Creator's locator. +target_sources(tst_basic + PRIVATE + ${test_data} +) + +set_source_files_properties(${test_data} + PROPERTIES + HEADER_FILE_ONLY ON +) + #### Keys ignored in scope 1:.:.:basic.pro:<TRUE>: # OTHER_FILES = "$$PWD/../data/*.qml" # TEMPLATE = "app" diff --git a/tests/auto/quickcontrols2/controls/basic/dummy.qml b/tests/auto/quickcontrols2/controls/basic/dummy.qml deleted file mode 100644 index 6b1bdfec0e..0000000000 --- a/tests/auto/quickcontrols2/controls/basic/dummy.qml +++ /dev/null @@ -1,9 +0,0 @@ -import QtQml -import QtQuick -import QtQuick.NativeStyle -import QtQuick.Layouts -import Qt.labs.settings -import Qt.labs.qmlmodels - -QtObject { -} diff --git a/tests/auto/quickcontrols2/controls/basic/dummy_imports.qml b/tests/auto/quickcontrols2/controls/basic/dummy_imports.qml new file mode 100644 index 0000000000..580db56d12 --- /dev/null +++ b/tests/auto/quickcontrols2/controls/basic/dummy_imports.qml @@ -0,0 +1,12 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++. + +import QtQml +import QtQuick +import QtQuick.NativeStyle +import QtQuick.Layouts +import Qt.labs.settings +import Qt.labs.qmlmodels + +QtObject { } diff --git a/tests/auto/quickcontrols2/controls/data/tst_button.qml b/tests/auto/quickcontrols2/controls/data/tst_button.qml index a6ec4faad9..f26a050dfc 100644 --- a/tests/auto/quickcontrols2/controls/data/tst_button.qml +++ b/tests/auto/quickcontrols2/controls/data/tst_button.qml @@ -272,7 +272,9 @@ TestCase { // no change sequenceSpy.expectedSequence = [] - var keys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab] + // Not testing Key_Enter and Key_Return because QGnomeTheme uses them for + // pressing buttons and the CI uses the QGnomeTheme platform theme. + var keys = [Qt.Key_Escape, Qt.Key_Tab] for (var i = 0; i < keys.length; ++i) { sequenceSpy.reset() keyClick(keys[i]) diff --git a/tests/auto/quickcontrols2/controls/data/tst_checkbox.qml b/tests/auto/quickcontrols2/controls/data/tst_checkbox.qml index be68ac0d1f..ff6b7ec8b8 100644 --- a/tests/auto/quickcontrols2/controls/data/tst_checkbox.qml +++ b/tests/auto/quickcontrols2/controls/data/tst_checkbox.qml @@ -313,7 +313,9 @@ TestCase { // no change sequenceSpy.expectedSequence = [] - var keys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab] + // Not testing Key_Enter and Key_Return because QGnomeTheme uses them for + // pressing buttons and the CI uses the QGnomeTheme platform theme. + var keys = [Qt.Key_Escape, Qt.Key_Tab] for (var i = 0; i < keys.length; ++i) { sequenceSpy.reset() keyClick(keys[i]) diff --git a/tests/auto/quickcontrols2/controls/data/tst_combobox.qml b/tests/auto/quickcontrols2/controls/data/tst_combobox.qml index 100ec45a40..e8e24652de 100644 --- a/tests/auto/quickcontrols2/controls/data/tst_combobox.qml +++ b/tests/auto/quickcontrols2/controls/data/tst_combobox.qml @@ -641,14 +641,15 @@ TestCase { } function test_keys_space_enter_escape_data() { + // Not testing Key_Enter + Key_Enter and Key_Return + Key_Return because + // QGnomeTheme uses Key_Enter and Key_Return for pressing buttons/comboboxes + // and the CI uses the QGnomeTheme platform theme. return [ { tag: "space-space", key1: Qt.Key_Space, key2: Qt.Key_Space, showPopup: true, showPress: true, hidePopup: true, hidePress: true }, { tag: "space-enter", key1: Qt.Key_Space, key2: Qt.Key_Enter, showPopup: true, showPress: true, hidePopup: true, hidePress: true }, { tag: "space-return", key1: Qt.Key_Space, key2: Qt.Key_Return, showPopup: true, showPress: true, hidePopup: true, hidePress: true }, { tag: "space-escape", key1: Qt.Key_Space, key2: Qt.Key_Escape, showPopup: true, showPress: true, hidePopup: true, hidePress: false }, { tag: "space-0", key1: Qt.Key_Space, key2: Qt.Key_0, showPopup: true, showPress: true, hidePopup: false, hidePress: false }, - { tag: "enter-enter", key1: Qt.Key_Enter, key2: Qt.Key_Enter, showPopup: false, showPress: false, hidePopup: true, hidePress: false }, - { tag: "return-return", key1: Qt.Key_Return, key2: Qt.Key_Return, showPopup: false, showPress: false, hidePopup: true, hidePress: false }, { tag: "escape-escape", key1: Qt.Key_Escape, key2: Qt.Key_Escape, showPopup: false, showPress: false, hidePopup: true, hidePress: false } ] } diff --git a/tests/auto/quickcontrols2/controls/data/tst_delaybutton.qml b/tests/auto/quickcontrols2/controls/data/tst_delaybutton.qml index 0e8d188dd2..7350b54320 100644 --- a/tests/auto/quickcontrols2/controls/data/tst_delaybutton.qml +++ b/tests/auto/quickcontrols2/controls/data/tst_delaybutton.qml @@ -307,7 +307,9 @@ TestCase { // no change sequenceSpy.expectedSequence = [] - var keys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab] + // Not testing Key_Enter and Key_Return because QGnomeTheme uses them for + // pressing buttons and the CI uses the QGnomeTheme platform theme. + var keys = [Qt.Key_Escape, Qt.Key_Tab] for (var i = 0; i < keys.length; ++i) { sequenceSpy.reset() keyClick(keys[i]) diff --git a/tests/auto/quickcontrols2/controls/data/tst_radiobutton.qml b/tests/auto/quickcontrols2/controls/data/tst_radiobutton.qml index 973e56a360..d0ff56207d 100644 --- a/tests/auto/quickcontrols2/controls/data/tst_radiobutton.qml +++ b/tests/auto/quickcontrols2/controls/data/tst_radiobutton.qml @@ -258,7 +258,9 @@ TestCase { // no change sequenceSpy.expectedSequence = [] - var keys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab] + // Not testing Key_Enter and Key_Return because QGnomeTheme uses them for + // pressing buttons and the CI uses the QGnomeTheme platform theme. + var keys = [Qt.Key_Escape, Qt.Key_Tab] for (var i = 0; i < keys.length; ++i) { sequenceSpy.reset() keyClick(keys[i]) diff --git a/tests/auto/quickcontrols2/controls/data/tst_scrollbar.qml b/tests/auto/quickcontrols2/controls/data/tst_scrollbar.qml index d49658762c..d453f017e7 100644 --- a/tests/auto/quickcontrols2/controls/data/tst_scrollbar.qml +++ b/tests/auto/quickcontrols2/controls/data/tst_scrollbar.qml @@ -964,4 +964,38 @@ TestCase { compare(vertical.visualPosition, 0.2) compare(vertical.contentItem.y, vertical.topPadding + 0.2 * vertical.availableHeight) } + + function test_setting_invalid_property_values() { + var control = createTemporaryObject(scrollBar, testCase, {size: 2.0, minimumSize: -1.0}) + verify(control) + + // check that the values are within the expected range + compare(control.size, 1.0) + compare(control.minimumSize, 0) + + control.minimumSize = 2.0 + compare(control.minimumSize, 1.0) + + // test if setting NaN is prevented + control.size = NaN + control.minimumSize = NaN + compare(control.size, 1.0) + compare(control.minimumSize, 1.0) + + + // test if setting float infinity is prevented + control.size = Number.POSITIVE_INFINITY + control.minimumSize = Number.POSITIVE_INFINITY + compare(control.size, 1.0) + compare(control.minimumSize, 1.0) + + let oldPosition = control.position; + let oldStepSize = control.stepSize; + + control.position = NaN; + control.stepSize = NaN; + + compare(oldPosition, control.position) + compare(oldStepSize, control.stepSize) + } } diff --git a/tests/auto/quickcontrols2/controls/data/tst_switch.qml b/tests/auto/quickcontrols2/controls/data/tst_switch.qml index bf25fcacff..a4e1f813a0 100644 --- a/tests/auto/quickcontrols2/controls/data/tst_switch.qml +++ b/tests/auto/quickcontrols2/controls/data/tst_switch.qml @@ -570,7 +570,9 @@ TestCase { // no change spy.expectedSequence = [] - var keys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab] + // Not testing Key_Enter and Key_Return because QGnomeTheme uses them for + // pressing buttons and the CI uses the QGnomeTheme platform theme. + var keys = [Qt.Key_Escape, Qt.Key_Tab] for (var i = 0; i < keys.length; ++i) { keyClick(keys[i]) compare(control.checked, false) diff --git a/tests/auto/quickcontrols2/controls/data/tst_toolbutton.qml b/tests/auto/quickcontrols2/controls/data/tst_toolbutton.qml index a7acd05ae4..f85a9193e8 100644 --- a/tests/auto/quickcontrols2/controls/data/tst_toolbutton.qml +++ b/tests/auto/quickcontrols2/controls/data/tst_toolbutton.qml @@ -169,7 +169,9 @@ TestCase { compare(clickedSpy.count, 2) // no change - var keys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab] + // Not testing Key_Enter and Key_Return because QGnomeTheme uses them for + // pressing buttons and the CI uses the QGnomeTheme platform theme. + var keys = [Qt.Key_Escape, Qt.Key_Tab] for (var i = 0; i < keys.length; ++i) { keyClick(keys[i]) compare(clickedSpy.count, 2) diff --git a/tests/auto/quickcontrols2/controls/fusion/CMakeLists.txt b/tests/auto/quickcontrols2/controls/fusion/CMakeLists.txt index 633e0442de..6381718103 100644 --- a/tests/auto/quickcontrols2/controls/fusion/CMakeLists.txt +++ b/tests/auto/quickcontrols2/controls/fusion/CMakeLists.txt @@ -20,9 +20,21 @@ qt_internal_add_test(tst_fusion PUBLIC_LIBRARIES Qt::Gui Qt::QuickControls2 + Qt::QuickTemplates2 TESTDATA ${test_data} ) +# Make the QML files available to Creator's locator. +target_sources(tst_fusion + PRIVATE + ${test_data} +) + +set_source_files_properties(${test_data} + PROPERTIES + HEADER_FILE_ONLY ON +) + #### Keys ignored in scope 1:.:.:fusion.pro:<TRUE>: # OTHER_FILES = "$$PWD/../data/*.qml" # TEMPLATE = "app" diff --git a/tests/auto/quickcontrols2/controls/fusion/dummy.qml b/tests/auto/quickcontrols2/controls/fusion/dummy.qml deleted file mode 100644 index 6b1bdfec0e..0000000000 --- a/tests/auto/quickcontrols2/controls/fusion/dummy.qml +++ /dev/null @@ -1,9 +0,0 @@ -import QtQml -import QtQuick -import QtQuick.NativeStyle -import QtQuick.Layouts -import Qt.labs.settings -import Qt.labs.qmlmodels - -QtObject { -} diff --git a/tests/auto/quickcontrols2/controls/fusion/dummy_imports.qml b/tests/auto/quickcontrols2/controls/fusion/dummy_imports.qml new file mode 100644 index 0000000000..580db56d12 --- /dev/null +++ b/tests/auto/quickcontrols2/controls/fusion/dummy_imports.qml @@ -0,0 +1,12 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++. + +import QtQml +import QtQuick +import QtQuick.NativeStyle +import QtQuick.Layouts +import Qt.labs.settings +import Qt.labs.qmlmodels + +QtObject { } diff --git a/tests/auto/quickcontrols2/controls/imagine/CMakeLists.txt b/tests/auto/quickcontrols2/controls/imagine/CMakeLists.txt index 9638eb8250..962628e1b6 100644 --- a/tests/auto/quickcontrols2/controls/imagine/CMakeLists.txt +++ b/tests/auto/quickcontrols2/controls/imagine/CMakeLists.txt @@ -20,9 +20,21 @@ qt_internal_add_test(tst_imagine PUBLIC_LIBRARIES Qt::Gui Qt::QuickControls2 + Qt::QuickTemplates2 TESTDATA ${test_data} ) +# Make the QML files available to Creator's locator. +target_sources(tst_imagine + PRIVATE + ${test_data} +) + +set_source_files_properties(${test_data} + PROPERTIES + HEADER_FILE_ONLY ON +) + #### Keys ignored in scope 1:.:.:imagine.pro:<TRUE>: # OTHER_FILES = "$$PWD/../data/*.qml" # TEMPLATE = "app" diff --git a/tests/auto/quickcontrols2/controls/imagine/dummy.qml b/tests/auto/quickcontrols2/controls/imagine/dummy.qml deleted file mode 100644 index 6b1bdfec0e..0000000000 --- a/tests/auto/quickcontrols2/controls/imagine/dummy.qml +++ /dev/null @@ -1,9 +0,0 @@ -import QtQml -import QtQuick -import QtQuick.NativeStyle -import QtQuick.Layouts -import Qt.labs.settings -import Qt.labs.qmlmodels - -QtObject { -} diff --git a/tests/auto/quickcontrols2/controls/imagine/dummy_imports.qml b/tests/auto/quickcontrols2/controls/imagine/dummy_imports.qml new file mode 100644 index 0000000000..580db56d12 --- /dev/null +++ b/tests/auto/quickcontrols2/controls/imagine/dummy_imports.qml @@ -0,0 +1,12 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++. + +import QtQml +import QtQuick +import QtQuick.NativeStyle +import QtQuick.Layouts +import Qt.labs.settings +import Qt.labs.qmlmodels + +QtObject { } diff --git a/tests/auto/quickcontrols2/controls/macos/CMakeLists.txt b/tests/auto/quickcontrols2/controls/macos/CMakeLists.txt index da8c14230e..555a11b4ea 100644 --- a/tests/auto/quickcontrols2/controls/macos/CMakeLists.txt +++ b/tests/auto/quickcontrols2/controls/macos/CMakeLists.txt @@ -17,3 +17,13 @@ qt_internal_add_test(tst_macos TESTDATA ${test_data} ) +# Make the QML files available to Creator's locator. +target_sources(tst_macos + PRIVATE + ${test_data} +) + +set_source_files_properties(${test_data} + PROPERTIES + HEADER_FILE_ONLY ON +) diff --git a/tests/auto/quickcontrols2/controls/material/CMakeLists.txt b/tests/auto/quickcontrols2/controls/material/CMakeLists.txt index 8cc0cd3fab..b07a63e32d 100644 --- a/tests/auto/quickcontrols2/controls/material/CMakeLists.txt +++ b/tests/auto/quickcontrols2/controls/material/CMakeLists.txt @@ -20,9 +20,21 @@ qt_internal_add_test(tst_material PUBLIC_LIBRARIES Qt::Gui Qt::QuickControls2 + Qt::QuickTemplates2 TESTDATA ${test_data} ) +# Make the QML files available to Creator's locator. +target_sources(tst_material + PRIVATE + ${test_data} +) + +set_source_files_properties(${test_data} + PROPERTIES + HEADER_FILE_ONLY ON +) + #### Keys ignored in scope 1:.:.:material.pro:<TRUE>: # OTHER_FILES = "$$PWD/../data/*.qml" # TEMPLATE = "app" diff --git a/tests/auto/quickcontrols2/controls/material/dummy.qml b/tests/auto/quickcontrols2/controls/material/dummy.qml deleted file mode 100644 index 6b1bdfec0e..0000000000 --- a/tests/auto/quickcontrols2/controls/material/dummy.qml +++ /dev/null @@ -1,9 +0,0 @@ -import QtQml -import QtQuick -import QtQuick.NativeStyle -import QtQuick.Layouts -import Qt.labs.settings -import Qt.labs.qmlmodels - -QtObject { -} diff --git a/tests/auto/quickcontrols2/controls/material/dummy_imports.qml b/tests/auto/quickcontrols2/controls/material/dummy_imports.qml new file mode 100644 index 0000000000..580db56d12 --- /dev/null +++ b/tests/auto/quickcontrols2/controls/material/dummy_imports.qml @@ -0,0 +1,12 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++. + +import QtQml +import QtQuick +import QtQuick.NativeStyle +import QtQuick.Layouts +import Qt.labs.settings +import Qt.labs.qmlmodels + +QtObject { } diff --git a/tests/auto/quickcontrols2/controls/universal/CMakeLists.txt b/tests/auto/quickcontrols2/controls/universal/CMakeLists.txt index 2faa30cc37..0b3478a227 100644 --- a/tests/auto/quickcontrols2/controls/universal/CMakeLists.txt +++ b/tests/auto/quickcontrols2/controls/universal/CMakeLists.txt @@ -20,9 +20,21 @@ qt_internal_add_test(tst_universal PUBLIC_LIBRARIES Qt::Gui Qt::QuickControls2 + Qt::QuickTemplates2 TESTDATA ${test_data} ) +# Make the QML files available to Creator's locator. +target_sources(tst_universal + PRIVATE + ${test_data} +) + +set_source_files_properties(${test_data} + PROPERTIES + HEADER_FILE_ONLY ON +) + #### Keys ignored in scope 1:.:.:universal.pro:<TRUE>: # OTHER_FILES = "$$PWD/../data/*.qml" # TEMPLATE = "app" diff --git a/tests/auto/quickcontrols2/controls/universal/dummy.qml b/tests/auto/quickcontrols2/controls/universal/dummy.qml deleted file mode 100644 index 6b1bdfec0e..0000000000 --- a/tests/auto/quickcontrols2/controls/universal/dummy.qml +++ /dev/null @@ -1,9 +0,0 @@ -import QtQml -import QtQuick -import QtQuick.NativeStyle -import QtQuick.Layouts -import Qt.labs.settings -import Qt.labs.qmlmodels - -QtObject { -} diff --git a/tests/auto/quickcontrols2/controls/universal/dummy_imports.qml b/tests/auto/quickcontrols2/controls/universal/dummy_imports.qml new file mode 100644 index 0000000000..580db56d12 --- /dev/null +++ b/tests/auto/quickcontrols2/controls/universal/dummy_imports.qml @@ -0,0 +1,12 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++. + +import QtQml +import QtQuick +import QtQuick.NativeStyle +import QtQuick.Layouts +import Qt.labs.settings +import Qt.labs.qmlmodels + +QtObject { } diff --git a/tests/auto/quickcontrols2/controls/windows/CMakeLists.txt b/tests/auto/quickcontrols2/controls/windows/CMakeLists.txt index 33755c1bbd..c448c55d25 100644 --- a/tests/auto/quickcontrols2/controls/windows/CMakeLists.txt +++ b/tests/auto/quickcontrols2/controls/windows/CMakeLists.txt @@ -16,3 +16,14 @@ qt_internal_add_test(tst_windows Qt::QuickControls2 TESTDATA ${test_data} ) + +# Make the QML files available to Creator's locator. +target_sources(tst_windows + PRIVATE + ${test_data} +) + +set_source_files_properties(${test_data} + PROPERTIES + HEADER_FILE_ONLY ON +) diff --git a/tests/auto/quickcontrols2/customization/dummy_imports.qml b/tests/auto/quickcontrols2/customization/dummy_imports.qml new file mode 100644 index 0000000000..40d9cea712 --- /dev/null +++ b/tests/auto/quickcontrols2/customization/dummy_imports.qml @@ -0,0 +1,9 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++. + +import QtQuick +import QtQuick.Window +import QtQuick.Controls + +QtObject { } diff --git a/tests/auto/quickcontrols2/designer/CMakeLists.txt b/tests/auto/quickcontrols2/designer/CMakeLists.txt index 50331f8fef..d13b014c93 100644 --- a/tests/auto/quickcontrols2/designer/CMakeLists.txt +++ b/tests/auto/quickcontrols2/designer/CMakeLists.txt @@ -11,6 +11,7 @@ qt_internal_add_test(tst_designer Qt::Gui Qt::Quick Qt::QuickControls2 + Qt::QuickTemplates2 Qt::QuickPrivate ) diff --git a/tests/auto/quickcontrols2/designer/dummy_imports.qml b/tests/auto/quickcontrols2/designer/dummy_imports.qml new file mode 100644 index 0000000000..401692e570 --- /dev/null +++ b/tests/auto/quickcontrols2/designer/dummy_imports.qml @@ -0,0 +1,8 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++. + +import QtQuick +import QtQuick.Controls + +QtObject { } diff --git a/tests/auto/quickcontrols2/pressandhold/CMakeLists.txt b/tests/auto/quickcontrols2/pressandhold/CMakeLists.txt index 926d446f82..cf2d92a567 100644 --- a/tests/auto/quickcontrols2/pressandhold/CMakeLists.txt +++ b/tests/auto/quickcontrols2/pressandhold/CMakeLists.txt @@ -11,6 +11,7 @@ qt_internal_add_test(tst_pressandhold Qt::Gui Qt::Quick Qt::QuickControls2 + Qt::QuickTemplates2 ) #### Keys ignored in scope 1:.:.:pressandhold.pro:<TRUE>: diff --git a/tests/auto/quickcontrols2/pressandhold/tst_pressandhold.cpp b/tests/auto/quickcontrols2/pressandhold/tst_pressandhold.cpp index 1bf3c6e343..89038201cf 100644 --- a/tests/auto/quickcontrols2/pressandhold/tst_pressandhold.cpp +++ b/tests/auto/quickcontrols2/pressandhold/tst_pressandhold.cpp @@ -93,10 +93,14 @@ void tst_PressAndHold::pressAndHold() QVERIFY(spy.isValid() && waitSpy.isValid()); int startDragDistance = QGuiApplication::styleHints()->startDragDistance(); - QMouseEvent press(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); - QMouseEvent press2(QEvent::MouseButtonPress, QPointF(), Qt::RightButton, Qt::RightButton, Qt::NoModifier); - QMouseEvent move(QEvent::MouseMove, QPointF(2 * startDragDistance, 2 * startDragDistance), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); - QMouseEvent release(QEvent::MouseButtonRelease, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QMouseEvent press(QEvent::MouseButtonPress, QPointF(), QPointF(), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QMouseEvent press2(QEvent::MouseButtonPress, QPointF(), QPointF(), + Qt::RightButton, Qt::RightButton, Qt::NoModifier); + QMouseEvent move(QEvent::MouseMove, QPointF(2 * startDragDistance, 2 * startDragDistance), QPointF(), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QMouseEvent release(QEvent::MouseButtonRelease, QPointF(), QPointF(), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); // pressAndHold() emitted QGuiApplication::sendEvent(control.data(), &press); @@ -166,9 +170,12 @@ void tst_PressAndHold::keepSelection() QSignalSpy waitSpy(waitControl.data(), SIGNAL(pressAndHold(QQuickMouseEvent*))); QVERIFY(spy.isValid() && waitSpy.isValid()); - QMouseEvent press(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); - QMouseEvent press2(QEvent::MouseButtonPress, QPointF(), Qt::RightButton, Qt::RightButton, Qt::NoModifier); - QMouseEvent release(QEvent::MouseButtonRelease, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QMouseEvent press(QEvent::MouseButtonPress, QPointF(), QPointF(), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QMouseEvent press2(QEvent::MouseButtonPress, QPointF(), QPointF(), + Qt::RightButton, Qt::RightButton, Qt::NoModifier); + QMouseEvent release(QEvent::MouseButtonRelease, QPointF(), QPointF(), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QVERIFY(!control->property("text").toString().isEmpty()); QVERIFY(QMetaObject::invokeMethod(control.data(), "selectAll")); diff --git a/tests/auto/quickcontrols2/qquickcontrol/data/fractionalFontSize.qml b/tests/auto/quickcontrols2/qquickcontrol/data/fractionalFontSize.qml new file mode 100644 index 0000000000..a4574e460e --- /dev/null +++ b/tests/auto/quickcontrols2/qquickcontrol/data/fractionalFontSize.qml @@ -0,0 +1,18 @@ +import QtQuick +import QtQuick.Controls + +ApplicationWindow { + width: 400 + height: 400 + + property alias control: ctrl + + Control { + id: ctrl + contentItem: Text { + font.pointSize: 10.5 + elide: Text.ElideRight + text: "This is some sample text" + } + } +} diff --git a/tests/auto/quickcontrols2/qquickcontrol/tst_qquickcontrol.cpp b/tests/auto/quickcontrols2/qquickcontrol/tst_qquickcontrol.cpp index 94dccf6115..3ee9cc2328 100644 --- a/tests/auto/quickcontrols2/qquickcontrol/tst_qquickcontrol.cpp +++ b/tests/auto/quickcontrols2/qquickcontrol/tst_qquickcontrol.cpp @@ -33,6 +33,7 @@ #include <QtQuickTestUtils/private/visualtestutils_p.h> #include <QtQuickTemplates2/private/qquickbutton_p.h> #include <QtQuickControlsTestUtils/private/qtest_quickcontrols_p.h> +#include <QtQuick/private/qquicktext_p_p.h> using namespace QQuickVisualTestUtils; @@ -46,6 +47,7 @@ public: private slots: void initTestCase() override; void flickable(); + void fractionalFontSize(); private: QScopedPointer<QPointingDevice> touchDevice; @@ -93,6 +95,24 @@ void tst_QQuickControl::flickable() QTRY_COMPARE(buttonClickedSpy.count(), 1); } +void tst_QQuickControl::fractionalFontSize() +{ + QQuickApplicationHelper helper(this, QStringLiteral("fractionalFontSize.qml")); + QQuickWindow *window = helper.window; + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + const QQuickControl *control = window->property("control").value<QQuickControl *>(); + QVERIFY(control); + QQuickText *contentItem = qobject_cast<QQuickText *>(control->contentItem()); + QVERIFY(contentItem); + + QVERIFY(!contentItem->truncated()); + + QVERIFY2(qFuzzyCompare(contentItem->contentWidth(), + QQuickTextPrivate::get(contentItem)->layout.boundingRect().width()), + "The QQuickText::contentWidth() doesn't match the layout's preferred text width"); +} + QTEST_QUICKCONTROLS_MAIN(tst_QQuickControl) #include "tst_qquickcontrol.moc" diff --git a/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp b/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp index edcd1e7173..da3522d89d 100644 --- a/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp +++ b/tests/auto/quickcontrols2/qquickdrawer/tst_qquickdrawer.cpp @@ -34,6 +34,7 @@ #include <QtGui/qstylehints.h> #include <QtGui/qguiapplication.h> #include <QtGui/qpa/qwindowsysteminterface.h> +#include <QtQml/QQmlComponent> #include <QtQuick/private/qquickwindow_p.h> #include <QtQuick/private/qquickflickable_p.h> #include <QtQuickTestUtils/private/qmlutils_p.h> diff --git a/tests/auto/quickcontrols2/qquickheaderview/tst_qquickheaderview.cpp b/tests/auto/quickcontrols2/qquickheaderview/tst_qquickheaderview.cpp index 5f8ad65248..2dcb6a4a18 100644 --- a/tests/auto/quickcontrols2/qquickheaderview/tst_qquickheaderview.cpp +++ b/tests/auto/quickcontrols2/qquickheaderview/tst_qquickheaderview.cpp @@ -31,6 +31,7 @@ #include <QAbstractItemModelTester> #include <QtQml/QQmlEngine> +#include <QtQml/QQmlComponent> #include <QtQuick/private/qquickwindow_p.h> #include <QtQuick/private/qquicktext_p.h> #include <QtQuickTestUtils/private/qmlutils_p.h> diff --git a/tests/auto/quickcontrols2/qquickiconimage/BLACKLIST b/tests/auto/quickcontrols2/qquickiconimage/BLACKLIST new file mode 100644 index 0000000000..70c935375d --- /dev/null +++ b/tests/auto/quickcontrols2/qquickiconimage/BLACKLIST @@ -0,0 +1,15 @@ +# QTBUG-101006 +[nameBindingSourceSize] +android +[sourceBindingSourceSize] +android +[alignment] +android +[color] +android +[fileSelectors] +android +[imageProvider] +android +[translucentColors] +android diff --git a/tests/auto/quickcontrols2/qquickiconimage/tst_qquickiconimage.cpp b/tests/auto/quickcontrols2/qquickiconimage/tst_qquickiconimage.cpp index c35f8420f7..8c5c8780c9 100644 --- a/tests/auto/quickcontrols2/qquickiconimage/tst_qquickiconimage.cpp +++ b/tests/auto/quickcontrols2/qquickiconimage/tst_qquickiconimage.cpp @@ -410,9 +410,6 @@ void tst_qquickiconimage::color() { SKIP_IF_DPR_TOO_HIGH(); - if (QGuiApplication::platformName() == QLatin1String("offscreen")) - QSKIP("grabToImage() doesn't work on the \"offscreen\" platform plugin (QTBUG-63185)"); - QQuickView view(testFileUrl("color.qml")); QCOMPARE(view.status(), QQuickView::Ready); view.show(); @@ -468,9 +465,6 @@ void tst_qquickiconimage::fileSelectors() { SKIP_IF_DPR_TOO_HIGH(); - if (QGuiApplication::platformName() == QLatin1String("offscreen")) - QSKIP("grabToImage() doesn't work on the \"offscreen\" platform plugin (QTBUG-63185)"); - QQuickView view; QScopedPointer<QQmlFileSelector> fileSelector(new QQmlFileSelector(view.engine())); fileSelector->setExtraSelectors(QStringList() << "testselector"); @@ -513,9 +507,6 @@ public: // don't crash (QTBUG-63959) void tst_qquickiconimage::imageProvider() { - if (QGuiApplication::platformName() == QLatin1String("offscreen")) - QSKIP("grabToImage() doesn't work on the \"offscreen\" platform plugin (QTBUG-63185)"); - QQuickView view; view.engine()->addImageProvider("provider", new TestImageProvider); view.setSource(testFileUrl("imageProvider.qml")); @@ -545,9 +536,6 @@ void tst_qquickiconimage::imageProvider() */ void tst_qquickiconimage::translucentColors() { - if (QGuiApplication::platformName() == QLatin1String("offscreen")) - QSKIP("grabToImage() doesn't work on the \"offscreen\" platform plugin (QTBUG-63185)"); - // Doesn't reproduce with QQuickView. QQmlApplicationEngine engine; engine.load(testFileUrl("translucentColors.qml")); diff --git a/tests/auto/quickcontrols2/qquickiconlabel/data/iconSourceContext.qml b/tests/auto/quickcontrols2/qquickiconlabel/data/iconSourceContext.qml index 0d562d1500..8cf09666d0 100644 --- a/tests/auto/quickcontrols2/qquickiconlabel/data/iconSourceContext.qml +++ b/tests/auto/quickcontrols2/qquickiconlabel/data/iconSourceContext.qml @@ -1,7 +1,18 @@ -import QtQuick.Controls import QtQuick +import QtQuick.Controls.Basic +import QtQuick.Controls.impl +import "sub" as Sub Item { Image { source: "a.png" } IconLabel { icon.source: "a.png" } + Button { + icon.color: "transparent" + icon.source: "a.png" + } + Button { + action: actions.action + icon.color: "transparent" + Sub.Actions { id: actions } + } } diff --git a/tests/auto/quickcontrols2/qquickiconlabel/data/sub/Actions.qml b/tests/auto/quickcontrols2/qquickiconlabel/data/sub/Actions.qml new file mode 100644 index 0000000000..20bd6a5e6b --- /dev/null +++ b/tests/auto/quickcontrols2/qquickiconlabel/data/sub/Actions.qml @@ -0,0 +1,6 @@ +import QtQuick +import QtQuick.Controls.Basic + +QtObject { + readonly property Action action: Action { icon.source: "../a.png" } +} diff --git a/tests/auto/quickcontrols2/qquickiconlabel/tst_qquickiconlabel.cpp b/tests/auto/quickcontrols2/qquickiconlabel/tst_qquickiconlabel.cpp index 2ee0bf4c9f..21018d742f 100644 --- a/tests/auto/quickcontrols2/qquickiconlabel/tst_qquickiconlabel.cpp +++ b/tests/auto/quickcontrols2/qquickiconlabel/tst_qquickiconlabel.cpp @@ -30,6 +30,7 @@ #include <qtest.h> +#include <QtQml/QQmlComponent> #include <QtQuick/qquickitem.h> #include <QtQuick/qquickview.h> #include <QtQuick/qquickitemgrabresult.h> @@ -37,6 +38,7 @@ #include <QtQuick/private/qquickimage_p_p.h> #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/visualtestutils_p.h> +#include <QtQuickTemplates2/private/qquickabstractbutton_p.h> #include <QtQuickTemplates2/private/qquickicon_p.h> #include <QtQuickControls2Impl/private/qquickiconimage_p.h> #include <QtQuickControls2Impl/private/qquickiconlabel_p.h> @@ -303,9 +305,6 @@ void tst_qquickiconlabel::emptyIconSource() void tst_qquickiconlabel::colorChanges() { - if (QGuiApplication::platformName() == QLatin1String("offscreen")) - QSKIP("grabToImage() doesn't work on the \"offscreen\" platform plugin (QTBUG-63185)"); - QQuickView view(testFileUrl("colorChanges.qml")); QCOMPARE(view.status(), QQuickView::Ready); view.show(); @@ -345,7 +344,13 @@ void tst_qquickiconlabel::iconSourceContext() for (QQuickItem *child : root->childItems()) { QQuickImage *image = qobject_cast<QQuickImage *>(child); if (!image) { - if (QQuickIconLabel *label = qobject_cast<QQuickIconLabel *>(child)) { + QQuickIconLabel *label = nullptr; + if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(child)) { + label = qobject_cast<QQuickIconLabel *>(button->contentItem()); + } else { + label = qobject_cast<QQuickIconLabel *>(child); + } + if (label) { QQuickIconLabelPrivate *labelPrivate = static_cast<QQuickIconLabelPrivate *>( QQuickItemPrivate::get(label)); image = labelPrivate->image; diff --git a/tests/auto/quickcontrols2/qquicktreeviewdelegate/tst_qquicktreeviewdelegate.cpp b/tests/auto/quickcontrols2/qquicktreeviewdelegate/tst_qquicktreeviewdelegate.cpp index 021628709c..0478c97431 100644 --- a/tests/auto/quickcontrols2/qquicktreeviewdelegate/tst_qquicktreeviewdelegate.cpp +++ b/tests/auto/quickcontrols2/qquicktreeviewdelegate/tst_qquicktreeviewdelegate.cpp @@ -89,6 +89,8 @@ private slots: void showTreeView(); void expandAndCollapsUsingDoubleClick(); void expandAndCollapseClickOnIndicator(); + void expandAndCollapsUsingNonSupportedButtonAndModifers_data(); + void expandAndCollapsUsingNonSupportedButtonAndModifers(); void checkPropertiesRoot(); void checkPropertiesChildren(); }; @@ -163,6 +165,57 @@ void tst_qquicktreeviewdelegate::expandAndCollapseClickOnIndicator() QCOMPARE(treeViewPrivate->loadedRows.count(), 1); } +void tst_qquicktreeviewdelegate::expandAndCollapsUsingNonSupportedButtonAndModifers_data() +{ + QTest::addColumn<Qt::MouseButton>("button"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers"); + + QTest::newRow("left + Qt::ControlModifier") << Qt::LeftButton << Qt::KeyboardModifiers(Qt::ControlModifier); + QTest::newRow("left + Qt::ShiftModifier") << Qt::LeftButton << Qt::KeyboardModifiers(Qt::ShiftModifier); + QTest::newRow("left + Qt::AltModifier") << Qt::LeftButton << Qt::KeyboardModifiers(Qt::AltModifier); + QTest::newRow("left + Qt::MetaModifier") << Qt::LeftButton << Qt::KeyboardModifiers(Qt::MetaModifier); + QTest::newRow("left + Qt::ControlModifier + Qt::ShiftModifier") << Qt::LeftButton << (Qt::ShiftModifier | Qt::ControlModifier); + + QTest::newRow("right + Qt::NoModifier") << Qt::RightButton << Qt::KeyboardModifiers(Qt::ControlModifier); + QTest::newRow("right + Qt::ControlModifier") << Qt::RightButton << Qt::KeyboardModifiers(Qt::ShiftModifier); +} + +void tst_qquicktreeviewdelegate::expandAndCollapsUsingNonSupportedButtonAndModifers() +{ + QFETCH(Qt::MouseButton, button); + QFETCH(Qt::KeyboardModifiers, modifiers); + // Ensure that we don't expand or collapse the tree if the user is using the right mouse + // button, or holding down modifier keys. This "space" is reserved for application specific actions. + LOAD_TREEVIEW("unmodified.qml"); + + QCOMPARE(treeViewPrivate->loadedRows.count(), 1); + const auto item = treeView->itemAtCell(0, 0); + QVERIFY(item); + const QPoint localPos = QPoint(item->width() / 2, item->height() / 2); + const QPoint pos = item->window()->contentItem()->mapFromItem(item, localPos).toPoint(); + QTest::mouseDClick(item->window(), button, modifiers, pos); + + WAIT_UNTIL_POLISHED; + + QCOMPARE(treeViewPrivate->loadedRows.count(), 1); + + // Expand first row, and ensure we don't collapse it again + // if doing a double click together with Qt::CTRL. + QTest::mouseDClick(item->window(), Qt::LeftButton, Qt::NoModifier, pos); + + WAIT_UNTIL_POLISHED; + + // We now expect 5 rows, the root pluss it's 4 children + QCOMPARE(treeViewPrivate->loadedRows.count(), 5); + + QTest::mouseDClick(item->window(), button, modifiers, pos); + + WAIT_UNTIL_POLISHED; + + // We still expect 5 rows, the root pluss it's 4 children + QCOMPARE(treeViewPrivate->loadedRows.count(), 5); +} + void tst_qquicktreeviewdelegate::checkPropertiesRoot() { LOAD_TREEVIEW("unmodified.qml"); diff --git a/tests/auto/quickcontrols2/revisions/CMakeLists.txt b/tests/auto/quickcontrols2/revisions/CMakeLists.txt index 79a0f8e310..801dd6d66d 100644 --- a/tests/auto/quickcontrols2/revisions/CMakeLists.txt +++ b/tests/auto/quickcontrols2/revisions/CMakeLists.txt @@ -11,6 +11,7 @@ qt_internal_add_test(tst_revisions Qt::Gui Qt::Qml Qt::QuickControls2 + Qt::QuickTemplates2 ) ## Scopes: diff --git a/tests/auto/quickcontrols2/snippets/CMakeLists.txt b/tests/auto/quickcontrols2/snippets/CMakeLists.txt index 1d8ac11673..ce76c4fbc5 100644 --- a/tests/auto/quickcontrols2/snippets/CMakeLists.txt +++ b/tests/auto/quickcontrols2/snippets/CMakeLists.txt @@ -20,6 +20,7 @@ qt_internal_add_test(tst_snippets Qt::Quick Qt::QuickControls2 Qt::QuickControls2Private + Qt::QuickTemplates2 TESTDATA ${test_data} ) diff --git a/tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/dummy_imports.qml b/tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/dummy_imports.qml new file mode 100644 index 0000000000..aa37fd14b0 --- /dev/null +++ b/tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/dummy_imports.qml @@ -0,0 +1,8 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++. + +import QtQuick +import QtQuick.Controls.Basic + +QtObject { } diff --git a/tests/auto/quickdialogs/CMakeLists.txt b/tests/auto/quickdialogs/CMakeLists.txt index a3b5f15cb3..a8541bb3d5 100644 --- a/tests/auto/quickdialogs/CMakeLists.txt +++ b/tests/auto/quickdialogs/CMakeLists.txt @@ -1,4 +1,6 @@ +if(NOT ANDROID) # temporarily disabled until QTBUG-100991 is fixed add_subdirectory(qquickfiledialogimpl) add_subdirectory(qquickfolderdialogimpl) add_subdirectory(qquickfontdialogimpl) +endif() add_subdirectory(qquickmessagedialogimpl) diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST b/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST index 822b92dd19..1145255781 100644 --- a/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST +++ b/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST @@ -5,3 +5,6 @@ # QTBUG-92585 [fileMode:OpenFiles] * +#QTBUG-101488 +[goUp] +qnx diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/CMakeLists.txt b/tests/auto/quickdialogs/qquickfiledialogimpl/CMakeLists.txt index 64022f6293..096ebaacb1 100644 --- a/tests/auto/quickdialogs/qquickfiledialogimpl/CMakeLists.txt +++ b/tests/auto/quickdialogs/qquickfiledialogimpl/CMakeLists.txt @@ -21,6 +21,7 @@ qt_internal_add_test(tst_qquickfiledialogimpl Qt::QuickDialogs2QuickImplPrivate Qt::QuickDialogs2UtilsPrivate Qt::QuickPrivate + Qt::QuickTemplates2 Qt::QuickTemplates2Private Qt::QuickTest Qt::QuickTestUtilsPrivate diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/dummy_imports.qml b/tests/auto/quickdialogs/qquickfiledialogimpl/dummy_imports.qml new file mode 100644 index 0000000000..4ea33de671 --- /dev/null +++ b/tests/auto/quickdialogs/qquickfiledialogimpl/dummy_imports.qml @@ -0,0 +1,9 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++. + +import QtQuick.Controls +import QtQuick.Layouts +import Qt.labs.folderlistmodel + +QtObject { } diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp index e70c7e7171..a9fa0f21cc 100644 --- a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp +++ b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp @@ -123,6 +123,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, diff --git a/tests/auto/quickdialogs/qquickfolderdialogimpl/dummy_imports.qml b/tests/auto/quickdialogs/qquickfolderdialogimpl/dummy_imports.qml new file mode 100644 index 0000000000..7faa9b98e0 --- /dev/null +++ b/tests/auto/quickdialogs/qquickfolderdialogimpl/dummy_imports.qml @@ -0,0 +1,10 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++. + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt.labs.folderlistmodel + +QtObject { } diff --git a/tests/auto/quickdialogs/qquickfontdialogimpl/dummy_imports.qml b/tests/auto/quickdialogs/qquickfontdialogimpl/dummy_imports.qml new file mode 100644 index 0000000000..5b8af918ea --- /dev/null +++ b/tests/auto/quickdialogs/qquickfontdialogimpl/dummy_imports.qml @@ -0,0 +1,8 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++. + +import QtQuick +import QtQuick.Layouts + +QtObject { } diff --git a/tests/auto/quickdialogs/qquickmessagedialogimpl/dummy_imports.qml b/tests/auto/quickdialogs/qquickmessagedialogimpl/dummy_imports.qml new file mode 100644 index 0000000000..5b8af918ea --- /dev/null +++ b/tests/auto/quickdialogs/qquickmessagedialogimpl/dummy_imports.qml @@ -0,0 +1,8 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++. + +import QtQuick +import QtQuick.Layouts + +QtObject { } diff --git a/tests/auto/quicktest/quicktestmainwithsetup/CMakeLists.txt b/tests/auto/quicktest/quicktestmainwithsetup/CMakeLists.txt index c8900b38de..bca7d7fb00 100644 --- a/tests/auto/quicktest/quicktestmainwithsetup/CMakeLists.txt +++ b/tests/auto/quicktest/quicktestmainwithsetup/CMakeLists.txt @@ -10,6 +10,11 @@ file(GLOB_RECURSE test_data_glob ${CMAKE_CURRENT_SOURCE_DIR}/data/*.qml) list(APPEND test_data ${test_data_glob}) +file(GLOB_RECURSE test_imports_glob + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/imports/*) +list(APPEND test_data ${test_imports_glob}) + qt_internal_add_test(tst_quicktestmainwithsetup QMLTEST SOURCES diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt index 91628f0e51..386e72d153 100644 --- a/tests/manual/CMakeLists.txt +++ b/tests/manual/CMakeLists.txt @@ -3,7 +3,6 @@ add_subdirectory(highdpi) add_subdirectory(listview) add_subdirectory(mousearea) add_subdirectory(nodetypes_ng) -add_subdirectory(pointer) # add_subdirectory(qmltypememory) # TODO: C++ API changed # add_subdirectory(quickwidgetviewer) # TODO: C++ API changed add_subdirectory(scalablepath) diff --git a/tests/manual/treeview/sidebyside/data/treeview.qml b/tests/manual/treeview/sidebyside/data/treeview.qml index 6587e11f6e..a6fd99b076 100644 --- a/tests/manual/treeview/sidebyside/data/treeview.qml +++ b/tests/manual/treeview/sidebyside/data/treeview.qml @@ -48,6 +48,7 @@ ApplicationWindow { visible: true property alias treeView: treeView + property var selectedIndex: undefined UICallback { id: callback } @@ -92,6 +93,16 @@ ApplicationWindow { treeView.model.removeRows(index.row, 1, index.parent); } } + Button { + text: "Expand to" + enabled: selectedIndex != undefined + onClicked: { + treeView.expandToIndex(selectedIndex); + treeView.forceLayout() + let row = treeView.rowAtIndex(selectedIndex) + treeView.positionViewAtRow(row, Qt.AlignVCenter) + } + } } TreeView { @@ -114,15 +125,28 @@ ApplicationWindow { id: testModel } - Rectangle { - anchors.fill: parent - color: "white" - z: -1 - } - Component { id: treeViewDelegate TreeViewDelegate { + TapHandler { + acceptedModifiers: Qt.ControlModifier + onTapped: { + if (treeView.isExpanded(row)) + treeView.collapseRecursively(row) + else + treeView.expandRecursively(row) + } + } + TapHandler { + acceptedModifiers: Qt.ShiftModifier + onTapped: selectedIndex = treeView.modelIndex(row, 0) + } + Rectangle { + anchors.fill: parent + border.color: "red" + border.width: 1 + visible: treeView.modelIndex(row, column) === selectedIndex + } } } |