diff options
Diffstat (limited to 'tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp')
-rw-r--r-- | tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp | 186 |
1 files changed, 163 insertions, 23 deletions
diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp index 5203ba9615..ea06a11006 100644 --- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp +++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 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 <qtest.h> #include <QDebug> @@ -21,7 +21,7 @@ #include <private/qv4executablecompilationunit_p.h> #include <qcolor.h> #include <qsignalspy.h> - +#include "lifecyclewatcher.h" #include <algorithm> using namespace Qt::StringLiterals; @@ -90,13 +90,6 @@ public slots: } }; -static void gc(QQmlEngine &engine) -{ - engine.collectGarbage(); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCoreApplication::processEvents(); -} - class tst_qqmlcomponent : public QQmlDataTest { Q_OBJECT @@ -140,11 +133,16 @@ private slots: void boundComponent(); void loadFromModule_data(); void loadFromModule(); + void loadFromModuleLifecycle(); void loadFromModuleThenCreateWithIncubator(); void loadFromModuleFailures_data(); void loadFromModuleFailures(); void loadFromModuleRequired(); void loadFromQrc(); + void removeBinding(); + void complexObjectArgument(); + void bindingEvaluationOrder(); + void compilationUnitsWithSameUrl(); private: QQmlEngine engine; @@ -182,14 +180,12 @@ void tst_qqmlcomponent::loadEmptyUrl() void tst_qqmlcomponent::qmlIncubateObject() { QQmlComponent component(&engine, testFileUrl("incubateObject.qml")); - QObject *object = component.create(); + std::unique_ptr<QObject> object { component.create() }; QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toBool(), true); QCOMPARE(object->property("test2").toBool(), false); QTRY_VERIFY(object->property("test2").toBool()); - - delete object; } void tst_qqmlcomponent::qmlCreateWindow() @@ -282,7 +278,7 @@ void tst_qqmlcomponent::qmlCreateObjectWithProperties() QTest::ignoreMessage( QtMsgType::QtWarningMsg, QRegularExpression( - ".*createObjectWithScript.qml:42:13: Required property i was not initialized")); + ".*createObjectWithScript.qml:45:13: Required property i was not initialized")); QQmlComponent component(&engine, testFileUrl("createObjectWithScript.qml")); QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8()); @@ -342,6 +338,12 @@ void tst_qqmlcomponent::qmlCreateObjectWithProperties() QCOMPARE(goodRequired->parent(), object.data()); QCOMPARE(goodRequired->property("i").value<int>(), 42); } + + { + QScopedPointer<QObject> bindingAsInitial(object->property("bindingAsInitial").value<QObject *>()); + QVERIFY(bindingAsInitial); + QVERIFY(object->property("bindingUsed").toBool()); + } } void tst_qqmlcomponent::qmlCreateObjectClean() @@ -390,11 +392,11 @@ void tst_qqmlcomponent::qmlCreateParentReference() QQmlComponent component(&engine, testFileUrl("createParentReference.qml")); QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8()); - QObject *object = component.create(); + std::unique_ptr<QObject> object { component.create() }; QVERIFY(object != nullptr); - QVERIFY(QMetaObject::invokeMethod(object, "createChild")); - delete object; + QVERIFY(QMetaObject::invokeMethod(object.get(), "createChild")); + object.reset(); engine.setOutputWarningsToStandardError(false); QCOMPARE(engine.outputWarningsToStandardError(), false); @@ -416,10 +418,8 @@ void tst_qqmlcomponent::async() QCOMPARE(watcher.ready, 1); QCOMPARE(watcher.error, 0); - QObject *object = component.create(); + std::unique_ptr<QObject> object { component.create() }; QVERIFY(object != nullptr); - - delete object; } void tst_qqmlcomponent::asyncHierarchy() @@ -437,7 +437,7 @@ void tst_qqmlcomponent::asyncHierarchy() QCOMPARE(watcher.ready, 1); QCOMPARE(watcher.error, 0); - QObject *root = component.create(); + std::unique_ptr<QObject> root { component.create() }; QVERIFY(root != nullptr); // ensure that the parent-child relationship hierarchy is correct @@ -461,8 +461,6 @@ void tst_qqmlcomponent::asyncHierarchy() // ensure that values and bindings are assigned correctly QVERIFY(root->property("success").toBool()); - - delete root; } void tst_qqmlcomponent::asyncForceSync() @@ -1245,12 +1243,14 @@ void tst_qqmlcomponent::boundComponent() { QQmlComponent component(&engine, testFileUrl("nestedBoundComponent.qml")); QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QVERIFY(component.isBound()); QScopedPointer<QObject> o(component.create()); QVERIFY(!o.isNull()); QQmlComponent *nestedComponent = o->property("c").value<QQmlComponent *>(); QVERIFY(nestedComponent != nullptr); + QVERIFY(nestedComponent->isBound()); QObject *nestedObject = o->property("o").value<QObject *>(); QVERIFY(nestedObject != nullptr); @@ -1269,6 +1269,7 @@ void tst_qqmlcomponent::boundComponent() { QQmlComponent component(&engine, testFileUrl("BoundInlineComponent.qml")); QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QVERIFY(component.isBound()); QScopedPointer<QObject> o(component.create()); QVERIFY2(!o.isNull(), qPrintable(component.errorString())); @@ -1282,11 +1283,22 @@ void tst_qqmlcomponent::boundComponent() { QQmlComponent component(&engine, testFileUrl("boundInlineComponentUser.qml")); QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QVERIFY(!component.isBound()); QScopedPointer<QObject> o(component.create()); QVERIFY(o.isNull()); QVERIFY(component.errorString().contains( QLatin1String("Cannot instantiate bound inline component in different file"))); + + } + + { + QQmlComponent component(&engine); + QVERIFY(!component.isBound()); + + component.setData("pragma ComponentBehavior: Bound\nsyntax error", QUrl()); + QCOMPARE(component.errorString(), ":2 Syntax error\n"_L1); + QVERIFY(!component.isBound()); } } @@ -1334,6 +1346,34 @@ void tst_qqmlcomponent::loadFromModule() name); } +void tst_qqmlcomponent::loadFromModuleLifecycle() +{ + QQmlEngine engine; + QList<int> loadFromModuleOrder; + QList<int> plainLoadOrder; + const QList<int> expected {1, 2, 3}; + { + QQmlComponent component(&engine); + component.loadFromModule("test", "LifeCycleWatcher"); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + std::unique_ptr<QObject> root{ component.create() }; + LifeCycleWatcher *watcher = qobject_cast<LifeCycleWatcher *>(root.get()); + QVERIFY(watcher); + loadFromModuleOrder = watcher->states; + QCOMPARE(loadFromModuleOrder, expected); + } + { + QQmlComponent component(&engine); + component.setData("import test; LifeCycleWatcher {}", {}); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + std::unique_ptr<QObject> root{ component.create() }; + LifeCycleWatcher *watcher = qobject_cast<LifeCycleWatcher *>(root.get()); + QVERIFY(watcher); + plainLoadOrder = watcher->states; + } + QCOMPARE(loadFromModuleOrder, plainLoadOrder); +} + struct CallVerifyingIncubtor : QQmlIncubator { void setInitialState(QObject *) override { setInitialStateCalled = true; } @@ -1373,6 +1413,10 @@ void tst_qqmlcomponent::loadFromModuleFailures_data() QTest::addRow("CppSingleton") << u"QtQuick"_s << u"Application"_s << u"Application is a singleton, and cannot be loaded"_s; + QTest::addRow("passedFileName") << "plainqml" + << "Plain.qml" + << R"(Type "Plain" from module "plainqml" contains no inline component named "qml". )" + R"(To load the type "Plain", drop the ".qml" extension.)"; } void tst_qqmlcomponent::loadFromModuleFailures() @@ -1384,7 +1428,11 @@ void tst_qqmlcomponent::loadFromModuleFailures() QQmlEngine engine; QQmlComponent component(&engine); QSignalSpy errorSpy(&component, &QQmlComponent::statusChanged); + QSignalSpy progressSpy(&component, &QQmlComponent::progressChanged); component.loadFromModule(uri, typeName); + // verify that we changed the progress correctly to 1 + QTRY_VERIFY(!progressSpy.isEmpty()); + QTRY_COMPARE(progressSpy.last().at(0).toDouble(), 1.0); QVERIFY(!errorSpy.isEmpty()); QCOMPARE(errorSpy.first().first().value<QQmlComponent::Status>(), QQmlComponent::Error); @@ -1422,7 +1470,99 @@ void tst_qqmlcomponent::loadFromQrc() QQmlComponentPrivate *p = QQmlComponentPrivate::get(&component); QVERIFY(p); QVERIFY(p->compilationUnit); - QVERIFY(p->compilationUnit->aotCompiledFunctions); + QVERIFY(p->compilationUnit->baseCompilationUnit()->aotCompiledFunctions); +} + +void tst_qqmlcomponent::removeBinding() +{ + QQmlEngine e; + const QUrl url = testFileUrl("removeBinding.qml"); + QQmlComponent c(&e, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(url.toString() + QStringLiteral(":7:27: QML Component: Unsuitable arguments " + "passed to createObject(). The first argument " + "should be a QObject* or null, and the second " + "argument should be a JavaScript object or a " + "QVariantMap"))); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("result"), QStringLiteral("42")); + QCOMPARE(o->property("result2"), QStringLiteral("43")); +} + +void tst_qqmlcomponent::complexObjectArgument() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("complexObjectArgument.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->objectName(), QStringLiteral("26 - 25")); +} + +void tst_qqmlcomponent::bindingEvaluationOrder() +{ + // Note: This test explicitly tests the order in which bindings are + // evaluated, which is generally unspecified. This, however, exists + // as a regression test for QQmlObjectCreator code that is supposed + // to *not* mess with the QmlIR given to it. + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData(R"( + import QtQml + QtObject { + property var myList: ["dummy"] + property int p1: { myList.push("p1"); return 0; } + property int p2: { myList.push("p2"); return 0; } + })", QUrl()); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + + const QList<QVariant> myList = o->property("myList").toList(); + QCOMPARE(myList.size(), 3); + QCOMPARE(myList[0].toString(), u"dummy"_s); + QCOMPARE(myList[1].toString(), u"p1"_s); + QCOMPARE(myList[2].toString(), u"p2"_s); +} + +void tst_qqmlcomponent::compilationUnitsWithSameUrl() +{ + QQmlEngine engine; + engine.setUiLanguage("de_CH"); + + std::vector<std::unique_ptr<QObject>> objects; + for (int i = 0; i < 10; ++i) { + QQmlComponent component(&engine); + component.setData(R"( + import QtQml + QtObject { + function returnThing() : string { return Qt.uiLanguage } + } + )", QUrl("duplicate.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + + std::unique_ptr<QObject> o(component.create()); + QVERIFY(o.get()); + + QString result; + QMetaObject::invokeMethod(o.get(), "returnThing", Q_RETURN_ARG(QString, result)); + QCOMPARE(result, "de_CH"); + + objects.push_back(std::move(o)); + } + + gc(engine); + + for (const auto &o: objects) { + QString result; + QMetaObject::invokeMethod(o.get(), "returnThing", Q_RETURN_ARG(QString, result)); + QCOMPARE(result, "de_CH"); + } } QTEST_MAIN(tst_qqmlcomponent) |