diff options
Diffstat (limited to 'tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp')
-rw-r--r-- | tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 395 |
1 files changed, 330 insertions, 65 deletions
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index edb1e9ba80..bd60093a7b 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2017 Crimson AS <info@crimson.no> // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + #include <QtTest/QtTest> #include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlengine.h> @@ -27,6 +28,8 @@ #include <private/qqmlabstractbinding_p.h> #include <private/qqmlvaluetypeproxybinding_p.h> #include <QtCore/private/qproperty_p.h> +#include <QtQuick/qquickwindow.h> +#include <QtQuick/private/qquickitem_p.h> #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/testhttpserver_p.h> @@ -376,6 +379,8 @@ private slots: void qpropertyBindingHandlesUndefinedCorrectly(); void qpropertyBindingHandlesUndefinedWithoutResetCorrectly_data(); void qpropertyBindingHandlesUndefinedWithoutResetCorrectly(); + void qpropertyBindingRestoresObserverAfterReset(); + void qpropertyBindingObserverCorrectlyLinkedAfterReset(); void hugeRegexpQuantifiers(); void singletonTypeWrapperLookup(); void getThisObject(); @@ -416,6 +421,13 @@ private slots: void doNotCrashOnReadOnlyBindable(); + void resetGadget(); + void assignListPropertyByIndexOnGadget(); + + void methodCallOnDerivedSingleton(); + + void proxyMetaObject(); + private: // static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); static void verifyContextLifetime(const QQmlRefPointer<QQmlContextData> &ctxt); @@ -432,14 +444,6 @@ private: } }; -static void gc(QQmlEngine &engine) -{ - engine.collectGarbage(); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); -} - - tst_qqmlecmascript::tst_qqmlecmascript() : QQmlDataTest(QT_QMLTEST_DATADIR) { @@ -3127,11 +3131,14 @@ void tst_qqmlecmascript::callQtInvokables() QCOMPARE(o->invoked(), -1); QCOMPARE(o->actuals().size(), 0); - o->reset(); - QVERIFY(EVALUATE_ERROR("object.method_QPointF(object)")); - QCOMPARE(o->error(), false); - QCOMPARE(o->invoked(), -1); - QCOMPARE(o->actuals().size(), 0); + // This fails if the QtQml module is loaded but works if it's not. + // If QtQml is loaded, QPointF is a structured value type that can be created from any object. + // + // o->reset(); + // QVERIFY(EVALUATE_ERROR("object.method_QPointF(object)")); + // QCOMPARE(o->error(), false); + // QCOMPARE(o->invoked(), -1); + // QCOMPARE(o->actuals().size(), 0); o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", QV4::Primitive::undefinedValue())); @@ -3186,6 +3193,17 @@ void tst_qqmlecmascript::callQtInvokables() QCOMPARE(o->invoked(), -1); // no function got called due to incompatible arguments } + { + o->reset(); + QQmlComponent comp(&qmlengine, testFileUrl("qmlTypeWrapperArgs3.qml")); + QScopedPointer<QObject> root {comp.createWithInitialProperties({{"invokableObject", QVariant::fromValue(o)}}) }; + QVERIFY(root); + QCOMPARE(o->error(), false); + QCOMPARE(o->actuals().size(), 2); + QCOMPARE(o->actuals().at(0).metaType(), QMetaType::fromType<QQmlComponentAttached *>()); + QCOMPARE(o->actuals().at(1).metaType(), QMetaType::fromType<SingletonWithEnum *>()); + } + o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); @@ -3517,6 +3535,27 @@ void tst_qqmlecmascript::callQtInvokables() QCOMPARE(o->error(), false); QCOMPARE(o->invoked(), -1); QCOMPARE(o->actuals(), QVariantList()); + + o->reset(); + QVERIFY(EVALUATE_VALUE("object.method_component(object.someComponent())", + QV4::Primitive::undefinedValue())); + QCOMPARE(o->error(), false); + QCOMPARE(o->invoked(), 42); + QCOMPARE(o->actuals(), QVariantList() << QVariant::fromValue(o->someComponent())); + + o->reset(); + QVERIFY(EVALUATE_VALUE("object.method_component(object.someTypeObject())", + QV4::Primitive::undefinedValue())); + QCOMPARE(o->error(), false); + QCOMPARE(o->invoked(), 43); + QCOMPARE(o->actuals(), QVariantList() << QVariant::fromValue(o->someTypeObject())); + + o->reset(); + QVERIFY(EVALUATE_VALUE("object.method_component('qrc:/somewhere/else')", + QV4::Primitive::undefinedValue())); + QCOMPARE(o->error(), false); + QCOMPARE(o->invoked(), 44); + QCOMPARE(o->actuals(), QVariantList() << QVariant::fromValue(QUrl("qrc:/somewhere/else"))); } void tst_qqmlecmascript::resolveClashingProperties() @@ -3815,6 +3854,82 @@ void tst_qqmlecmascript::scriptConnect() QScopedPointer<QObject> root { component.create() }; QVERIFY2(root, qPrintable(component.errorString())); } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnect.8.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(obj, qPrintable(component.errorString())); + QVERIFY(obj.data() != nullptr); + + QCOMPARE(obj.data()->property("count"), 0); + + QMetaObject::invokeMethod(obj.data(), "someSignal"); + QCOMPARE(obj.data()->property("count"), 1); + + QMetaObject::invokeMethod(obj.data(), "itemDestroy"); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + + QMetaObject::invokeMethod(obj.data(), "someSignal"); + QCOMPARE(obj.data()->property("count"), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnect.9.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(obj, qPrintable(component.errorString())); + QVERIFY(obj.data() != nullptr); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(obj.data()); + + QCOMPARE(object->property("a"), 0); + + QMetaObject::invokeMethod(object, "someSignal"); + QCOMPARE(object->property("a"), 1); + + QMetaObject::invokeMethod(object, "destroyObj", Qt::DirectConnection); + QApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); + QApplication::processEvents(); + + QMetaObject::invokeMethod(object, "someSignal"); + + QCOMPARE(object->property("a"), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnectSingleton.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(obj, qPrintable(component.errorString())); + QVERIFY(obj.data() != nullptr); + + QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection); + QCOMPARE(obj.data()->property("a").toInt(), 1); + engine.clearSingletons(); + QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection); + QCOMPARE(obj.data()->property("a").toInt(), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnect.deletion.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(obj, qPrintable(component.errorString())); + QVERIFY(!obj.isNull()); + + QCOMPARE(obj->property("a"), 0); + + QMetaObject::invokeMethod(obj.data(), "someSignal"); + QCOMPARE(obj->property("a"), 1); + + QCOMPARE(obj->property("b"), 0); + QMetaObject::invokeMethod(obj.data(), "destroyObj", Qt::DirectConnection); + + QTRY_COMPARE(obj->property("b"), 1); + QCOMPARE(obj->property("a"), 1); + } } void tst_qqmlecmascript::scriptDisconnect() @@ -3895,6 +4010,60 @@ void tst_qqmlecmascript::scriptDisconnect() emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); QCOMPARE(object->property("test").toInt(), 3); } + + { + QQmlComponent component(&engine, testFileUrl("scriptDisconnect.5.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(obj, qPrintable(component.errorString())); + QVERIFY(obj.data() != nullptr); + + QCOMPARE(obj.data()->property("count"), 0); + + QMetaObject::invokeMethod(obj.data(), "someSignal"); + QCOMPARE(obj.data()->property("count"), 1); + + QMetaObject::invokeMethod(obj.data(), "disconnectSignal"); + + QMetaObject::invokeMethod(obj.data(), "someSignal"); + QCOMPARE(obj.data()->property("count"), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnect.9.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(obj, qPrintable(component.errorString())); + QVERIFY(obj.data() != nullptr); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(obj.data()); + + QCOMPARE(object->property("a"), 0); + + QMetaObject::invokeMethod(object, "someSignal"); + QCOMPARE(object->property("a"), 1); + + QMetaObject::invokeMethod(object, "disconnectSignal", Qt::DirectConnection); + + QMetaObject::invokeMethod(object, "someSignal"); + + QCOMPARE(object->property("a"), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnectSingleton.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(obj, qPrintable(component.errorString())); + QVERIFY(obj.data() != nullptr); + + QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection); + QCOMPARE(obj.data()->property("a").toInt(), 1); + + QMetaObject::invokeMethod(obj.data(), "disconnectSingleton", Qt::DirectConnection); + QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection); + QCOMPARE(obj.data()->property("a").toInt(), 1); + } } class OwnershipObject : public QObject @@ -3923,10 +4092,7 @@ void tst_qqmlecmascript::ownership() QScopedPointer<QObject> object(component.create(context.data())); - engine.collectGarbage(); - - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(engine); QVERIFY(own.object.isNull()); } @@ -3940,10 +4106,7 @@ void tst_qqmlecmascript::ownership() QScopedPointer<QObject> object(component.create(context.data())); - engine.collectGarbage(); - - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(engine); QVERIFY(own.object != nullptr); } @@ -4019,9 +4182,7 @@ void tst_qqmlecmascript::ownershipCustomReturnValue() QVERIFY(source.value != nullptr); } - engine.collectGarbage(); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(engine); QVERIFY(source.value.isNull()); } @@ -4052,10 +4213,7 @@ void tst_qqmlecmascript::ownershipRootObject() QScopedPointer<QObject> object(component.create(context.data())); QVERIFY2(object, qPrintable(component.errorString())); - engine.collectGarbage(); - - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(engine); QVERIFY(own.object != nullptr); } @@ -4080,10 +4238,7 @@ void tst_qqmlecmascript::ownershipConsistency() QScopedPointer<QObject> object(component.create(context.data())); QVERIFY2(object, qPrintable(component.errorString())); - engine.collectGarbage(); - - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(engine); QVERIFY(own.object != nullptr); } @@ -4613,6 +4768,7 @@ void tst_qqmlecmascript::verifyContextLifetime(const QQmlRefPointer<QQmlContextD } ctxt->engine()->collectGarbage(); + QTRY_VERIFY(gcDone(ctxt->engine())); qml = scripts->get(i); newContext = qml ? qml->getContext() : nullptr; QCOMPARE(scriptContext.data(), newContext.data()); @@ -5257,6 +5413,7 @@ void tst_qqmlecmascript::propertyChangeSlots() QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY2(object, qPrintable(component.errorString())); + QCOMPARE(object->property("changeCount"), 15); // ensure that invalid property names fail properly. QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); @@ -5272,20 +5429,6 @@ void tst_qqmlecmascript::propertyChangeSlots() QCOMPARE(e2.errors().at(0).toString(), expectedErrorString); object.reset(e2.create()); QVERIFY(!object); - - QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); - QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml")); - expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\""); - QCOMPARE(e3.errors().at(0).toString(), expectedErrorString); - object.reset(e3.create()); - QVERIFY(!object); - - QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); - QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml")); - expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\""); - QCOMPARE(e4.errors().at(0).toString(), expectedErrorString); - object.reset(e4.create()); - QVERIFY(!object); } void tst_qqmlecmascript::propertyVar_data() @@ -5453,7 +5596,9 @@ void tst_qqmlecmascript::propertyVarOwnership() QScopedPointer<QObject> object(component.create()); QVERIFY2(object, qPrintable(component.errorString())); QMetaObject::invokeMethod(object.data(), "createComponent"); - engine.collectGarbage(); + // This test only works if we don't deliver the pending delete later event + // that collectGarbage will post before calling runTest + gc(engine, GCFlags::DontSendPostedEvents); QMetaObject::invokeMethod(object.data(), "runTest"); QCOMPARE(object->property("test").toBool(), true); } @@ -5469,8 +5614,7 @@ void tst_qqmlecmascript::propertyVarImplicitOwnership() QScopedPointer<QObject> object(component.create()); QVERIFY2(object, qPrintable(component.errorString())); QMetaObject::invokeMethod(object.data(), "assignCircular"); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. - QCoreApplication::processEvents(); + gc(engine); QObject *rootObject = object->property("vp").value<QObject*>(); QVERIFY(rootObject != nullptr); QObject *childObject = rootObject->findChild<QObject*>("text"); @@ -5479,6 +5623,8 @@ void tst_qqmlecmascript::propertyVarImplicitOwnership() QCOMPARE(childObject->property("textCanary").toInt(), 10); // Creates a reference to a constructed QObject: QMetaObject::invokeMethod(childObject, "constructQObject"); + // Don't send delete later events yet, we do it manually later + gc(engine, GCFlags::DontSendPostedEvents); QPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events. QVERIFY(!qobjectGuard.isNull()); QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. @@ -5497,8 +5643,7 @@ void tst_qqmlecmascript::propertyVarReparent() QScopedPointer<QObject> object(component.create()); QVERIFY2(object, qPrintable(component.errorString())); QMetaObject::invokeMethod(object.data(), "assignVarProp"); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. - QCoreApplication::processEvents(); + gc(engine); QObject *rect = object->property("vp").value<QObject*>(); QObject *text = rect->findChild<QObject*>("textOne"); QObject *text2 = rect->findChild<QObject*>("textTwo"); @@ -5512,6 +5657,7 @@ void tst_qqmlecmascript::propertyVarReparent() QCOMPARE(text2->property("textCanary").toInt(), 12); // now construct an image which we will reparent. QMetaObject::invokeMethod(text2, "constructQObject"); + gc(engine, GCFlags::DontSendPostedEvents); QObject *image = text2->property("vp").value<QObject*>(); QPointer<QObject> imageGuard(image); QVERIFY(!imageGuard.isNull()); @@ -5539,8 +5685,7 @@ void tst_qqmlecmascript::propertyVarReparentNullContext() QScopedPointer<QObject> object(component.create()); QVERIFY2(object, qPrintable(component.errorString())); QMetaObject::invokeMethod(object.data(), "assignVarProp"); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. - QCoreApplication::processEvents(); + gc(engine); QObject *rect = object->property("vp").value<QObject*>(); QObject *text = rect->findChild<QObject*>("textOne"); QObject *text2 = rect->findChild<QObject*>("textTwo"); @@ -5554,6 +5699,7 @@ void tst_qqmlecmascript::propertyVarReparentNullContext() QCOMPARE(text2->property("textCanary").toInt(), 12); // now construct an image which we will reparent. QMetaObject::invokeMethod(text2, "constructQObject"); + gc(engine); QObject *image = text2->property("vp").value<QObject*>(); QPointer<QObject> imageGuard(image); QVERIFY(!imageGuard.isNull()); @@ -5780,9 +5926,7 @@ void tst_qqmlecmascript::handleReferenceManagement() gc(hrmEngine); QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference object.reset(); - hrmEngine.collectGarbage(); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(hrmEngine); QCOMPARE(dtorCount, 3); } @@ -5799,9 +5943,7 @@ void tst_qqmlecmascript::handleReferenceManagement() gc(hrmEngine); QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive. object.reset(); - hrmEngine.collectGarbage(); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); + gc(hrmEngine); QCOMPARE(dtorCount, 3); } @@ -7980,12 +8122,9 @@ void tst_qqmlecmascript::onDestructionViaGC() v4->memoryManager->allocate<QV4::WeakReferenceSentinel>(weakRef.data(), &sentinelResult); } gc(engine); - + QVERIFY2(weakRef->isNullOrUndefined(), "The weak value was not cleared"); QVERIFY2(mutatorResult, "We failed to re-assign the weak reference a new value during GC"); - QVERIFY2(!sentinelResult, "The weak value was cleared on first GC run"); - QVERIFY2(!weakRef->isNullOrUndefined(), "The weak value was cleared on first GC run"); - gc(engine); - QVERIFY2(weakRef->isNullOrUndefined(), "The weak value was not cleared on second gc run"); + QVERIFY2(sentinelResult, "The weak reference was not cleared properly"); } struct EventProcessor : public QObject @@ -8078,7 +8217,9 @@ void tst_qqmlecmascript::qqmldataDestroyed() QVERIFY2(object, qPrintable(c.errorString())); // now gc causing the collection of the dynamically constructed object. engine.collectGarbage(); + QTRY_VERIFY(gcDone(&engine)); engine.collectGarbage(); + QTRY_VERIFY(gcDone(&engine)); // now process events to allow deletion (calling qqmldata::destroyed()) QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); @@ -9389,6 +9530,32 @@ void tst_qqmlecmascript::qpropertyBindingHandlesUndefinedWithoutResetCorrectly() QCOMPARE(root->property("value2").toInt(), 2); } +void tst_qqmlecmascript::qpropertyBindingRestoresObserverAfterReset() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("restoreObserverAfterReset.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QTRY_COMPARE(o->property("height").toDouble(), 60.0); + QVERIFY(o->property("steps").toInt() > 3); +} + +void tst_qqmlecmascript::qpropertyBindingObserverCorrectlyLinkedAfterReset() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("qpropertyResetCorrectlyLinked.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + std::unique_ptr<QObject> o(c.create()); + QVERIFY(o); + QCOMPARE(o->property("width"), 200); + auto item = qobject_cast<QQuickItem *>(o.get()); + auto itemPriv = QQuickItemPrivate::get(item); + QBindingStorage *storage = qGetBindingStorage(itemPriv); + QPropertyBindingDataPointer ptr { storage->bindingData(&itemPriv->width) }; + QCOMPARE(ptr.observerCount(), 1); +} + void tst_qqmlecmascript::hugeRegexpQuantifiers() { QJSEngine engine; @@ -10027,6 +10194,9 @@ public: Q_INVOKABLE void triggerSignal() { emit fooMember2Emitted(&m_fooMember2); } + Q_INVOKABLE const FrozenFoo *getConst() { return createFloating(); } + Q_INVOKABLE FrozenFoo *getNonConst() { return createFloating(); } + FrozenFoo *fooMember() { return &m_fooMember; } FrozenFoo *fooMember2() { return &m_fooMember2; } @@ -10036,6 +10206,16 @@ signals: private: const FrozenFoo *fooMemberConst() const { return &m_fooMember; } + FrozenFoo *createFloating() + { + if (!m_floating) { + m_floating = new FrozenFoo; + m_floating->setObjectName(objectName()); + } + return m_floating; + } + + FrozenFoo *m_floating = nullptr; FrozenFoo m_fooMember; FrozenFoo m_fooMember2; }; @@ -10058,6 +10238,17 @@ void tst_qqmlecmascript::frozenQObject() QVERIFY(frozenObjects->property("caughtSignal").toBool()); QCOMPARE(frozenObjects->fooMember()->name(), QStringLiteral("Jane")); QCOMPARE(frozenObjects->fooMember2()->name(), QStringLiteral("Jane")); + + QQmlComponent component3(&engine, testFileUrl("frozenQObject3.qml")); + QScopedPointer<QObject> root3(component3.create()); + QCOMPARE(root3->objectName(), QLatin1String("a/b")); + QVERIFY(root3->property("objConst").value<QObject *>()); + QVERIFY(root3->property("objNonConst").value<QObject *>()); + + QTRY_VERIFY(root3->property("gcs").toInt() > 8); + + QVERIFY(root3->property("objConst").value<QObject *>()); + QVERIFY(root3->property("objNonConst").value<QObject *>()); } struct ConstPointer : QObject @@ -10373,6 +10564,80 @@ void tst_qqmlecmascript::doNotCrashOnReadOnlyBindable() QCOMPARE(o->property("x").toInt(), 7); } +void tst_qqmlecmascript::resetGadget() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("resetGadget.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + auto resettableGadgetHolder = qobject_cast<ResettableGadgetHolder *>(o.get()); + QVERIFY(resettableGadgetHolder); + QCOMPARE(resettableGadgetHolder->g().value(), 0); + resettableGadgetHolder->setProperty("trigger", QVariant::fromValue(true)); + QCOMPARE(resettableGadgetHolder->g().value(), 42); +} + +void tst_qqmlecmascript::assignListPropertyByIndexOnGadget() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFile("AssignListPropertyByIndexOnGadget.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + + const auto &gadget = o->property("gadget").value<ListPropertyAssignment_Gadget>(); + const auto *object = o->property("object").value<ListPropertyAssignment_Object *>(); + QVERIFY(object); + + QStringList expected{ "Completely new Element", "Element2", "Element3" }; + QVariantList variants { + u"Completely new Element"_s, + u"foo"_s, + QVariant::fromValue<std::nullptr_t>(nullptr), + QVariant::fromValue<bool>(true) + }; + + QCOMPARE(gadget.gadgetStringList(), expected); + QCOMPARE(gadget.gadgetVariantList(), variants); + QCOMPARE(object->qobjectStringList(), expected); +} + +void tst_qqmlecmascript::methodCallOnDerivedSingleton() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFile("methodCallOnDerivedSingleton.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + auto singleton = engine.singletonInstance<SingletonBase *>("Qt.test", "SingletonInheritanceTest"); + QVERIFY(singleton); + QVERIFY(singleton->m_okay); +} + +void tst_qqmlecmascript::proxyMetaObject() +{ + // Verify that TypeWithCustomMetaObject, that extends another type, + // thereby triggering a QQmlProxyMetaObject, is still proxied the + // QDynamicMetaObjectData::objectDestroyed callback. + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData(R"( + import QtQuick + import QtQml + import Qt.test + Rectangle { + TypeWithCustomMetaObject {} + } + )", QUrl("testData")); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o); + QVERIFY(!MetaCallInterceptor::didGetObjectDestroyedCallback); + o.reset(nullptr); + QVERIFY(MetaCallInterceptor::didGetObjectDestroyedCallback); +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" |