From 131243b3c8d71febe5294f0035844cea8a9e3049 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 6 Sep 2017 10:30:03 +0200 Subject: Stabilize the threadSignal() test We've seen the case in the CI where we delete the worker thread object before it has had a chance (resulting in a crash). This patch attempts to stabilize this by waiting for the thread to terminate properly. In addition QSignalSpy's connection to the done(QString) signal is forced to be direct, which means the spy's internal list is accessed from the gui thread (via QCOMPARE) at the same time as the thread may be emitting the signal and calling the signalspy's slot (metacall), which helgrind complains about (rightly so). I don't see any purpose in connecting to the signal, so let's remove that code. The test continues to cover the threading code in QQmlData::signalEmitted, once as the thread is triggered via C++ and once via QML (doIt invocation). Change-Id: I5e8a4ae65e2d0890a26491d25c73de1ba33a6668 Reviewed-by: Ulf Hermann --- tests/auto/qml/qqmlecmascript/testtypes.cpp | 9 ++++++++- tests/auto/qml/qqmlecmascript/testtypes.h | 5 +++++ .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 22 +++++++++------------- 3 files changed, 22 insertions(+), 14 deletions(-) (limited to 'tests/auto/qml') diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp index 63c2918325..13bb0435cd 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.cpp +++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp @@ -245,9 +245,16 @@ public: MyWorkerObject *o; }; +MyWorkerObject::~MyWorkerObject() +{ + if (m_thread) + m_thread->wait(); +} + void MyWorkerObject::doIt() { - new MyWorkerObjectThread(this); + Q_ASSERT(!m_thread); + m_thread = new MyWorkerObjectThread(this); } class MyDateClass : public QObject diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h index eedeb66647..e15a05a00c 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.h +++ b/tests/auto/qml/qqmlecmascript/testtypes.h @@ -1532,12 +1532,17 @@ private: class MyWorkerObject : public QObject { Q_OBJECT +public: + ~MyWorkerObject(); public Q_SLOTS: void doIt(); Q_SIGNALS: void done(const QString &result); + +private: + QThread *m_thread = 0; }; class MyUnregisteredEnumTypeObject : public QObject diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 7b9a43dc38..14c2aa18bf 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -7440,21 +7440,17 @@ void tst_qqmlecmascript::signalEmitted() void tst_qqmlecmascript::threadSignal() { { - QQmlComponent c(&engine, testFileUrl("threadSignal.qml")); - QObject *object = c.create(); - QVERIFY(object != 0); - QTRY_VERIFY(object->property("passed").toBool()); - delete object; + QQmlComponent c(&engine, testFileUrl("threadSignal.qml")); + QScopedPointer object(c.create()); + QVERIFY(!object.isNull()); + QTRY_VERIFY(object->property("passed").toBool()); } { - QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml")); - QObject *object = c.create(); - QVERIFY(object != 0); - QSignalSpy doneSpy(object, SIGNAL(done(QString))); - QMetaObject::invokeMethod(object, "doIt"); - QTRY_VERIFY(object->property("passed").toBool()); - QCOMPARE(doneSpy.count(), 1); - delete object; + QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml")); + QScopedPointer object(c.create()); + QVERIFY(!object.isNull()); + QMetaObject::invokeMethod(object.data(), "doIt"); + QTRY_VERIFY(object->property("passed").toBool()); } } -- cgit v1.2.3 From e22b624d9ab1f36021adb9cdbfa9b37054282bb8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Aug 2017 16:29:46 +0200 Subject: Fix crashes with closures created in QML components When closures created inside QML components are called after the surrounding component (and consequently QML context) has been destroyed, we are in a somewhat limited environment. Initially we would just crash as the calling QML context is not valid anymore. We can alleviate that by introducing reference counting on the context and letting the QML context wrapper keep a strong reference. This avoids the crashes and also ensures that at least imports continue to be accessible within these contexts (as the singleton test case demonstrates). Task-number: QTBUG-61781 Change-Id: I893f171842d01b0863d95a02ea738adc2620e236 Reviewed-by: Lars Knoll --- tests/auto/qml/qqmlcontext/data/Singleton.qml | 5 ++++ .../data/contextViaClosureAfterDestruction.qml | 14 +++++++++++ tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp | 28 ++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 tests/auto/qml/qqmlcontext/data/Singleton.qml create mode 100644 tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml (limited to 'tests/auto/qml') diff --git a/tests/auto/qml/qqmlcontext/data/Singleton.qml b/tests/auto/qml/qqmlcontext/data/Singleton.qml new file mode 100644 index 0000000000..68ef5850e3 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/Singleton.qml @@ -0,0 +1,5 @@ +pragma Singleton +import QtQml 2.0 +QtObject { + readonly property string song: "Highway to Hell" +} diff --git a/tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml b/tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml new file mode 100644 index 0000000000..2e0e6f20e2 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml @@ -0,0 +1,14 @@ +import QtQml 2.0 + +import constants 1.0 + +QtObject { + function createClosure() { + return function() { return Sing.song; } + } + function createComponentFactory() { + return function(parentObj) { + return Qt.createQmlObject('import QtQml 2.0; QtObject { property string test: "ok"; }', parentObj); + } + } +} diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp index f49fd391ac..5f57b9ebb0 100644 --- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp +++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp @@ -62,6 +62,7 @@ private slots: void evalAfterInvalidate(); void qobjectDerived(); void qtbug_49232(); + void contextViaClosureAfterDestruction(); private: QQmlEngine engine; @@ -723,6 +724,33 @@ void tst_qqmlcontext::qtbug_49232() QCOMPARE(obj->property("valueTwo"), QVariant(97)); } +void tst_qqmlcontext::contextViaClosureAfterDestruction() +{ + qmlRegisterSingletonType(testFileUrl("Singleton.qml"), "constants", 1, 0, "Sing"); + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("contextViaClosureAfterDestruction.qml")); + QJSValue valueClosure; + QJSValue componentFactoryClosure; + { + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + // meta-calls don't support QJSValue return types, so do the call "by hand" + valueClosure = engine.newQObject(obj.data()).property(QStringLiteral("createClosure")).call(); + QVERIFY(valueClosure.isCallable()); + componentFactoryClosure = engine.newQObject(obj.data()).property(QStringLiteral("createComponentFactory")).call(); + QVERIFY(componentFactoryClosure.isCallable()); + } + QCOMPARE(valueClosure.call().toString(), QLatin1String("Highway to Hell")); + + QScopedPointer parent(new QObject); + QJSValue parentWrapper = engine.newQObject(parent.data()); + QQmlEngine::setObjectOwnership(parent.data(), QQmlEngine::CppOwnership); + + QJSValue subObject = componentFactoryClosure.callWithInstance(componentFactoryClosure, QJSValueList() << parentWrapper); + QVERIFY(subObject.isError()); + QCOMPARE(subObject.toString(), QLatin1String("Error: Qt.createQmlObject(): Cannot create a component in an invalid context")); +} + QTEST_MAIN(tst_qqmlcontext) #include "tst_qqmlcontext.moc" -- cgit v1.2.3 From a32cf1a22d096b33340cddbe91328c6c088e221d Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 6 Sep 2017 16:26:27 +0200 Subject: Disable deferring when referenced as a grouped property This allows us to fix QTBUG-50992 - the issue with most votes in QQC2. Task-number: QTBUG-63036 Change-Id: I996cd1128582b80e0c8480ae143d682c1e8eb8fe Reviewed-by: Simon Hausmann Reviewed-by: Qt CI Bot --- .../qqmllanguage/data/MyLazyDeferredSubObject.qml | 6 ++++++ .../qml/qqmllanguage/data/lazyDeferredSubObject.qml | 5 +++++ tests/auto/qml/qqmllanguage/testtypes.cpp | 2 ++ tests/auto/qml/qqmllanguage/testtypes.h | 20 ++++++++++++++++++++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 16 ++++++++++++++++ 5 files changed, 49 insertions(+) create mode 100644 tests/auto/qml/qqmllanguage/data/MyLazyDeferredSubObject.qml create mode 100644 tests/auto/qml/qqmllanguage/data/lazyDeferredSubObject.qml (limited to 'tests/auto/qml') diff --git a/tests/auto/qml/qqmllanguage/data/MyLazyDeferredSubObject.qml b/tests/auto/qml/qqmllanguage/data/MyLazyDeferredSubObject.qml new file mode 100644 index 0000000000..f311f6b602 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/MyLazyDeferredSubObject.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +import Test 1.0 +LazyDeferredSubObject { + subObject: QtObject { objectName: 'default' } + objectName: subObject.objectName +} diff --git a/tests/auto/qml/qqmllanguage/data/lazyDeferredSubObject.qml b/tests/auto/qml/qqmllanguage/data/lazyDeferredSubObject.qml new file mode 100644 index 0000000000..2465a18320 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lazyDeferredSubObject.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 +import Test 1.0 +MyLazyDeferredSubObject { + subObject.objectName: 'custom' +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index bdcdaa8137..72e06d26aa 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -103,6 +103,8 @@ void registerTypes() qmlRegisterSingletonType("Test", 1, 0, "MyTypeObjectSingleton", myTypeObjectSingleton); qmlRegisterType("Test", 1, 0, "MyArrayBufferTestClass"); + + qmlRegisterType("Test", 1, 0, "LazyDeferredSubObject"); } QVariant myCustomVariantTypeConverter(const QString &data) diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 7d7a8ac6d3..b0e677feb8 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -1331,6 +1331,26 @@ private: QObject *obj; }; +class LazyDeferredSubObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QObject *subObject READ subObject WRITE setSubObject NOTIFY subObjectChanged FINAL) + Q_CLASSINFO("DeferredPropertyNames", "subObject"); +public: + LazyDeferredSubObject() + : obj(0) + {} + + QObject *subObject() const { if (!obj) qmlExecuteDeferred(const_cast(this)); return obj; } + void setSubObject(QObject *o) { if (obj == o) return; obj = o; emit subObjectChanged(); } + +signals: + void subObjectChanged(); + +private: + QObject *obj; +}; + void registerTypes(); #endif // TESTTYPES_H diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 39f5082c70..8b84ee878f 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -247,6 +247,7 @@ private slots: void propertyCacheInSync(); void rootObjectInCreationNotForSubObjects(); + void lazyDeferredSubObject(); void noChildEvents(); @@ -4242,6 +4243,21 @@ void tst_qqmllanguage::rootObjectInCreationNotForSubObjects() QVERIFY(!ddata->rootObjectInCreation); } +// QTBUG-63036 +void tst_qqmllanguage::lazyDeferredSubObject() +{ + QQmlComponent component(&engine, testFile("lazyDeferredSubObject.qml")); + VERIFY_ERRORS(0); + QScopedPointer object(component.create()); + QVERIFY(!object.isNull()); + + QObject *subObject = qvariant_cast(object->property("subObject")); + QVERIFY(subObject); + + QCOMPARE(object->objectName(), QStringLiteral("custom")); + QCOMPARE(subObject->objectName(), QStringLiteral("custom")); +} + void tst_qqmllanguage::noChildEvents() { QQmlComponent component(&engine); -- cgit v1.2.3 From ebda8170a6b00b830fba59a230506ab3ca52e6c5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Sep 2017 10:03:48 +0200 Subject: Get rid of the root object index variable This is a follow-up to the parent commit to remove the variable that is really a constant (zero). Change-Id: I8fc20027c5c7b871269b814cb8b93636e94be267 Reviewed-by: Lars Knoll --- tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 2 +- tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'tests/auto/qml') diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 8b84ee878f..30c34426af 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -2136,7 +2136,7 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData; td->compilationUnit()->data = qmlUnit; - const QV4::CompiledData::Object *rootObject = qmlUnit->objectAt(qmlUnit->indexOfRootObject); + const QV4::CompiledData::Object *rootObject = qmlUnit->objectAt(/*root object*/0); QCOMPARE(qmlUnit->stringAt(rootObject->inheritedTypeNameIndex), QString("MyTypeObject")); quint32 i; for (i = 0; i < rootObject->nBindings; ++i) { diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp index 1fc803a395..dd4edeb97d 100644 --- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp +++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp @@ -85,7 +85,7 @@ void tst_qqmltranslation::translation() << QStringLiteral("singular") << QStringLiteral("plural"); const QV4::CompiledData::Unit *unit = compilationUnit->data; - const QV4::CompiledData::Object *rootObject = unit->objectAt(unit->indexOfRootObject); + const QV4::CompiledData::Object *rootObject = unit->objectAt(/*root object*/0); const QV4::CompiledData::Binding *binding = rootObject->bindingTable(); for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { const QString propertyName = unit->stringAt(binding->propertyNameIndex); @@ -140,7 +140,7 @@ void tst_qqmltranslation::idTranslation() QVERIFY(compilationUnit); const QV4::CompiledData::Unit *unit = compilationUnit->data; - const QV4::CompiledData::Object *rootObject = unit->objectAt(unit->indexOfRootObject); + const QV4::CompiledData::Object *rootObject = unit->objectAt(/*root object*/0); const QV4::CompiledData::Binding *binding = rootObject->bindingTable(); for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { const QString propertyName = unit->stringAt(binding->propertyNameIndex); -- cgit v1.2.3 From c1fed764a2495373a9e4563bc3ac0d578b2f9409 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 8 Aug 2017 12:39:54 +0200 Subject: Fix reuse of regexp objects by regexp literals Accoding to the standard the regexp objects created by literals should be separate objects as if calling new. We were violating that by caching the same object for every instance of a literal. This also fixes a problem with leaking values of lastIndex between separate instances of the same global regexp literal. Task-number: QTBUG-62175 Change-Id: Ib22e9ee68de1d1209fbd4212e72f576bc059d245 Reviewed-by: Simon Hausmann --- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'tests/auto/qml') diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 2642d10545..8b815f7a06 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -145,6 +145,7 @@ private slots: void array_join_QTBUG_53672(); void regexpLastMatch(); + void regexpLastIndex(); void indexedAccesses(); void prototypeChainGc(); @@ -3296,6 +3297,28 @@ void tst_QJSEngine::regexpLastMatch() } +void tst_QJSEngine::regexpLastIndex() +{ + QJSEngine eng; + QJSValue result; + result = eng.evaluate("function test(text, rx) {" + " var res;" + " while (res = rx.exec(text)) { " + " return true;" + " }" + " return false;" + " }" + "function tester(text) {" + " return test(text, /,\\s*/g);" + "}"); + QVERIFY(!result.isError()); + + result = eng.evaluate("tester(\", \\n\");"); + QVERIFY(result.toBool()); + result = eng.evaluate("tester(\", \\n\");"); + QVERIFY(result.toBool()); +} + void tst_QJSEngine::indexedAccesses() { QJSEngine engine; -- cgit v1.2.3 From 98358715930739ca8de172d88c5ce6941c275ff3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Sep 2017 15:13:33 +0200 Subject: Fix qml cache invalidation when changing dependent C++ registered QML singletons When a qml file uses a qml singleton, we need to reliably detect when the singleton changes and re-generate the cache of the qml file using it. This is a scenario covered and fixed by commit 5b94de09cc738837d1539e28b3c0dccd17c18d29, with the exception that currently QML singletons registered via qmlRegisterSingleton were not added to the list of dependent singletons for a qml file. We can fix this by extending findCompositeSingletons() to also cover the singletons that do not originate from a qmldir file. [ChangeLog][Qt][Qml] Fixed bug where sometimes changes to a qml singleton would not propagate to the users or cause crashes. Task-number: QTBUG-62243 Change-Id: I16c3d9ba65fd82e898a29b946c341907751135a9 Reviewed-by: Lars Knoll --- tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 61 ++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'tests/auto/qml') diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index 6ab84774f2..e75e51ed29 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -59,6 +59,7 @@ private slots: void cacheResources(); void stableOrderOfDependentCompositeTypes(); void singletonDependency(); + void cppRegisteredSingletonDependency(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -790,6 +791,66 @@ void tst_qmldiskcache::singletonDependency() } } +void tst_qmldiskcache::cppRegisteredSingletonDependency() +{ + qmlClearTypeRegistrations(); + QScopedPointer engine(new QQmlEngine); + + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + writeTempFile("MySingleton.qml", "import QtQml 2.0\npragma Singleton\nQtObject { property int value: 42 }"); + + qmlRegisterSingletonType(QUrl::fromLocalFile(tempDir.path() + QLatin1String("/MySingleton.qml")), "CppRegisteredSingletonDependency", 1, 0, "Singly"); + + const QString testFilePath = writeTempFile("main.qml", "import QtQml 2.0\nimport CppRegisteredSingletonDependency 1.0\nQtObject {\n" + " function getValue() { return Singly.value; }\n" + "}"); + + { + CleanlyLoadingComponent component(engine.data(), QUrl::fromLocalFile(testFilePath)); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + QVariant value; + QVERIFY(QMetaObject::invokeMethod(obj.data(), "getValue", Q_RETURN_ARG(QVariant, value))); + QCOMPARE(value.toInt(), 42); + } + + const QString testFileCachePath = testFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(testFileCachePath)); + QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); + + engine.reset(new QQmlEngine); + waitForFileSystem(); + + writeTempFile("MySingleton.qml", "import QtQml 2.0\npragma Singleton\nQtObject { property int value: 100 }"); + waitForFileSystem(); + + { + CleanlyLoadingComponent component(engine.data(), QUrl::fromLocalFile(testFilePath)); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + + { + QVERIFY(QFile::exists(testFileCachePath)); + QDateTime newCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); + QVERIFY2(newCacheTimeStamp > initialCacheTimeStamp, qPrintable(newCacheTimeStamp.toString())); + } + + QVariant value; + QVERIFY(QMetaObject::invokeMethod(obj.data(), "getValue", Q_RETURN_ARG(QVariant, value))); + QCOMPARE(value.toInt(), 100); + } +} + QTEST_MAIN(tst_qmldiskcache) #include "tst_qmldiskcache.moc" -- cgit v1.2.3 From fa00f0a0206f87b43cd1ee5448efe20cb6ff8e44 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Sep 2017 17:20:47 +0200 Subject: Error out when compiling signal handlers with arguments in qml files Ahead of time we cannot tell whether the use of "arguments" in a signal hander refers to the JS arguments object or a potential arguments signal parameter. Resolving that requires access to information we currently don't have. The QML engine has it at run-time (in SignalHandlerConverter) and that's why it works there accordingly. However when generating caches ahead of time, let's rather produce an error message with a hint how to work around it instead of producing differing behavior at run-time. Task-number: QTBUG-60011 Change-Id: I9e460bd467dbb5998f12a44c439223ea44e7bbad Reviewed-by: Lars Knoll --- tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp | 35 ++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) (limited to 'tests/auto/qml') diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index b7e616a050..b69071dd59 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -43,6 +43,7 @@ private slots: void loadGeneratedFile(); void translationExpressionSupport(); + void errorOnArgumentsInSignalHandler(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -67,15 +68,20 @@ public: } }; -static bool generateCache(const QString &qmlFileName) +static bool generateCache(const QString &qmlFileName, QByteArray *capturedStderr = nullptr) { QProcess proc; - proc.setProcessChannelMode(QProcess::ForwardedChannels); + if (capturedStderr == nullptr) + proc.setProcessChannelMode(QProcess::ForwardedChannels); proc.setProgram(QLibraryInfo::location(QLibraryInfo::BinariesPath) + QDir::separator() + QLatin1String("qmlcachegen")); proc.setArguments(QStringList() << (QLatin1String("--target-architecture=") + QSysInfo::buildCpuArchitecture()) << (QLatin1String("--target-abi=") + QSysInfo::buildAbi()) << qmlFileName); proc.start(); if (!proc.waitForFinished()) return false; + + if (capturedStderr) + *capturedStderr = proc.readAllStandardError(); + if (proc.exitStatus() != QProcess::NormalExit) return false; return proc.exitCode() == 0; @@ -158,6 +164,31 @@ void tst_qmlcachegen::translationExpressionSupport() QCOMPARE(obj->property("text").toString(), QString("All Ok")); } +void tst_qmlcachegen::errorOnArgumentsInSignalHandler() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile("test.qml", "import QtQml 2.2\n" + "QtObject {\n" + " signal mySignal(var arguments);\n" + " onMySignal: console.log(arguments);\n" + "}"); + + + QByteArray errorOutput; + QVERIFY(!generateCache(testFilePath, &errorOutput)); + QVERIFY2(errorOutput.contains("error: The use of the arguments object in signal handlers is"), errorOutput); +} + QTEST_GUILESS_MAIN(tst_qmlcachegen) #include "tst_qmlcachegen.moc" -- cgit v1.2.3 From bfab1db38167d31f0487f96d2d4b02d9243bc777 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Fri, 15 Sep 2017 13:56:36 +0200 Subject: Fall back to the ObjectWrapper for model advanceIterator When falling back to the QObjectWrapper it will add in the extra parts added when the roles were added to the object created by the model to hold the data being returned. This was causing the last entry to be duplicated and causing extra work too. Task-number: QTBUG-54285 Task-number: QTBUG-62156 Change-Id: I2907477277df8d16db4491a4999f004433e4205c Reviewed-by: Simon Hausmann --- tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'tests/auto/qml') diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp index 555ca5713e..f5c0e5ddf7 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -123,6 +123,7 @@ private slots: void about_to_be_signals(); void modify_through_delegate(); void bindingsOnGetResult(); + void stringifyModelEntry(); }; bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object) @@ -1482,6 +1483,28 @@ void tst_qqmllistmodel::bindingsOnGetResult() QVERIFY(obj->property("success").toBool()); } +void tst_qqmllistmodel::stringifyModelEntry() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData( + "import QtQuick 2.0\n" + "Item {\n" + " ListModel {\n" + " id: testModel\n" + " objectName: \"testModel\"\n" + " ListElement { name: \"Joe\"; age: 22 }\n" + " }\n" + "}\n", QUrl()); + QScopedPointer scene(component.create()); + QQmlListModel *model = scene->findChild("testModel"); + QQmlExpression expr(engine.rootContext(), model, "JSON.stringify(get(0));"); + QVariant v = expr.evaluate(); + QVERIFY2(!expr.hasError(), QTest::toString(expr.error().toString())); + const QString expectedString = QStringLiteral("{\"age\":22,\"name\":\"Joe\"}"); + QCOMPARE(v.toString(), expectedString); +} + QTEST_MAIN(tst_qqmllistmodel) #include "tst_qqmllistmodel.moc" -- cgit v1.2.3 From 71a18fd64c93afa00f4d89de2ba47663f41eba2c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 18 Sep 2017 16:26:17 +0200 Subject: Fix qmlClearTypeRegistrations() not dropping all registrations In commit 48c09a85ce397979c7e706e3694c879ffe456e09 we added the undeletableTypes container to hold a reference on C++ registered types to keep the indices returned by the public qmlRegisterType() API stable. Since qmlClearTypeRegistrations() is API that also resets those indices, we must also clear the undeletableTypes container to avoid leaking memory. Change-Id: I2038c00913f894d58aca3714d64d497493585326 Reviewed-by: Lars Knoll --- .../qml/qqmlenginecleanup/qqmlenginecleanup.pro | 2 +- .../qqmlenginecleanup/tst_qqmlenginecleanup.cpp | 59 +++++++++++++++++++--- 2 files changed, 52 insertions(+), 9 deletions(-) (limited to 'tests/auto/qml') diff --git a/tests/auto/qml/qqmlenginecleanup/qqmlenginecleanup.pro b/tests/auto/qml/qqmlenginecleanup/qqmlenginecleanup.pro index 5bcec9f5b4..90508609a8 100644 --- a/tests/auto/qml/qqmlenginecleanup/qqmlenginecleanup.pro +++ b/tests/auto/qml/qqmlenginecleanup/qqmlenginecleanup.pro @@ -6,4 +6,4 @@ include (../../shared/util.pri) SOURCES += tst_qqmlenginecleanup.cpp -QT += testlib qml +QT += testlib qml qml-private diff --git a/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp b/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp index d0a8b6401f..7e9a1524b0 100644 --- a/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp +++ b/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include //Separate test, because if engine cleanup attempts fail they can easily break unrelated tests class tst_qqmlenginecleanup : public QQmlDataTest @@ -44,41 +46,82 @@ private slots: void test_valueTypeProviderModule(); // QTBUG-43004 }; +// A wrapper around QQmlComponent to ensure the temporary reference counts +// on the type data as a result of the main thread <> loader thread communication +// are dropped. Regular Synchronous loading will leave us with an event posted +// to the gui thread and an extra refcount that will only be dropped after the +// event delivery. A plain sendPostedEvents() however is insufficient because +// we can't be sure that the event is posted after the constructor finished. +class CleanlyLoadingComponent : public QQmlComponent +{ +public: + CleanlyLoadingComponent(QQmlEngine *engine, const QUrl &url) + : QQmlComponent(engine, url, QQmlComponent::Asynchronous) + { waitForLoad(); } + CleanlyLoadingComponent(QQmlEngine *engine, const QString &fileName) + : QQmlComponent(engine, fileName, QQmlComponent::Asynchronous) + { waitForLoad(); } + + void waitForLoad() + { + QTRY_VERIFY(status() == QQmlComponent::Ready || status() == QQmlComponent::Error); + } +}; + void tst_qqmlenginecleanup::test_qmlClearTypeRegistrations() { //Test for preventing memory leaks is in tests/manual/qmltypememory QQmlEngine* engine; - QQmlComponent* component; + CleanlyLoadingComponent* component; QUrl testFile = testFileUrl("types.qml"); + const auto qmlTypeForTestType = []() { + return QQmlMetaType::qmlType(QStringLiteral("TestTypeCpp"), QStringLiteral("Test"), 2, 0); + }; + + QVERIFY(!qmlTypeForTestType().isValid()); qmlRegisterType("Test", 2, 0, "TestTypeCpp"); + QVERIFY(qmlTypeForTestType().isValid()); + engine = new QQmlEngine; - component = new QQmlComponent(engine, testFile); + component = new CleanlyLoadingComponent(engine, testFile); QVERIFY(component->isReady()); - delete engine; delete component; - qmlClearTypeRegistrations(); + delete engine; + + { + auto cppType = qmlTypeForTestType(); + + qmlClearTypeRegistrations(); + QVERIFY(!qmlTypeForTestType().isValid()); + + // cppType should hold the last ref, qmlClearTypeRegistration should have wiped + // all internal references. + QCOMPARE(QQmlType::refCount(cppType.priv()), 1); + } //2nd run verifies that types can reload after a qmlClearTypeRegistrations qmlRegisterType("Test", 2, 0, "TestTypeCpp"); + QVERIFY(qmlTypeForTestType().isValid()); engine = new QQmlEngine; - component = new QQmlComponent(engine, testFile); + component = new CleanlyLoadingComponent(engine, testFile); QVERIFY(component->isReady()); - delete engine; delete component; + delete engine; qmlClearTypeRegistrations(); + QVERIFY(!qmlTypeForTestType().isValid()); //3nd run verifies that TestTypeCpp is no longer registered engine = new QQmlEngine; - component = new QQmlComponent(engine, testFile); + component = new CleanlyLoadingComponent(engine, testFile); QVERIFY(component->isError()); QCOMPARE(component->errorString(), testFile.toString() +":33 module \"Test\" is not installed\n"); - delete engine; delete component; + delete engine; } static void cleanState(QQmlEngine **e) -- cgit v1.2.3 From 40d0ddaa8357ba4955c85178b5b34a28bec4fd71 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 20 Sep 2017 11:25:22 +0200 Subject: Make qmlplugindump work in shadow builds QQmlDataTest has provisions for looking up the data files in the right place. Use that. Change-Id: I3ec8d75efc0e66980986192e87e5ac52bc1119df Reviewed-by: Simon Hausmann --- .../qmlplugindump/data/dumper/CompositeSingleton/Singleton.qml | 6 ++++++ .../auto/qml/qmlplugindump/data/dumper/CompositeSingleton/qmldir | 3 +++ tests/auto/qml/qmlplugindump/qmlplugindump.pro | 4 +++- .../qmlplugindump/tests/dumper/CompositeSingleton/Singleton.qml | 6 ------ .../qml/qmlplugindump/tests/dumper/CompositeSingleton/qmldir | 3 --- tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp | 9 ++++++--- 6 files changed, 18 insertions(+), 13 deletions(-) create mode 100644 tests/auto/qml/qmlplugindump/data/dumper/CompositeSingleton/Singleton.qml create mode 100644 tests/auto/qml/qmlplugindump/data/dumper/CompositeSingleton/qmldir delete mode 100644 tests/auto/qml/qmlplugindump/tests/dumper/CompositeSingleton/Singleton.qml delete mode 100644 tests/auto/qml/qmlplugindump/tests/dumper/CompositeSingleton/qmldir (limited to 'tests/auto/qml') diff --git a/tests/auto/qml/qmlplugindump/data/dumper/CompositeSingleton/Singleton.qml b/tests/auto/qml/qmlplugindump/data/dumper/CompositeSingleton/Singleton.qml new file mode 100644 index 0000000000..b47d2e98f4 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/CompositeSingleton/Singleton.qml @@ -0,0 +1,6 @@ +pragma Singleton +import QtQuick 2.0 + +QtObject { + property int test: 0 +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/CompositeSingleton/qmldir b/tests/auto/qml/qmlplugindump/data/dumper/CompositeSingleton/qmldir new file mode 100644 index 0000000000..8df57f6d47 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/CompositeSingleton/qmldir @@ -0,0 +1,3 @@ +module tests.dumper.CompositeSingleton +singleton Singleton 1.0 Singleton.qml +depends QtQuick 2.0 diff --git a/tests/auto/qml/qmlplugindump/qmlplugindump.pro b/tests/auto/qml/qmlplugindump/qmlplugindump.pro index c713edc541..76f3fe2299 100644 --- a/tests/auto/qml/qmlplugindump/qmlplugindump.pro +++ b/tests/auto/qml/qmlplugindump/qmlplugindump.pro @@ -1,6 +1,8 @@ CONFIG += testcase TARGET = tst_qmlplugindump -QT += testlib gui-private +QT += testlib gui-private qml macx:CONFIG -= app_bundle +include(../../shared/util.pri) + SOURCES += tst_qmlplugindump.cpp diff --git a/tests/auto/qml/qmlplugindump/tests/dumper/CompositeSingleton/Singleton.qml b/tests/auto/qml/qmlplugindump/tests/dumper/CompositeSingleton/Singleton.qml deleted file mode 100644 index b47d2e98f4..0000000000 --- a/tests/auto/qml/qmlplugindump/tests/dumper/CompositeSingleton/Singleton.qml +++ /dev/null @@ -1,6 +0,0 @@ -pragma Singleton -import QtQuick 2.0 - -QtObject { - property int test: 0 -} diff --git a/tests/auto/qml/qmlplugindump/tests/dumper/CompositeSingleton/qmldir b/tests/auto/qml/qmlplugindump/tests/dumper/CompositeSingleton/qmldir deleted file mode 100644 index 8df57f6d47..0000000000 --- a/tests/auto/qml/qmlplugindump/tests/dumper/CompositeSingleton/qmldir +++ /dev/null @@ -1,3 +0,0 @@ -module tests.dumper.CompositeSingleton -singleton Singleton 1.0 Singleton.qml -depends QtQuick 2.0 diff --git a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp index 68e11e3551..6915e789c6 100644 --- a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp +++ b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp @@ -26,6 +26,8 @@ ** ****************************************************************************/ +#include "util.h" + #include #include #include @@ -33,7 +35,7 @@ #include #include -class tst_qmlplugindump : public QObject +class tst_qmlplugindump : public QQmlDataTest { Q_OBJECT public: @@ -54,6 +56,7 @@ tst_qmlplugindump::tst_qmlplugindump() void tst_qmlplugindump::initTestCase() { + QQmlDataTest::initTestCase(); qmlplugindumpPath = QLibraryInfo::location(QLibraryInfo::BinariesPath); #if defined(Q_OS_WIN) @@ -102,8 +105,8 @@ void tst_qmlplugindump::singleton() { QProcess dumper; QStringList args; - args << QLatin1String("tests.dumper.CompositeSingleton") << QLatin1String("1.0") - << QLatin1String("."); + args << QLatin1String("dumper.CompositeSingleton") << QLatin1String("1.0") + << testFile("."); dumper.start(qmlplugindumpPath, args); QVERIFY2(dumper.waitForStarted(), qPrintable(dumper.errorString())); QVERIFY2(dumper.waitForFinished(), qPrintable(dumper.errorString())); -- cgit v1.2.3 From 4b286a2975d37700c7e500c61516f01b63e04b01 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 20 Sep 2017 11:47:49 +0200 Subject: In tst_qqmlimport, import from the source directory The imports are all QML, not C++ plugins. They don't get built and cannot be found in the build directory if that is different from the source directory. Change-Id: Ib922908746932ff5e18a1a16921b99189c12cd61 Reviewed-by: Simon Hausmann --- tests/auto/qml/qqmlimport/tst_qqmlimport.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tests/auto/qml') diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp index 68739886c4..70aaa9678e 100644 --- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp +++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp @@ -54,7 +54,7 @@ void tst_QQmlImport::cleanup() void tst_QQmlImport::testDesignerSupported() { QQuickView *window = new QQuickView(); - window->engine()->addImportPath(QT_TESTCASE_BUILDDIR); + window->engine()->addImportPath(directory()); window->setSource(testFileUrl("testfile_supported.qml")); QVERIFY(window->errors().isEmpty()); @@ -68,7 +68,7 @@ void tst_QQmlImport::testDesignerSupported() delete window; window = new QQuickView(); - window->engine()->addImportPath(QT_TESTCASE_BUILDDIR); + window->engine()->addImportPath(directory()); window->engine()->clearComponentCache(); window->setSource(testFileUrl("testfile_supported.qml")); @@ -91,7 +91,7 @@ void tst_QQmlImport::uiFormatLoading() int size = 0; QQmlApplicationEngine *test = new QQmlApplicationEngine(testFileUrl("TestForm.ui.qml")); - test->addImportPath(QT_TESTCASE_BUILDDIR); + test->addImportPath(directory()); QCOMPARE(test->rootObjects().size(), ++size); QVERIFY(test->rootObjects()[size -1]); QVERIFY(test->rootObjects()[size -1]->property("success").toBool()); -- cgit v1.2.3 From d8d3259b2340baed2e7f8273907bd0bb5598b540 Mon Sep 17 00:00:00 2001 From: Sami Nurmenniemi Date: Wed, 30 Aug 2017 15:39:28 +0300 Subject: Enable tests for boot2qt Some tests needed fixing - Disabled tests too heavy for qemu - Skipped tests requiring OpenGL without support from the platform - Skipped tests requiring functionality on broken offscreen platform - Skipped tests that take too long on qemu + software renderer - Blacklisted tests for created bugs QTBUG-63049, QTBUG-63053 QTBUG-63055 and QTBUG-63057 Task-number: QTBUG-60268 Change-Id: I0346b0e436cf286d7d9cbc140acf324a4087cfb9 Reviewed-by: Simon Hausmann --- .../qqmlprofilerservice/tst_qqmlprofilerservice.cpp | 19 ++++++++++++------- tests/auto/qml/qml.pro | 10 ++++------ tests/auto/qml/qmlmin/qmlmin.pro | 6 ++++-- tests/auto/qml/qqmlengine/qqmlengine.pro | 5 +++++ tests/auto/qml/qqmlengine/tst_qqmlengine.cpp | 4 ++++ 5 files changed, 29 insertions(+), 15 deletions(-) (limited to 'tests/auto/qml') diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index 6232a8a867..cc6083cb98 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -36,6 +36,9 @@ #include #include +#include +#include + struct QQmlProfilerData { QQmlProfilerData(qint64 time = -2, int messageType = -1, int detailType = -1, @@ -584,16 +587,18 @@ void tst_QQmlProfilerService::scenegraphData() qint64 contextFrameTime = -1; qint64 renderFrameTime = -1; #if QT_CONFIG(opengl) //Software renderer doesn't have context frames - foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { - if (msg.messageType == QQmlProfilerDefinitions::SceneGraphFrame) { - if (msg.detailType == QQmlProfilerDefinitions::SceneGraphContextFrame) { - contextFrameTime = msg.time; - break; + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) { + foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { + if (msg.messageType == QQmlProfilerDefinitions::SceneGraphFrame) { + if (msg.detailType == QQmlProfilerDefinitions::SceneGraphContextFrame) { + contextFrameTime = msg.time; + break; + } } } - } - QVERIFY(contextFrameTime != -1); + QVERIFY(contextFrameTime != -1); + } #endif foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { if (msg.detailType == QQmlProfilerDefinitions::SceneGraphRendererFrame) { diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 69af3cd13b..9155f5b021 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -9,7 +9,6 @@ PUBLICTESTS += \ qjsonbinding \ qqmlfile \ -!boot2qt { PUBLICTESTS += \ qmlmin \ qqmlcomponent \ @@ -31,7 +30,6 @@ PUBLICTESTS += \ qqmlsettings \ qqmlstatemachine \ qmldiskcache -} PRIVATETESTS += \ qqmlcpputils \ @@ -39,7 +37,6 @@ PRIVATETESTS += \ v4misc \ qmlcachegen -!boot2qt { PRIVATETESTS += \ animation \ qqmlecmascript \ @@ -74,7 +71,6 @@ PRIVATETESTS += \ qv4mm \ ecmascripttests \ bindingdependencyapi -} qtHaveModule(widgets) { PUBLICTESTS += \ @@ -84,9 +80,11 @@ qtHaveModule(widgets) { SUBDIRS += $$PUBLICTESTS SUBDIRS += $$METATYPETESTS -qtConfig(process):!boot2qt { +qtConfig(process) { !contains(QT_CONFIG, no-qml-debug): SUBDIRS += debugger - SUBDIRS += qmllint qmlplugindump + !boot2qt { + SUBDIRS += qmllint qmlplugindump + } } qtConfig(library) { diff --git a/tests/auto/qml/qmlmin/qmlmin.pro b/tests/auto/qml/qmlmin/qmlmin.pro index 6af6653270..93e5caabcf 100644 --- a/tests/auto/qml/qmlmin/qmlmin.pro +++ b/tests/auto/qml/qmlmin/qmlmin.pro @@ -6,5 +6,7 @@ macx:CONFIG -= app_bundle SOURCES += tst_qmlmin.cpp DEFINES += SRCDIR=\\\"$$PWD\\\" - -cross_compile: DEFINES += QTEST_CROSS_COMPILED +# Boot2qt is cross compiled but it has sources available +!boot2qt { + cross_compile: DEFINES += QTEST_CROSS_COMPILED +} diff --git a/tests/auto/qml/qqmlengine/qqmlengine.pro b/tests/auto/qml/qqmlengine/qqmlengine.pro index e7952d8e3a..8d1e149d62 100644 --- a/tests/auto/qml/qqmlengine/qqmlengine.pro +++ b/tests/auto/qml/qqmlengine/qqmlengine.pro @@ -7,3 +7,8 @@ include (../../shared/util.pri) SOURCES += tst_qqmlengine.cpp QT += core-private gui-private qml-private network testlib + +boot2qt: { + # GC corruption test is too heavy for qemu-arm + DEFINES += SKIP_GCCORRUPTION_TEST +} diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 7aca830297..b332ab2194 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -862,6 +862,10 @@ void tst_qqmlengine::qmlContextProperties() void tst_qqmlengine::testGCCorruption() { +#ifdef SKIP_GCCORRUPTION_TEST + QSKIP("Test too heavy for qemu"); +#endif + QQmlEngine e; QQmlComponent c(&e, testFileUrl("testGCCorruption.qml")); -- cgit v1.2.3 From 82fbaa78f3840bcc289adc4e1ec64e337346f148 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 29 Nov 2013 14:04:27 +0100 Subject: Fix support for creating QWidgets in QML When creating QWidgets, it is important to replace any call to QObject::setParent with QWidget::setParent. We do this now with the hook introduced in commit fc8adfea9f5e09e5f47ac4e592e5d9db471caede in qtbase. Change-Id: I2e069696b7ceb7f3b77d0f17e058f5a00ca4a029 Reviewed-by: Edward Welbourne Reviewed-by: Lars Knoll --- tests/auto/qml/qml.pro | 3 +- tests/auto/qml/qwidgetsinqml/qwidgetsinqml.pro | 7 + tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp | 288 +++++++++++++++++++++ 3 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 tests/auto/qml/qwidgetsinqml/qwidgetsinqml.pro create mode 100644 tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp (limited to 'tests/auto/qml') diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 9155f5b021..9eb500f537 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -75,7 +75,8 @@ PRIVATETESTS += \ qtHaveModule(widgets) { PUBLICTESTS += \ qjsengine \ - qjsvalue + qjsvalue \ + qwidgetsinqml } SUBDIRS += $$PUBLICTESTS diff --git a/tests/auto/qml/qwidgetsinqml/qwidgetsinqml.pro b/tests/auto/qml/qwidgetsinqml/qwidgetsinqml.pro new file mode 100644 index 0000000000..c86365d5ea --- /dev/null +++ b/tests/auto/qml/qwidgetsinqml/qwidgetsinqml.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +CONFIG += parallel_test +TARGET = tst_qwidgetsinqml +macos:CONFIG -= app_bundle +QT += qml widgets testlib gui-private +SOURCES += tst_qwidgetsinqml.cpp +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp b/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp new file mode 100644 index 0000000000..bbe717859b --- /dev/null +++ b/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +class tst_QWidgetsInQml : public QObject +{ + Q_OBJECT +public: + tst_QWidgetsInQml(); + +private slots: + void instantiateWidget(); + void instantiateWidgetWithoutParentWidget(); + void widgetAsDefaultPropertyCollected(); + void widgetAsDefaultPropertyKept(); + void widgetAsDefaultPropertyKeptDuringCreation(); +}; + +static void gc(QQmlEngine &engine) +{ + engine.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); +} + +// Like QtObject, but with default property +class QObjectContainer : public QObject +{ + Q_OBJECT + Q_CLASSINFO("DefaultProperty", "data"); + Q_PROPERTY(QQmlListProperty data READ data DESIGNABLE false); +public: + QObjectContainer() + : widgetParent(0) + , gcOnAppend(false) + {} + + QQmlListProperty data() { + return QQmlListProperty(this, 0, children_append, children_count, children_at, children_clear); + } + + static void children_append(QQmlListProperty *prop, QObject *o) + { + QObjectContainer *that = static_cast(prop->object); + that->dataChildren.append(o); + QObject::connect(o, SIGNAL(destroyed(QObject*)), prop->object, SLOT(childDestroyed(QObject*))); + QWidget *widget = qobject_cast(o); + if (widget && that->widgetParent) + widget->setParent(that->widgetParent); + + if (that->gcOnAppend) { + QQmlEngine *engine = qmlEngine(that); + gc(*engine); + } + } + + static int children_count(QQmlListProperty *prop) + { + return static_cast(prop->object)->dataChildren.count(); + } + + static QObject *children_at(QQmlListProperty *prop, int index) + { + return static_cast(prop->object)->dataChildren.at(index); + } + + static void children_clear(QQmlListProperty *prop) + { + QObjectContainer *that = static_cast(prop->object); + foreach (QObject *c, that->dataChildren) + QObject::disconnect(c, SIGNAL(destroyed(QObject*)), that, SLOT(childDestroyed(QObject*))); + that->dataChildren.clear(); + } + + QList dataChildren; + QWidget *widgetParent; + bool gcOnAppend; + +protected slots: + void childDestroyed(QObject *child) { + dataChildren.removeAll(child); + } +}; + +class QWidgetContainer : public QObjectContainer +{ + Q_OBJECT +public: + QWidgetContainer() + { + widgetParent = new QWidget; + QQmlEngine::setObjectOwnership(widgetParent, QQmlEngine::CppOwnership); + } + virtual ~QWidgetContainer() + { + delete widgetParent; + widgetParent = 0; + } +}; + +class QObjectContainerWithGCOnAppend : public QObjectContainer +{ + Q_OBJECT +public: + QObjectContainerWithGCOnAppend() + { + gcOnAppend = true; + } +}; + +tst_QWidgetsInQml::tst_QWidgetsInQml() +{ + qmlRegisterType("Qt.Widgets", 1, 0, "QWidget"); + qmlRegisterType("Qt.Widgets", 1, 0, "QObjectContainer"); + qmlRegisterType("Qt.Widgets", 1, 0, "QWidgetContainer"); + qmlRegisterType("Qt.Widgets", 1, 0, "QObjectContainerWithGCOnAppend"); +} + +void tst_QWidgetsInQml::instantiateWidget() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\nQWidget { property QWidget child: QWidget { objectName: 'child' } }", QUrl()); + QScopedPointer object(component.create()); + QVERIFY(!object.isNull()); + QWidget *rootWidget = qobject_cast(object.data()); + QVERIFY(rootWidget != 0); + QCOMPARE(rootWidget->children().count(), 1); + QWidget *firstChildWidget = qobject_cast(rootWidget->children().first()); + QVERIFY(firstChildWidget != 0); + + QWidget *widgetProperty = qvariant_cast(object->property("child")); + QVERIFY(widgetProperty != 0); + QCOMPARE(firstChildWidget, widgetProperty); + QCOMPARE(firstChildWidget->objectName(), QString("child")); +} + +void tst_QWidgetsInQml::instantiateWidgetWithoutParentWidget() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\n" + "import QtQml 2.0;\n" + "QtObject { property QtObject child: QWidget { objectName: 'child' } }", QUrl()); + QScopedPointer object(component.create()); + QVERIFY(!object.isNull()); + + QPointer widgetProperty = qvariant_cast(object->property("child")); + QVERIFY(!widgetProperty.isNull()); + QCOMPARE(widgetProperty->objectName(), QString("child")); + + QVERIFY(!widgetProperty->parent()); + gc(engine); + // Don't collect, the property reference should keep it alive + QVERIFY(!widgetProperty.isNull()); +} + +void tst_QWidgetsInQml::widgetAsDefaultPropertyCollected() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\n" + "import QtQml 2.0;\n" + "QObjectContainer {\n" + " QWidget {\n" + " id: parentLessChild;\n" + " objectName: 'child'\n" + " }\n" + " property var widgetHolder;\n" + " Component.onCompleted: {\n" + " widgetHolder = parentLessChild;\n" + " }\n" + "}", QUrl()); + QScopedPointer object(component.create()); + QVERIFY(!object.isNull()); + + QObjectContainer *container = qobject_cast(object.data()); + QCOMPARE(container->dataChildren.count(), 1); + + QJSValue holder = qvariant_cast(object->property("widgetHolder")); + QVERIFY(!holder.isNull()); + gc(engine); + QCOMPARE(container->dataChildren.count(), 1); + + holder = QJSValue(); + object->setProperty("widgetHolder", QVariant::fromValue(holder)); + + gc(engine); + // The QWidget is without a parent and nobody is referencing it anymore (the children + // list in QObjectContainer is weak!), so it should get collected. + QCOMPARE(container->dataChildren.count(), 0); +} + +void tst_QWidgetsInQml::widgetAsDefaultPropertyKept() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\n" + "import QtQml 2.0;\n" + "QWidgetContainer {\n" + " QWidget {\n" + " id: parentLessChild;\n" + " objectName: 'child'\n" + " }\n" + " property var widgetHolder;\n" + " Component.onCompleted: {\n" + " widgetHolder = parentLessChild;\n" + " }\n" + "}", QUrl()); + QScopedPointer object(component.create()); + QVERIFY(!object.isNull()); + + QWidgetContainer *container = qobject_cast(object.data()); + QCOMPARE(container->dataChildren.count(), 1); + + QJSValue holder = qvariant_cast(object->property("widgetHolder")); + QVERIFY(!holder.isNull()); + gc(engine); + QCOMPARE(container->dataChildren.count(), 1); + + holder = QJSValue(); + object->setProperty("widgetHolder", QVariant::fromValue(holder)); + + gc(engine); + QCOMPARE(container->dataChildren.count(), 1); +} + +void tst_QWidgetsInQml::widgetAsDefaultPropertyKeptDuringCreation() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\n" + "import QtQml 2.0;\n" + "QObjectContainerWithGCOnAppend {\n" + " QWidget {\n" + " id: parentLessChild;\n" + " objectName: 'child'\n" + " property var blah;\n" // Ensures that we have a JS wrapper + " }\n" + "}", QUrl()); + QScopedPointer object(component.create()); + QVERIFY(!object.isNull()); + + QObjectContainer *container = qobject_cast(object.data()); + QCOMPARE(container->dataChildren.count(), 1); + + gc(engine); + QCOMPARE(container->dataChildren.count(), 0); + +} + +QTEST_MAIN(tst_QWidgetsInQml) + +#include "tst_qwidgetsinqml.moc" -- cgit v1.2.3 From 681f93c74d7d60dc1998d3124e1f59ddc0f476ee Mon Sep 17 00:00:00 2001 From: Kevin Funk Date: Wed, 20 Sep 2017 20:57:39 +0200 Subject: Replace Q_DECL_OVERRIDE with override Change-Id: I176f91a8c51e81a2df3fe91733118261491223ee Reviewed-by: Lars Knoll --- tests/auto/qml/qqmlinstantiator/stringmodel.h | 4 ++-- tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tests/auto/qml') diff --git a/tests/auto/qml/qqmlinstantiator/stringmodel.h b/tests/auto/qml/qqmlinstantiator/stringmodel.h index 0bd4ada55e..b01817375a 100644 --- a/tests/auto/qml/qqmlinstantiator/stringmodel.h +++ b/tests/auto/qml/qqmlinstantiator/stringmodel.h @@ -65,7 +65,7 @@ public: return items.count(); } - virtual QHash roleNames() const Q_DECL_OVERRIDE + QHash roleNames() const override { return roles; } @@ -75,7 +75,7 @@ public: return 1; } - virtual bool hasChildren(const QModelIndex &) const Q_DECL_OVERRIDE + bool hasChildren(const QModelIndex &) const override { return rowCount(QModelIndex()) > 0; } diff --git a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp index beb60925bb..88a5d41975 100644 --- a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp +++ b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp @@ -98,7 +98,7 @@ public: } protected: - void connectNotify(const QMetaMethod &signal) Q_DECL_OVERRIDE { + void connectNotify(const QMetaMethod &signal) override { if (signal.name() == "qmlObjectPropChanged") qmlObjectPropConnections++; if (signal.name() == "cppObjectPropChanged") cppObjectPropConnections++; if (signal.name() == "unboundPropChanged") unboundPropConnections++; @@ -112,7 +112,7 @@ protected: //qDebug() << Q_FUNC_INFO << this << signal.name(); } - void disconnectNotify(const QMetaMethod &signal) Q_DECL_OVERRIDE { + void disconnectNotify(const QMetaMethod &signal) override { if (signal.name() == "qmlObjectPropChanged") qmlObjectPropConnections--; if (signal.name() == "cppObjectPropChanged") cppObjectPropConnections--; if (signal.name() == "unboundPropChanged") unboundPropConnections--; @@ -146,7 +146,7 @@ public: {} private slots: - void initTestCase() Q_DECL_OVERRIDE; + void initTestCase() override; void cleanupTestCase(); void testConnectNotify(); -- cgit v1.2.3