aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp')
-rw-r--r--tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp186
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)