diff options
Diffstat (limited to 'tests')
49 files changed, 935 insertions, 91 deletions
diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp index 33efcb4da4..9fe2de5368 100644 --- a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp +++ b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp @@ -597,7 +597,7 @@ void SingleTest::run() void SingleTest::runExternalTest() { - auto runTest = [=] (const char *header, TestCase::Result *result) { + auto runTest = [this] (const char *header, TestCase::Result *result) { QTemporaryFile tempFile; tempFile.open(); tempFile.write(header); diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index e08a1cc37e..0ccb8210bc 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -249,6 +249,8 @@ private slots: void interrupt_data(); void interrupt(); + void triggerBackwardJumpWithDestructuring(); + public: Q_INVOKABLE QJSValue throwingCppMethod1(); Q_INVOKABLE void throwingCppMethod2(); @@ -4938,6 +4940,19 @@ void tst_QJSEngine::interrupt() #endif } +void tst_QJSEngine::triggerBackwardJumpWithDestructuring() +{ + QJSEngine engine; + auto value = engine.evaluate( + "function makeArray(n) { return [...Array(n).keys()]; }\n" + "for (let i=0;i<100;++i) {\n" + " let arr = makeArray(20)\n" + " arr.sort( (a, b) => b - a )\n" + "}" + ); + QVERIFY(!value.isError()); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" diff --git a/tests/auto/qml/qmllint/data/FromRootDirectParent.qml b/tests/auto/qml/qmllint/data/FromRootDirectParent.qml deleted file mode 100644 index c0bfd0f26b..0000000000 --- a/tests/auto/qml/qmllint/data/FromRootDirectParent.qml +++ /dev/null @@ -1,14 +0,0 @@ -import QtQuick 2.0 - -Item { - id: root - property int unqualified: 42 - - Item { - x: unqualified // user defined property from root - } - - QtObject { - property int check: x // existing property from root - } -} diff --git a/tests/auto/qml/qmllint/data/SignalHandler.qml b/tests/auto/qml/qmllint/data/SignalHandler.qml index bdf503e3dc..865277cedb 100644 --- a/tests/auto/qml/qmllint/data/SignalHandler.qml +++ b/tests/auto/qml/qmllint/data/SignalHandler.qml @@ -1,5 +1,13 @@ import QtQuick 2.0 MouseArea { - onDoubleClicked: console.log(mouse); + onDoubleClicked: { + console.log(mouse); + // do further things + } + onClicked: console.info(mouse) + onPositionChanged: { + console.log(mouse) + } + onPressAndHold: console.warn(mouse) } diff --git a/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml b/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml new file mode 100644 index 0000000000..a59b736929 --- /dev/null +++ b/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml @@ -0,0 +1,8 @@ +import QtQuick 2.12 +import QtQml 2.12 + +Item { + QtObject { + property int x: parent.x + } +} diff --git a/tests/auto/qml/qmllint/data/spuriousParentWarning.qml b/tests/auto/qml/qmllint/data/spuriousParentWarning.qml new file mode 100644 index 0000000000..1323593031 --- /dev/null +++ b/tests/auto/qml/qmllint/data/spuriousParentWarning.qml @@ -0,0 +1,7 @@ +import QtQuick 2.12 + +Item { + Unknown { + property int x: parent.x + } +} diff --git a/tests/auto/qml/qmllint/main.cpp b/tests/auto/qml/qmllint/main.cpp index 038826790b..928575bc82 100644 --- a/tests/auto/qml/qmllint/main.cpp +++ b/tests/auto/qml/qmllint/main.cpp @@ -40,6 +40,7 @@ private Q_SLOTS: void test_data(); void testUnqualified(); void testUnqualified_data(); + void testUnqualifiedNoSpuriousParentWarning(); private: QString m_qmllintPath; }; @@ -73,13 +74,14 @@ void TestQmllint::test_data() void TestQmllint::testUnqualified() { + auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath); QFETCH(QString, filename); QFETCH(QString, warningMessage); QFETCH(int, warningLine); QFETCH(int, warningColumn); filename.prepend(QStringLiteral("data/")); QStringList args; - args << QStringLiteral("-U") << filename; + args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir; QProcess process; process.start(m_qmllintPath, args); @@ -106,14 +108,41 @@ void TestQmllint::testUnqualified_data() // access property of root object QTest::newRow("FromRootDirect") << QStringLiteral("FromRoot.qml") << QStringLiteral("x: root.unqualified") << 9 << 16; // new property QTest::newRow("FromRootAccess") << QStringLiteral("FromRoot.qml") << QStringLiteral("property int check: root.x") << 13 << 33; // builtin property - // access property of root object from direct child - QTest::newRow("FromRootDirectParentDirect") << QStringLiteral("FromRootDirectParent.qml") << QStringLiteral("x: parent.unqualified") << 8 << 12; - QTest::newRow("FromRootDirectParentAccess") << QStringLiteral("FromRootDirectParent.qml") << QStringLiteral("property int check: parent.x") << 12 << 29; // access injected name from signal - QTest::newRow("SignalHandler") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onDoubleClicked: function(mouse) {...") << 4 << 34; + QTest::newRow("SignalHandler1") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onDoubleClicked: function(mouse) {...") << 5 << 21; + QTest::newRow("SignalHandler2") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onPositionChanged: function(mouse) {...") << 10 << 21; + QTest::newRow("SignalHandlerShort1") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onClicked: (mouse) => {...") << 8 << 29; + QTest::newRow("SignalHandlerShort2") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onPressAndHold: (mouse) => {...") << 12 << 34; } +void TestQmllint::testUnqualifiedNoSpuriousParentWarning() +{ + auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath); + { + QString filename = QLatin1String("spuriousParentWarning.qml"); + filename.prepend(QStringLiteral("data/")); + QStringList args; + args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir; + QProcess process; + process.start(m_qmllintPath, args); + QVERIFY(process.waitForFinished()); + QVERIFY(process.exitStatus() == QProcess::NormalExit); + QVERIFY(process.exitCode() == 0); + } + { + QString filename = QLatin1String("nonSpuriousParentWarning.qml"); + filename.prepend(QStringLiteral("data/")); + QStringList args; + args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir; + QProcess process; + process.start(m_qmllintPath, args); + QVERIFY(process.waitForFinished()); + QVERIFY(process.exitStatus() == QProcess::NormalExit); + QVERIFY(process.exitCode()); + } +} + void TestQmllint::test() { QFETCH(QString, filename); diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp index 696ec66246..cae833cd60 100644 --- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp +++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp @@ -130,6 +130,8 @@ void tst_qmlmin::initTestCase() invalidFiles << "tests/auto/qml/qjsengine/script/com/trolltech/syntaxerror/__init__.js"; invalidFiles << "tests/auto/qml/debugger/qqmlpreview/data/broken.qml"; invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.2.qml"; + // generatorFunction.qml is not invalid per se, but the minifier cannot handle yield statements + invalidFiles << "tests/auto/qml/qqmlecmascript/data/generatorFunction.qml"; #endif } diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp index a9c28a0911..8787a43884 100644 --- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp @@ -96,6 +96,9 @@ void tst_qqmlapplicationengine::basicLoading() // will break. void tst_qqmlapplicationengine::testNonResolvedPath() { +#ifdef Q_OS_ANDROID + QSKIP("Android stores QML files in resources, and the path to a resource cannot be relative in this case"); +#endif { // NOTE NOTE NOTE! Missing testFileUrl is *WANTED* here! We want a // non-resolved URL. @@ -117,6 +120,9 @@ void tst_qqmlapplicationengine::testNonResolvedPath() void tst_qqmlapplicationengine::application_data() { +#ifdef Q_OS_ANDROID + QSKIP("Cannot launch external process on Android"); +#endif QTest::addColumn<QByteArray>("qmlFile"); QTest::addColumn<QByteArray>("expectedStdErr"); diff --git a/tests/auto/qml/qqmldirparser/CMakeLists.txt b/tests/auto/qml/qqmldirparser/CMakeLists.txt index 21a8fa44ae..30efe97fed 100644 --- a/tests/auto/qml/qqmldirparser/CMakeLists.txt +++ b/tests/auto/qml/qqmldirparser/CMakeLists.txt @@ -4,6 +4,14 @@ ## tst_qqmldirparser Test: ##################################################################### +# Collect test data + +file(GLOB test_data_glob + LIST_DIRECTORIES true + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + "data/*") +list(APPEND test_data ${test_data_glob}) + add_qt_test(tst_qqmldirparser GUI SOURCES @@ -16,17 +24,12 @@ add_qt_test(tst_qqmldirparser PUBLIC_LIBRARIES Qt::Gui Qt::Qml + TESTDATA ${test_data} ) -#### Keys ignored in scope 1:.:.:qqmldirparser.pro:<TRUE>: -# CONFIG = "testcase" - ## Scopes: ##################################################################### -#### Keys ignored in scope 2:.:.:qqmldirparser.pro:APPLE_OSX: -# CONFIG = "-app_bundle" - extend_target(tst_qqmldirparser CONDITION ANDROID OR APPLE_IOS DEFINES QT_QMLTEST_DATADIR=\\\":/data\\\" diff --git a/tests/auto/qml/qqmldirparser/qqmldirparser.pro b/tests/auto/qml/qqmldirparser/qqmldirparser.pro index dda74b1ef9..b5373a6e8f 100644 --- a/tests/auto/qml/qqmldirparser/qqmldirparser.pro +++ b/tests/auto/qml/qqmldirparser/qqmldirparser.pro @@ -6,3 +6,5 @@ macx:CONFIG -= app_bundle SOURCES += tst_qqmldirparser.cpp include (../../shared/util.pri) + +TESTDATA = data/* diff --git a/tests/auto/qml/qqmlecmascript/data/generatorFunction.qml b/tests/auto/qml/qqmlecmascript/data/generatorFunction.qml new file mode 100644 index 0000000000..66bb642f34 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/generatorFunction.qml @@ -0,0 +1,22 @@ +import QtQml 2.12 + +QtObject { + id: root + property bool test1: false; + property bool test2: false; + property bool test3: false; + property bool done: false; + function *gen() { + yield 1 + yield 2 + yield 3 + } + + Component.onCompleted: { + let it = root.gen(); + root.test1 = (it.next().value == 1); + root.test2 = (it.next().value == 2); + root.test3 = (it.next().value == 3); + root.done = it.next().done; + } +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index d603ca6907..b44fe9766c 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -233,6 +233,7 @@ private slots: void functionAssignment_afterBinding(); void eval(); void function(); + void topLevelGeneratorFunction(); void qtbug_10696(); void qtbug_11606(); void qtbug_11600(); @@ -2048,7 +2049,7 @@ void tst_qqmlecmascript::functionErrors() QObject *resource = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ScarceResourceObject(0x%1) is not a function"); - warning = warning.arg(QString::number((qintptr)resource, 16)); + warning = warning.arg(QString::number((quintptr)resource, 16)); QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed. QMetaObject::invokeMethod(object, "retrieveScarceResource"); delete object; @@ -4573,7 +4574,7 @@ void tst_qqmlecmascript::scarceResources_other() eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ScarceResourceObject(0x%1) is not a function"); - expectedWarning = expectedWarning.arg(QString::number((qintptr)eo, 16)); + expectedWarning = expectedWarning.arg(QString::number((quintptr)eo, 16)); QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed. QMetaObject::invokeMethod(object, "retrieveScarceResource"); QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred. @@ -4647,7 +4648,7 @@ void tst_qqmlecmascript::scarceResources_other() eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ScarceResourceObject(0x%1) is not a function"); - expectedWarning = expectedWarning.arg(QString::number((qintptr)eo, 16)); + expectedWarning = expectedWarning.arg(QString::number((quintptr)eo, 16)); QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed. QMetaObject::invokeMethod(object, "retrieveScarceResource"); QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred. @@ -6348,6 +6349,28 @@ void tst_qqmlecmascript::function() delete o; } +// QTBUG-77096 +void tst_qqmlecmascript::topLevelGeneratorFunction() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("generatorFunction.qml")); + + QScopedPointer<QObject> o {component.create()}; + QVERIFY(o != nullptr); + + // check that generator works correctly in QML + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + QCOMPARE(o->property("done").toBool(), true); + + // check that generator is accessible from C++ + QVariant returnedValue; + QMetaObject::invokeMethod(o.get(), "gen", Q_RETURN_ARG(QVariant, returnedValue)); + auto it = returnedValue.value<QJSValue>(); + QCOMPARE(it.property("next").callWithInstance(it).property("value").toInt(), 1); +} + // Test the "Qt.include" method void tst_qqmlecmascript::include() { diff --git a/tests/auto/qml/qqmlengine/CMakeLists.txt b/tests/auto/qml/qqmlengine/CMakeLists.txt index 5f38633234..e4c38d384c 100644 --- a/tests/auto/qml/qqmlengine/CMakeLists.txt +++ b/tests/auto/qml/qqmlengine/CMakeLists.txt @@ -4,6 +4,14 @@ ## tst_qqmlengine Test: ##################################################################### +# Collect test data + +file(GLOB test_data_glob + LIST_DIRECTORIES true + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + "data/*") +list(APPEND test_data ${test_data_glob}) + add_qt_test(tst_qqmlengine GUI SOURCES @@ -19,27 +27,26 @@ add_qt_test(tst_qqmlengine Qt::Gui Qt::Network Qt::Qml + TESTDATA ${test_data} ) # Resources: +set(qmake_immediate_resource_files + "data/qrcurls.js" + "data/qrcurls.qml" +) + add_qt_resource(tst_qqmlengine "qmake_immediate" PREFIX "/" FILES - data/qrcurls.js - data/qrcurls.qml + ${qmake_immediate_resource_files} ) -#### Keys ignored in scope 1:.:.:qqmlengine.pro:<TRUE>: -# CONFIG = "testcase" - ## Scopes: ##################################################################### -#### Keys ignored in scope 2:.:.:qqmlengine.pro:APPLE_OSX: -# CONFIG = "-app_bundle" - extend_target(tst_qqmlengine CONDITION boot2qt DEFINES SKIP_GCCORRUPTION_TEST diff --git a/tests/auto/qml/qqmlengine/qqmlengine.pro b/tests/auto/qml/qqmlengine/qqmlengine.pro index d2eb92bfd5..8946201ea0 100644 --- a/tests/auto/qml/qqmlengine/qqmlengine.pro +++ b/tests/auto/qml/qqmlengine/qqmlengine.pro @@ -4,6 +4,8 @@ macx:CONFIG -= app_bundle include (../../shared/util.pri) +TESTDATA = data/* + SOURCES += tst_qqmlengine.cpp QT += core-private gui-private qml-private network testlib diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 66d50cfe39..aae42e9ebb 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -428,7 +428,7 @@ void tst_qqmlengine::trimComponentCache() engine.setIncubationController(&componentCache); QQmlComponent component(&engine, testFileUrl(file)); - QVERIFY(component.isReady()); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("success").toBool(), true); @@ -742,13 +742,17 @@ public: CustomSelector(const QUrl &base):m_base(base){} virtual QUrl intercept(const QUrl &url, QQmlAbstractUrlInterceptor::DataType d) { - if (url.scheme() != QStringLiteral("file")) + if ((url.scheme() != QStringLiteral("file") && url.scheme() != QStringLiteral("qrc")) + || url.path().contains("QtQml")) return url; if (!m_interceptionPoints.contains(d)) return url; - if (url.path().endsWith("Test.2/qmldir"))//Special case - return QUrl::fromLocalFile(m_base.path() + "interception/module/intercepted/qmldir"); + if (url.path().endsWith("Test.2/qmldir")) {//Special case + QUrl url = m_base; + url.setPath(m_base.path() + "interception/module/intercepted/qmldir"); + return url; + } // Special case: with 5.10 we always add the implicit import, so we need to explicitly handle this case now if (url.path().endsWith("intercepted/qmldir")) return url; @@ -836,7 +840,7 @@ void tst_qqmlengine::urlInterceptor() QFETCH(QString, expectedAbsoluteUrl); QQmlEngine e; - e.addImportPath(testFileUrl("interception/imports").toLocalFile()); + e.addImportPath(testFileUrl("interception/imports").url()); CustomSelector cs(testFileUrl("")); cs.m_interceptionPoints = interceptionPoint; e.setUrlInterceptor(&cs); @@ -1033,6 +1037,46 @@ void tst_qqmlengine::singletonInstance() { qmlRegisterSingletonType<CppSingleton>("Qt.test",1,0,"NotAmbiguous", [](QQmlEngine* qeng, QJSEngine* jeng) -> QObject* {return CppSingleton::create(qeng, jeng);}); // test that overloads for qmlRegisterSingleton are not ambiguous } + { + // Register QObject* directly + CppSingleton single; + int id = qmlRegisterSingletonInstance("Qt.test", 1, 0, "CppOwned", + &single); + QQmlEngine engine2; + CppSingleton *singlePtr = engine2.singletonInstance<CppSingleton *>(id); + QVERIFY(singlePtr); + QCOMPARE(&single, singlePtr); + QVERIFY(engine2.objectOwnership(singlePtr) == QQmlEngine::CppOwnership); + } + + { + CppSingleton single; + QQmlEngine engineA; + QQmlEngine engineB; + int id = qmlRegisterSingletonInstance("Qt.test", 1, 0, "CppOwned", &single); + auto singlePtr = engineA.singletonInstance<CppSingleton *>(id); + QVERIFY(singlePtr); + singlePtr = engineA.singletonInstance<CppSingleton *>(id); // accessing the singleton multiple times from the same engine is fine + QVERIFY(singlePtr); + QTest::ignoreMessage(QtMsgType::QtCriticalMsg, "<Unknown File>: qmlRegisterSingletonType(): \"CppOwned\" is not available because the callback function returns a null pointer."); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: Singleton registered by registerSingletonInstance must only be accessed from one engine"); + QCOMPARE(&single, singlePtr); + auto noSinglePtr = engineB.singletonInstance<CppSingleton *>(id); + QVERIFY(!noSinglePtr); + } + + { + CppSingleton single; + QThread newThread {}; + single.moveToThread(&newThread); + QCOMPARE(single.thread(), &newThread); + QQmlEngine engineB; + int id = qmlRegisterSingletonInstance("Qt.test", 1, 0, "CppOwned", &single); + QTest::ignoreMessage(QtMsgType::QtCriticalMsg, "<Unknown File>: qmlRegisterSingletonType(): \"CppOwned\" is not available because the callback function returns a null pointer."); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: Registered object must live in the same thread as the engine it was registered with"); + auto noSinglePtr = engineB.singletonInstance<CppSingleton *>(id); + QVERIFY(!noSinglePtr); + } { // Invalid types diff --git a/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp b/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp index 7a7185e909..f282e60417 100644 --- a/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp +++ b/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp @@ -195,7 +195,7 @@ void tst_qqmlerror::debug() } { - QUrl url(dataDirectoryUrl().resolved(QUrl("test.txt"))); + QUrl url = testFileUrl("test.txt"); QQmlError error; error.setUrl(url); error.setDescription("An Error"); diff --git a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp index 1decc04ad2..0ba29d6b6a 100644 --- a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp +++ b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp @@ -28,6 +28,7 @@ #include <qtest.h> #include <QtQml/qqmlengine.h> +#include <QtQml/qqmlfile.h> #include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlexpression.h> #include <QtQml/qqmlscriptstring.h> @@ -125,10 +126,12 @@ void tst_qqmlexpression::expressionFromDataComponent() QQmlEngine engine; QQmlComponent c(&engine); - QUrl url = testFileUrl("expressionFromDataComponent.qml"); + const QString fn(QLatin1String("expressionFromDataComponent.qml")); + QUrl url = testFileUrl(fn); + QString path = testFile(fn); { - QFile f(url.toLocalFile()); + QFile f(path); QVERIFY(f.open(QIODevice::ReadOnly)); c.setData(f.readAll(), url); } diff --git a/tests/auto/qml/qqmlimport/CMakeLists.txt b/tests/auto/qml/qqmlimport/CMakeLists.txt index 71f71c7326..50b588cb04 100644 --- a/tests/auto/qml/qqmlimport/CMakeLists.txt +++ b/tests/auto/qml/qqmlimport/CMakeLists.txt @@ -4,6 +4,32 @@ ## tst_qqmlimport Test: ##################################################################### +# Collect test data + +file(GLOB test_data_glob + LIST_DIRECTORIES true + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + "data/*") +list(APPEND test_data ${test_data_glob}) + +file(GLOB test_data_glob + LIST_DIRECTORIES true + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + "MyPluginSupported/*") +list(APPEND test_data ${test_data_glob}) + +file(GLOB test_data_glob + LIST_DIRECTORIES true + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + "MyPluginUnsupported/*") +list(APPEND test_data ${test_data_glob}) + +file(GLOB test_data_glob + LIST_DIRECTORIES true + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + "FormFromQmlDir/*") +list(APPEND test_data ${test_data_glob}) + add_qt_test(tst_qqmlimport GUI SOURCES @@ -17,17 +43,12 @@ add_qt_test(tst_qqmlimport Qt::Gui Qt::Qml Qt::Quick + TESTDATA ${test_data} ) -#### Keys ignored in scope 1:.:.:qqmlimport.pro:<TRUE>: -# CONFIG = "testcase" - ## Scopes: ##################################################################### -#### Keys ignored in scope 2:.:.:qqmlimport.pro:APPLE_OSX: -# CONFIG = "-app_bundle" - extend_target(tst_qqmlimport CONDITION ANDROID OR APPLE_IOS DEFINES QT_QMLTEST_DATADIR=\\\":/data\\\" diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MyComponent_0_9.qml b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MyComponent_0_9.qml new file mode 100644 index 0000000000..a63e2d121c --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MyComponent_0_9.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + +Rectangle { + width: 50 + height: 50 + + color: "yellow" + + Text { + anchors.centerIn: parent + text: "0.9" + + } +} diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MyComponent_1_0.qml b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MyComponent_1_0.qml new file mode 100644 index 0000000000..d9cff582bb --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MyComponent_1_0.qml @@ -0,0 +1,15 @@ + +import QtQuick 2.0 + +Rectangle { + width: 50 + height: 50 + + color: "orange" + + Text { + anchors.centerIn: parent + text: "1.0" + + } +} diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MySettings_1_0.qml b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MySettings_1_0.qml new file mode 100644 index 0000000000..63ad6ba1a5 --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MySettings_1_0.qml @@ -0,0 +1,7 @@ +pragma Singleton + +import QtQuick 2.0 + +Item { + property int baseWidth: 50 +} diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/qmldir b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/qmldir new file mode 100644 index 0000000000..57b8c55f81 --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/qmldir @@ -0,0 +1,6 @@ +module MyPlugin + +MyComponent 0.9 MyComponent_0_9.qml + +MyComponent 1.0 MyComponent_1_0.qml +singleton MySettings 1.0 MySettings_1_0.qml diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.0.9.fail.qml b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.0.9.fail.qml new file mode 100644 index 0000000000..0fbcec607a --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.0.9.fail.qml @@ -0,0 +1,11 @@ +import QtQuick 2.12 +import MyPlugin 0.9 + +Item { + width: MySettings.baseWidth + height: 100 + + MyComponent { + anchors.centerIn: parent + } +} diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.0.9.qml b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.0.9.qml new file mode 100644 index 0000000000..53fc25699c --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.0.9.qml @@ -0,0 +1,11 @@ +import QtQuick 2.12 +import MyPlugin 0.9 + +Item { + width: 100 + height: 100 + + MyComponent { + anchors.centerIn: parent + } +} diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.1.0.qml b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.1.0.qml new file mode 100644 index 0000000000..1f6244068b --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.1.0.qml @@ -0,0 +1,11 @@ +import QtQuick 2.12 +import MyPlugin 1.0 + +Item { + width: MySettings.baseWidth + height: 100 + + MyComponent { + anchors.centerIn: parent + } +} diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.nonumber.qml b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.nonumber.qml new file mode 100644 index 0000000000..dea00c0163 --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.nonumber.qml @@ -0,0 +1,11 @@ +import QtQuick 2.12 +import "imports/MyPlugin" + +Item { + width: MySettings.baseWidth + height: 100 + + MyComponent { + anchors.centerIn: parent + } +} diff --git a/tests/auto/qml/qqmlimport/qqmlimport.pro b/tests/auto/qml/qqmlimport/qqmlimport.pro index c8b0f68d9f..ba80547f4e 100644 --- a/tests/auto/qml/qqmlimport/qqmlimport.pro +++ b/tests/auto/qml/qqmlimport/qqmlimport.pro @@ -6,3 +6,8 @@ osx:CONFIG -= app_bundle SOURCES += tst_qqmlimport.cpp include (../../shared/util.pri) + +TESTDATA = data/* \ + MyPluginSupported/* \ + MyPluginUnsupported/* \ + FormFromQmlDir/* diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp index a3cb68fdcb..ca1e52ad2c 100644 --- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp +++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp @@ -45,6 +45,7 @@ private slots: void completeQmldirPaths_data(); void completeQmldirPaths(); void interceptQmldir(); + void singletonVersionResolution(); void cleanup(); }; @@ -77,7 +78,7 @@ void tst_QQmlImport::testDesignerSupported() QVERIFY(window->errors().isEmpty()); QString warningString("%1:30:1: module does not support the designer \"MyPluginUnsupported\" \n import MyPluginUnsupported 1.0\r \n ^ "); -#ifndef Q_OS_WIN +#if !defined(Q_OS_WIN) && !defined(Q_OS_ANDROID) warningString.remove('\r'); #endif warningString = warningString.arg(testFileUrl("testfile_unsupported.qml").toString()); @@ -129,6 +130,9 @@ void tst_QQmlImport::uiFormatLoading() void tst_QQmlImport::importPathOrder() { +#ifdef Q_OS_ANDROID + QSKIP("QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath) returns bogus path on Android, but its nevertheless unusable."); +#endif QStringList expectedImportPaths; QString appDirPath = QCoreApplication::applicationDirPath(); QString qml2Imports = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath); @@ -212,6 +216,50 @@ void tst_QQmlImport::interceptQmldir() QVERIFY(!obj.isNull()); } +// QTBUG-77102 +void tst_QQmlImport::singletonVersionResolution() +{ + QQmlEngine engine; + engine.addImportPath(testFile("QTBUG-77102/imports")); + { + // Singleton with higher version is simply ignored when importing lower version of plugin + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("QTBUG-77102/main.0.9.qml")); + QVERIFY(component.isReady()); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + } + { + // but the singleton is not accessible + QQmlComponent component(&engine); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, QRegularExpression {".*ReferenceError: MySettings is not defined$"} ); + component.loadUrl(testFileUrl("QTBUG-77102/main.0.9.fail.qml")); + QVERIFY(component.isReady()); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + } + { + // unless a version which is high enough is imported + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("QTBUG-77102/main.1.0.qml")); + QVERIFY(component.isReady()); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + auto item = qobject_cast<QQuickItem*>(obj.get()); + QCOMPARE(item->width(), 50); + } + { + // or when there is no number because we are importing from a path + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("QTBUG-77102/main.nonumber.qml")); + QVERIFY(component.isReady()); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + auto item = qobject_cast<QQuickItem*>(obj.get()); + QCOMPARE(item->width(), 50); + } +} + QTEST_MAIN(tst_QQmlImport) diff --git a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp index cc13fb4b5f..a90749208c 100644 --- a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp +++ b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp @@ -32,6 +32,8 @@ #include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlcontext.h> #include <QtCore/QDateTime> +#include <QtCore/qscopeguard.h> +#include <QtCore/qscopedpointer.h> #include <qcolor.h> #include "../../shared/util.h" @@ -1270,13 +1272,21 @@ void tst_qqmllocale::timeZoneUpdated() // Set the timezone to Brisbane time, AEST-10:00 setTimeZone(QByteArray("Australia/Brisbane")); + QScopedPointer<QObject> obj; + auto cleanup = qScopeGuard([&original, &obj] { + // Restore to original time zone + setTimeZone(original); + QMetaObject::invokeMethod(obj.data(), "resetTimeZone"); + }); + DateFormatter formatter; QQmlEngine e; e.rootContext()->setContextObject(&formatter); QQmlComponent c(&e, testFileUrl("timeZoneUpdated.qml")); - QScopedPointer<QObject> obj(c.create()); + QVERIFY2(!c.isError(), qPrintable(c.errorString())); + obj.reset(c.create()); QVERIFY(obj); QVERIFY(obj->property("success").toBool()); @@ -1284,11 +1294,6 @@ void tst_qqmllocale::timeZoneUpdated() setTimeZone(QByteArray("Asia/Kolkata")); QMetaObject::invokeMethod(obj.data(), "check"); - - // Reset to original time - setTimeZone(original); - QMetaObject::invokeMethod(obj.data(), "resetTimeZone"); - QVERIFY(obj->property("success").toBool()); } #endif diff --git a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp index a5332c8860..de762d66c5 100644 --- a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp +++ b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp @@ -342,6 +342,9 @@ void tst_qqmlnotifier::lotsOfBindings() void tst_qqmlnotifier::deleteFromHandler() { +#ifdef Q_OS_ANDROID + QSKIP("Android seems to have problems with QProcess"); +#endif #if !QT_CONFIG(process) QSKIP("Need QProcess support to test qFatal."); #else diff --git a/tests/auto/qml/qqmlpromise/data/promisechain.qml b/tests/auto/qml/qqmlpromise/data/promisechain.qml new file mode 100644 index 0000000000..fa1809aef0 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promisechain.qml @@ -0,0 +1,24 @@ +import QtQml 2.0 + +QtObject { + property int x: 0 + id: root; + Component.onCompleted: { + new Promise((res) => { + res(1) + }) + .then((data) => { + console.debug(data) + return new Promise((res) => {res(2)}); + }) + .then((data) => { + console.debug(data) + return new Promise((res) => {res(3)}); + }) + .then((data) => { + console.debug(data); + root.x = 42; + }); + } + +} diff --git a/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp b/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp index 0f4bb5cdcc..41850d0263 100644 --- a/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp +++ b/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp @@ -82,6 +82,7 @@ private slots: void then_fulfilled_non_callable(); void then_reject_non_callable(); void then_resolve_multiple_then(); + void promiseChain(); private: void execute_test(QString testName); @@ -270,6 +271,20 @@ void tst_qqmlpromise::execute_test(QString testName) QTRY_COMPARE(object->property("wasTestSuccessful").toBool(), true); } +void tst_qqmlpromise::promiseChain() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("promisechain.qml")); + QVERIFY(component.isReady()); + QTest::ignoreMessage(QtDebugMsg, "1"); + QTest::ignoreMessage(QtDebugMsg, "2"); + QTest::ignoreMessage(QtDebugMsg, "3"); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root); + QTRY_VERIFY(root->property("x") == 42); + +} + QTEST_MAIN(tst_qqmlpromise) diff --git a/tests/auto/qml/qqmlproperty/data/nullPropertyBinding.qml b/tests/auto/qml/qqmlproperty/data/nullPropertyBinding.qml new file mode 100644 index 0000000000..4fffc7aead --- /dev/null +++ b/tests/auto/qml/qqmlproperty/data/nullPropertyBinding.qml @@ -0,0 +1,22 @@ +import QtQuick 2.12 + +Item { + id: root + + width: 640 + height: 480 + + property bool toggle: false + property Item bound + property string message: "defined" + + readonly property Item item: root.toggle ? root : null + + Binding { target: root; property: "bound"; value: item} + + function tog() { + console.info(root.bound ? root.bound.message: "undefined") + root.toggle = !root.toggle + return 42; + } +} diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index 04c61ec11b..67da768f73 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -144,6 +144,7 @@ private slots: void deeplyNestedObject(); void readOnlyDynamicProperties(); void aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSystem(); + void nullPropertyBinding(); void floatToStringPrecision_data(); void floatToStringPrecision(); @@ -2059,6 +2060,22 @@ void tst_qqmlproperty::aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSyst QVERIFY(property.isValid()); } +// QTBUG-77027 +void tst_qqmlproperty::nullPropertyBinding() +{ + const QUrl url = testFileUrl("nullPropertyBinding.qml"); + QQmlEngine engine; + QQmlComponent component(&engine, url); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root); + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "undefined"); + QMetaObject::invokeMethod(root.get(), "tog"); + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "defined"); + QMetaObject::invokeMethod(root.get(), "tog"); + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "undefined"); + QMetaObject::invokeMethod(root.get(), "tog"); +} + void tst_qqmlproperty::floatToStringPrecision_data() { QTest::addColumn<QString>("propertyName"); diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml new file mode 100644 index 0000000000..dc7e5f6411 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 + +Item { + width: 640 + height: 480 + property alias touchpointPressed: tp1.pressed + + Rectangle { + color: tp1.pressed ? "lightsteelblue" : drag.active ? "tomato" : "wheat" + width: 180 + height: 180 + + DragHandler { id: drag } + + MultiPointTouchArea { + anchors.fill: parent + touchPoints: [ + TouchPoint { id: tp1 } + ] + } + } +} diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp index 58bc3d40b5..35fed99e8b 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp @@ -56,6 +56,7 @@ private slots: void touchDrag(); void touchesThenPinch(); void unloadHandlerWithPassiveGrab(); + void dragHandlerInParentStealingGrabFromItem(); private: void createView(QScopedPointer<QQuickView> &window, const char *fileName); @@ -300,6 +301,48 @@ void tst_MptaInterop::unloadHandlerWithPassiveGrab() QTest::mouseRelease(window, Qt::LeftButton, 0, point); // QTBUG-73819: don't crash } +void tst_MptaInterop::dragHandlerInParentStealingGrabFromItem() // QTBUG-75025 +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "dragParentOfMPTA.qml"); + QQuickView * window = windowPtr.data(); + auto pointerEvent = QQuickWindowPrivate::get(window)->pointerEventInstance(QQuickPointerDevice::genericMouseDevice()); + + QPointer<QQuickPointerHandler> handler = window->rootObject()->findChild<QQuickPointerHandler*>(); + QVERIFY(handler); + QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>(); + QVERIFY(mpta); + + // In QTBUG-75025 there is a QQ Controls Button; the MPTA here stands in for that, + // simply as an Item that grabs the mouse. The bug has nothing to do with MPTA specifically. + + QPoint point(20, 20); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, point); + QCOMPARE(window->mouseGrabberItem(), mpta); + QCOMPARE(window->rootObject()->property("touchpointPressed").toBool(), true); + + // Start dragging + // DragHandler keeps monitoring, due to its passive grab, + // and eventually steals the exclusive grab from MPTA + int dragStoleGrab = 0; + for (int i = 0; i < 4; ++i) { + point += QPoint(dragThreshold / 2, 0); + QTest::mouseMove(window, point); + if (!dragStoleGrab && pointerEvent->point(0)->exclusiveGrabber() == handler) + dragStoleGrab = i; + } + if (dragStoleGrab) + qCDebug(lcPointerTests, "DragHandler stole the grab after %d events", dragStoleGrab); + QVERIFY(dragStoleGrab > 1); + QCOMPARE(handler->active(), true); + QCOMPARE(window->rootObject()->property("touchpointPressed").toBool(), false); + + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, point); + QCOMPARE(handler->active(), false); + QCOMPARE(window->rootObject()->property("touchpointPressed").toBool(), false); +} + QTEST_MAIN(tst_MptaInterop) #include "tst_multipointtoucharea_interop.moc" diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp index 7636bb38f1..66314f88a2 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp @@ -54,6 +54,7 @@ private slots: void defaultPropertyValues(); void touchDrag(); + void mouseDrag_data(); void mouseDrag(); void dragFromMargin(); void snapMode_data(); @@ -195,8 +196,22 @@ void tst_DragHandler::touchDrag() QCOMPARE(centroidChangedSpy.count(), 5); } +void tst_DragHandler::mouseDrag_data() +{ + QTest::addColumn<Qt::MouseButtons>("acceptedButtons"); + QTest::addColumn<Qt::MouseButtons>("dragButton"); + QTest::newRow("left: drag") << Qt::MouseButtons(Qt::LeftButton) << Qt::MouseButtons(Qt::LeftButton); + QTest::newRow("right: don't drag") << Qt::MouseButtons(Qt::LeftButton) << Qt::MouseButtons(Qt::RightButton); + QTest::newRow("left: don't drag") << Qt::MouseButtons(Qt::RightButton | Qt::MiddleButton) << Qt::MouseButtons(Qt::LeftButton); + QTest::newRow("right or middle: drag") << Qt::MouseButtons(Qt::RightButton | Qt::MiddleButton) << Qt::MouseButtons(Qt::MiddleButton); +} + void tst_DragHandler::mouseDrag() { + QFETCH(Qt::MouseButtons, acceptedButtons); + QFETCH(Qt::MouseButtons, dragButton); + bool shouldDrag = bool(acceptedButtons & dragButton); + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); QScopedPointer<QQuickView> windowPtr; createView(windowPtr, "draggables.qml"); @@ -206,6 +221,7 @@ void tst_DragHandler::mouseDrag() QVERIFY(ball); QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>(); QVERIFY(dragHandler); + dragHandler->setAcceptedButtons(acceptedButtons); // QTBUG-76875 QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged())); QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged())); @@ -213,45 +229,57 @@ void tst_DragHandler::mouseDrag() QPointF ballCenter = ball->clipRect().center(); QPointF scenePressPos = ball->mapToScene(ballCenter); QPoint p1 = scenePressPos.toPoint(); - QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTest::mousePress(window, static_cast<Qt::MouseButton>(int(dragButton)), Qt::NoModifier, p1); QVERIFY(!dragHandler->active()); - QCOMPARE(dragHandler->centroid().position(), ballCenter); - QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter); - QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos); - QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); - QCOMPARE(dragHandler->centroid().velocity(), QVector2D()); - QCOMPARE(centroidChangedSpy.count(), 1); + if (shouldDrag) { + QCOMPARE(dragHandler->centroid().position(), ballCenter); + QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter); + QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos); + QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); + QCOMPARE(dragHandler->centroid().velocity(), QVector2D()); + QCOMPARE(centroidChangedSpy.count(), 1); + } p1 += QPoint(dragThreshold, 0); QTest::mouseMove(window, p1); - QTRY_VERIFY(dragHandler->centroid().velocity().x() > 0); - QCOMPARE(centroidChangedSpy.count(), 2); - QVERIFY(!dragHandler->active()); + if (shouldDrag) { + QTRY_VERIFY(dragHandler->centroid().velocity().x() > 0); + QCOMPARE(centroidChangedSpy.count(), 2); + QVERIFY(!dragHandler->active()); + } p1 += QPoint(1, 0); QTest::mouseMove(window, p1); - QTRY_VERIFY(dragHandler->active()); + if (shouldDrag) + QTRY_VERIFY(dragHandler->active()); + else + QVERIFY(!dragHandler->active()); QCOMPARE(translationChangedSpy.count(), 0); - QCOMPARE(centroidChangedSpy.count(), 3); + if (shouldDrag) + QCOMPARE(centroidChangedSpy.count(), 3); QCOMPARE(dragHandler->translation().x(), 0.0); QPointF sceneGrabPos = p1; - QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); + if (shouldDrag) + QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); p1 += QPoint(19, 0); QTest::mouseMove(window, p1); - QTRY_VERIFY(dragHandler->active()); - QCOMPARE(dragHandler->centroid().position(), ballCenter); - QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter); - QCOMPARE(dragHandler->centroid().scenePosition(), ball->mapToScene(ballCenter)); - QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); - QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); - QCOMPARE(dragHandler->translation().x(), dragThreshold + 20.0); - QCOMPARE(dragHandler->translation().y(), 0.0); - QVERIFY(dragHandler->centroid().velocity().x() > 0); - QCOMPARE(centroidChangedSpy.count(), 4); - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QVERIFY(shouldDrag ? dragHandler->active() : !dragHandler->active()); + if (shouldDrag) { + QCOMPARE(dragHandler->centroid().position(), ballCenter); + QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter); + QCOMPARE(dragHandler->centroid().scenePosition(), ball->mapToScene(ballCenter)); + QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); + QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); + QCOMPARE(dragHandler->translation().x(), dragThreshold + 20.0); + QCOMPARE(dragHandler->translation().y(), 0.0); + QVERIFY(dragHandler->centroid().velocity().x() > 0); + QCOMPARE(centroidChangedSpy.count(), 4); + } + QTest::mouseRelease(window, static_cast<Qt::MouseButton>(int(dragButton)), Qt::NoModifier, p1); QTRY_VERIFY(!dragHandler->active()); QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton); - QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1); - QCOMPARE(translationChangedSpy.count(), 1); - QCOMPARE(centroidChangedSpy.count(), 5); + if (shouldDrag) + QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1); + QCOMPARE(translationChangedSpy.count(), shouldDrag ? 1 : 0); + QCOMPARE(centroidChangedSpy.count(), shouldDrag ? 5 : 0); } void tst_DragHandler::dragFromMargin() // QTBUG-74966 diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml index 221e7df139..042b730799 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml @@ -34,6 +34,7 @@ Rectangle { property alias pressed: tap.pressed property bool checked: false property alias gesturePolicy: tap.gesturePolicy + property point tappedPosition: Qt.point(0, 0) signal tapped signal canceled @@ -51,6 +52,7 @@ Rectangle { longPressThreshold: 100 // CI can be insanely slow, so don't demand a timely release to generate onTapped onTapped: { tapFlash.start() + root.tappedPosition = point.scenePosition root.tapped() } onCanceled: root.canceled() diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp index 33cea69147..e77ea97518 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp @@ -107,6 +107,8 @@ void tst_TapHandler::touchGesturePolicyDragThreshold() QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); QVERIFY(buttonDragThreshold); + QQuickTapHandler *tapHandler = buttonDragThreshold->findChild<QQuickTapHandler*>(); + QVERIFY(tapHandler); QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped())); // DragThreshold button stays pressed while touchpoint stays within dragThreshold, emits tapped on release @@ -122,6 +124,8 @@ void tst_TapHandler::touchGesturePolicyDragThreshold() QQuickTouchUtils::flush(window); QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); QCOMPARE(dragThresholdTappedSpy.count(), 1); + QCOMPARE(buttonDragThreshold->property("tappedPosition").toPoint(), p1); + QCOMPARE(tapHandler->point().position(), QPointF()); // DragThreshold button is no longer pressed if touchpoint goes beyond dragThreshold dragThresholdTappedSpy.clear(); @@ -152,6 +156,8 @@ void tst_TapHandler::mouseGesturePolicyDragThreshold() QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); QVERIFY(buttonDragThreshold); + QQuickTapHandler *tapHandler = buttonDragThreshold->findChild<QQuickTapHandler*>(); + QVERIFY(tapHandler); QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped())); // DragThreshold button stays pressed while mouse stays within dragThreshold, emits tapped on release @@ -164,6 +170,8 @@ void tst_TapHandler::mouseGesturePolicyDragThreshold() QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); QTRY_COMPARE(dragThresholdTappedSpy.count(), 1); + QCOMPARE(buttonDragThreshold->property("tappedPosition").toPoint(), p1); + QCOMPARE(tapHandler->point().position(), QPointF()); // DragThreshold button is no longer pressed if mouse goes beyond dragThreshold dragThresholdTappedSpy.clear(); diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 65a08ce87f..2314b82e8c 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -1652,6 +1652,7 @@ void tst_qquickflickable::flickTwiceUsingTouches() QQuickViewTestUtil::moveMouseAway(window.data()); window->show(); QVERIFY(window->rootObject() != nullptr); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); QVERIFY(flickable != nullptr); @@ -1994,6 +1995,7 @@ void tst_qquickflickable::nestedMouseAreaUsingTouch() QQuickViewTestUtil::moveMouseAway(window.data()); window->show(); QVERIFY(window->rootObject() != nullptr); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); QVERIFY(flickable != nullptr); @@ -2489,6 +2491,7 @@ void tst_qquickflickable::synchronousDrag() QQuickViewTestUtil::moveMouseAway(window); window->show(); QVERIFY(window->rootObject() != nullptr); + QVERIFY(QTest::qWaitForWindowActive(window)); QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); QVERIFY(flickable != nullptr); diff --git a/tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml b/tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml new file mode 100644 index 0000000000..851d8f9a0c --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 + +ListView { + id: listView + property var lastItem + anchors.fill: parent + model: 10 + delegate: Rectangle { + width: parent.width + height: 40 + border.color: "lightsteelblue" + Text { + text: "Item" + (index + 1) + } + Component.onCompleted: { + if (index == 9) + listView.lastItem = this + } + } + + populate: Transition { + NumberAnimation { + properties: "x,y" + duration: 1000 + } + } +} diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index f14a6e75f6..bab2ec34f8 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -278,6 +278,7 @@ private slots: void addOnCompleted(); void setPositionOnLayout(); void touchCancel(); + void resizeAfterComponentComplete(); private: template <class T> void items(const QUrl &source); @@ -9020,6 +9021,21 @@ void tst_QQuickListView::touchCancel() // QTBUG-74679 QTRY_COMPARE(listview->contentY(), 500.0); } +void tst_QQuickListView::resizeAfterComponentComplete() // QTBUG-76487 +{ + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("resizeAfterComponentComplete.qml")); + window->resize(640, 480); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QObject *listView = window->rootObject(); + QVERIFY(listView); + + QObject *lastItem = qvariant_cast<QObject *>(listView->property("lastItem")); + QTRY_COMPARE(lastItem->property("y").toInt(), 9 * lastItem->property("height").toInt()); +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" diff --git a/tests/auto/quick/qquickshape/data/pathitem8.png b/tests/auto/quick/qquickshape/data/pathitem8.png Binary files differnew file mode 100644 index 0000000000..ee585958ec --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem8.png diff --git a/tests/auto/quick/qquickshape/data/pathitem8.qml b/tests/auto/quick/qquickshape/data/pathitem8.qml new file mode 100644 index 0000000000..9789ff90e0 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem8.qml @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 +import QtQuick.Shapes 1.14 + +Item { + id: item + width: 200 + height: 150 + + Shape { + vendorExtensionsEnabled: false + objectName: "shape" + id: shape + anchors.fill: parent + + ShapePath { + strokeWidth: 4 + strokeColor: "red" + scale: Qt.size(shape.width - 1, shape.height - 1) + fillGradient: LinearGradient { + x1: 20; y1: 20 + x2: 180; y2: 130 + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } + } + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + PathMultiline { + paths: [[Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)), + Qt.point(180.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)), + Qt.point(20.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)), + Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)) ], + [Qt.point( 0.45 , 0.67 ), + Qt.point( 0.414906666468 , 0.573581858547 ), + Qt.point( 0.32604722665 , 0.522278837048 ), + Qt.point( 0.225 , 0.540096189432 ), + Qt.point( 0.159046106882 , 0.618696978501 ), + Qt.point( 0.159046106882 , 0.721303021499 ), + Qt.point( 0.225 , 0.799903810568 ), + Qt.point( 0.32604722665 , 0.817721162952 ), + Qt.point( 0.414906666468 , 0.766418141453 ), + Qt.point( 0.45 , 0.67 ), + ], + [Qt.point( 0.69 , 0.75 ), + Qt.point( 0.668943999881 , 0.692149115128 ), + Qt.point( 0.61562833599 , 0.661367302229 ), + Qt.point( 0.555 , 0.672057713659 ), + Qt.point( 0.515427664129 , 0.719218187101 ), + Qt.point( 0.515427664129 , 0.780781812899 ), + Qt.point( 0.555 , 0.827942286341 ), + Qt.point( 0.61562833599 , 0.838632697771 ), + Qt.point( 0.668943999881 , 0.807850884872 ), + Qt.point( 0.69 , 0.75 ), + ]] + } + } + } +} diff --git a/tests/auto/quick/qquickshape/tst_qquickshape.cpp b/tests/auto/quick/qquickshape/tst_qquickshape.cpp index 174ada65a5..b3b8d2d148 100644 --- a/tests/auto/quick/qquickshape/tst_qquickshape.cpp +++ b/tests/auto/quick/qquickshape/tst_qquickshape.cpp @@ -59,6 +59,7 @@ private slots: void radialGrad(); void conicalGrad(); void renderPolyline(); + void renderMultiline(); }; tst_QQuickShape::tst_QQuickShape() @@ -343,6 +344,35 @@ void tst_QQuickShape::renderPolyline() QVERIFY2(res, qPrintable(errorMessage)); } +void tst_QQuickShape::renderMultiline() +{ + QScopedPointer<QQuickView> window(createView()); + + window->setSource(testFileUrl("pathitem8.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + if ((QGuiApplication::platformName() == QLatin1String("offscreen")) + || (QGuiApplication::platformName() == QLatin1String("minimal"))) + QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimimal platforms", Abort); + + QImage img = window->grabWindow(); + QVERIFY(!img.isNull()); + + QImage refImg(testFileUrl("pathitem8.png").toLocalFile()); + QVERIFY(!refImg.isNull()); + + QString errorMessage; + const QImage actualImg = img.convertToFormat(refImg.format()); + const bool res = QQuickVisualTestUtil::compareImages(actualImg, refImg, &errorMessage); + if (!res) { // For visual inspection purposes. + QTest::qWait(5000); + const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation); + actualImg.save(tempLocation + QLatin1String("/pathitem8.png")); + } + QVERIFY2(res, qPrintable(errorMessage)); +} + QTEST_MAIN(tst_QQuickShape) #include "tst_qquickshape.moc" diff --git a/tests/auto/quick/qquicktext/BLACKLIST b/tests/auto/quick/qquicktext/BLACKLIST index 6c3c3af154..594f9af3b3 100644 --- a/tests/auto/quick/qquicktext/BLACKLIST +++ b/tests/auto/quick/qquicktext/BLACKLIST @@ -4,3 +4,5 @@ qemu osx-10.12 [fontSizeMode] opensuse-42.1 +[contentSize] +windows gcc diff --git a/tests/manual/nodetypes_ng/MultiClipRects.qml b/tests/manual/nodetypes_ng/MultiClipRects.qml index 793ebeae93..2d3804af21 100644 --- a/tests/manual/nodetypes_ng/MultiClipRects.qml +++ b/tests/manual/nodetypes_ng/MultiClipRects.qml @@ -61,8 +61,8 @@ Item { GradientStop { position: 1; color: "black" } } - // Clip using scissor, 2 levels. - // This means that the lightGreen-yellow-blue batch's clip list will have two clips. + // Clip using scissor, up to 2 levels. This means that the + // lightGreen-yellow-blue batch's clip list will have two clips. Row { spacing: 10 Repeater { @@ -103,9 +103,9 @@ Item { } } - // Clip using stencil, 2 levels. - // This means that the lightGreen-yellow batch's clip list will have two clips - // and so two stencil draw calls before drawing the actual content. + // Clip using stencil, up to 3 levels. This means that the + // lightGreen-yellow-blue batch's clip list will have three clips and + // so two stencil draw calls before drawing the actual content. Row { spacing: 10 Repeater { @@ -133,6 +133,16 @@ Item { x: 75 y: 75 NumberAnimation on rotation { from: 360; to: 0; duration: 5000; loops: Animation.Infinite; } + clip: true + + Rectangle { + color: "blue" + width: 50 + height: 50 + x: 0 + y: 0 + NumberAnimation on rotation { from: 360; to: 0; duration: 5000; loops: Animation.Infinite; } + } } } } diff --git a/tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml b/tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml new file mode 100644 index 0000000000..8524915bc4 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 +import QtQuick.Shapes 1.14 + +Item { + id: root + width: 320 + height: 320 + + Shape { + vendorExtensionsEnabled: false + anchors.fill: parent + + ShapePath { + strokeWidth: 1 + strokeColor: "red" + fillColor: Qt.rgba(1,0,0,0.3) + scale: Qt.size(root.width - 1, root.height - 1) + PathMultiline { + paths: [ + [Qt.point(0.5, 0.06698), + Qt.point(1, 0.93301), + Qt.point(0, 0.93301), + Qt.point(0.5, 0.06698)], + + [Qt.point(0.5, 0.12472), + Qt.point(0.95, 0.90414), + Qt.point(0.05, 0.90414), + Qt.point(0.5, 0.12472)], + + [Qt.point(0.47131, 0.32986), + Qt.point(0.36229, 0.64789), + Qt.point(0.51492, 0.58590), + Qt.point(0.47563, 0.76014), + Qt.point(0.44950, 0.73590), + Qt.point(0.46292, 0.83392), + Qt.point(0.52162, 0.75190), + Qt.point(0.48531, 0.76230), + Qt.point(0.57529, 0.53189), + Qt.point(0.41261, 0.59189), + Qt.point(0.53001, 0.32786), + Qt.point(0.47131, 0.32986)] + ] + } + } + } +} |