aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@qt.io>2017-12-12 10:35:21 +0100
committerShawn Rutledge <shawn.rutledge@qt.io>2018-01-04 14:41:16 +0100
commit2bee46e3f10e2c44d185d7a51a06830b68529676 (patch)
treeb65ac19203edfc2972b6020dd040e46c43b5d1fb /tests/auto/qml
parent52f7ab28172cea3710a16775b7a512fce821fc77 (diff)
parent41293196b4db1aa7a0c616af312875c484639644 (diff)
Merge remote-tracking branch 'origin/5.9' into 5.10
Conflicts: src/qml/memory/qv4mm.cpp src/qml/memory/qv4mmdefs_p.h src/quick/items/qquickwindow.cpp src/quick/items/qquickwindow_p.h tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp Change-Id: I7021fa1edf076627a67048f41f7b201220262b09
Diffstat (limited to 'tests/auto/qml')
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/data/memory.qml17
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro5
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp22
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp152
-rw-r--r--tests/auto/qml/qqmlmetatype/data/testUnregisterCustomSingletonType.qml8
-rw-r--r--tests/auto/qml/qqmlmetatype/data/testUnregisterCustomType.qml8
-rw-r--r--tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp193
-rw-r--r--tests/auto/qml/qqmltypeloader/data/Intercept.qml41
-rw-r--r--tests/auto/qml/qqmltypeloader/data/test_intercept.qml53
-rw-r--r--tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp201
10 files changed, 696 insertions, 4 deletions
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/memory.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/memory.qml
new file mode 100644
index 0000000000..f39dcdf16a
--- /dev/null
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/memory.qml
@@ -0,0 +1,17 @@
+import QtQml 2.0
+
+Timer {
+ interval: 1
+ running: true
+
+ function recurse(i) {
+ var x = { t: [1, 2, 3, 4] }
+ console.log(x.t[i]);
+ if (i < 3)
+ recurse(i + 1);
+ else
+ Qt.quit();
+ }
+
+ onTriggered: recurse(0)
+}
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro b/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro
index a26f48e6c7..7c78b5fcb3 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro
@@ -8,7 +8,7 @@ include(../shared/debugutil.pri)
TESTDATA = data/*
-QT += core qml testlib gui-private core-private
+QT += core qml testlib testlib-private gui-private core-private
OTHER_FILES += \
data/pixmapCacheTest.qml \
@@ -20,4 +20,5 @@ OTHER_FILES += \
data/signalSourceLocation.qml \
data/javascript.qml \
data/timer.qml \
- data/qstr.qml
+ data/qstr.qml \
+ data/memory.qml
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
index 4d37f16e41..f60e821071 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
@@ -33,6 +33,7 @@
#include <private/qqmldebugconnection_p.h>
#include <QtTest/qtest.h>
+#include <private/qtestresult_p.h>
#include <QtCore/qlibraryinfo.h>
struct QQmlProfilerData
@@ -299,6 +300,7 @@ private slots:
void javascript();
void flushInterval();
void translationBinding();
+ void memory();
};
#define VERIFY(type, position, expected, checks) QVERIFY(verify(type, position, expected, checks))
@@ -725,6 +727,26 @@ void tst_QQmlProfilerService::translationBinding()
CheckDetailType | CheckMessageType);
}
+void tst_QQmlProfilerService::memory()
+{
+ connect(true, "memory.qml");
+ if (QTest::currentTestFailed() || QTestResult::skipCurrentTest())
+ return;
+
+ m_client->sendRecordingStatus(true);
+
+ checkTraceReceived();
+ checkJsHeap();
+
+ int smallItems = 0;
+ for (auto message : m_client->jsHeapMessages) {
+ if (message.detailType == QV4::Profiling::SmallItem)
+ ++smallItems;
+ }
+
+ QVERIFY(smallItems > 5);
+}
+
QTEST_MAIN(tst_QQmlProfilerService)
#include "tst_qqmlprofilerservice.moc"
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 7fb4c809f7..a56318f80c 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -44,6 +44,7 @@
#include <private/qqmlglobal_p.h>
#include <private/qqmlscriptstring_p.h>
#include <private/qqmlvmemetaobject_p.h>
+#include <private/qqmlcomponent_p.h>
#include "testtypes.h"
#include "testhttpserver.h"
@@ -254,6 +255,7 @@ private slots:
void rootObjectInCreationNotForSubObjects();
void lazyDeferredSubObject();
void deferredProperties();
+ void executeDeferredPropertiesOnce();
void noChildEvents();
@@ -4392,8 +4394,17 @@ void tst_qqmllanguage::deferredProperties()
QQmlListProperty<QObject> listProperty = object->property("listProperty").value<QQmlListProperty<QObject>>();
QCOMPARE(listProperty.count(&listProperty), 0);
+ QQmlData *qmlData = QQmlData::get(object.data());
+ QVERIFY(qmlData);
+
+ QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml
+ QCOMPARE(qmlData->deferredData.first()->bindings.count(), 3); // "innerobj", "innerlist1", "innerlist2"
+ QCOMPARE(qmlData->deferredData.last()->bindings.count(), 3); // "outerobj", "outerlist1", "outerlist2"
+
qmlExecuteDeferred(object.data());
+ QCOMPARE(qmlData->deferredData.count(), 0);
+
innerObj = object->findChild<QObject *>(QStringLiteral("innerobj")); // MyDeferredListProperty.qml
QVERIFY(innerObj);
QCOMPARE(innerObj->property("wasCompleted"), QVariant(true));
@@ -4419,6 +4430,147 @@ void tst_qqmllanguage::deferredProperties()
QCOMPARE(listProperty.at(&listProperty, 3)->property("wasCompleted"), QVariant(true));
}
+static void beginDeferredOnce(QQmlEnginePrivate *enginePriv,
+ const QQmlProperty &property, QQmlComponentPrivate::DeferredState *deferredState)
+{
+ QObject *object = property.object();
+ QQmlData *ddata = QQmlData::get(object);
+ Q_ASSERT(!ddata->deferredData.isEmpty());
+
+ int propertyIndex = property.index();
+
+ for (auto dit = ddata->deferredData.rbegin(); dit != ddata->deferredData.rend(); ++dit) {
+ QQmlData::DeferredData *deferData = *dit;
+
+ auto range = deferData->bindings.equal_range(propertyIndex);
+ if (range.first == deferData->bindings.end())
+ continue;
+
+ QQmlComponentPrivate::ConstructionState *state = new QQmlComponentPrivate::ConstructionState;
+ state->completePending = true;
+
+ QQmlContextData *creationContext = nullptr;
+ state->creator.reset(new QQmlObjectCreator(deferData->context->parent, deferData->compilationUnit, creationContext));
+
+ enginePriv->inProgressCreations++;
+
+ typedef QMultiHash<int, const QV4::CompiledData::Binding *> QV4PropertyBindingHash;
+ auto it = std::reverse_iterator<QV4PropertyBindingHash::iterator>(range.second);
+ auto last = std::reverse_iterator<QV4PropertyBindingHash::iterator>(range.first);
+ while (it != last) {
+ if (!state->creator->populateDeferredBinding(property, deferData, *it))
+ state->errors << state->creator->errors;
+ ++it;
+ }
+
+ deferredState->constructionStates += state;
+
+ // Cleanup any remaining deferred bindings for this property, also in inner contexts,
+ // to avoid executing them later and overriding the property that was just populated.
+ while (dit != ddata->deferredData.rend()) {
+ (*dit)->bindings.remove(propertyIndex);
+ ++dit;
+ }
+ break;
+ }
+}
+
+static void testExecuteDeferredOnce(const QQmlProperty &property)
+{
+ QObject *object = property.object();
+ QQmlData *data = QQmlData::get(object);
+ if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object)) {
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine);
+
+ QQmlComponentPrivate::DeferredState state;
+ beginDeferredOnce(ep, property, &state);
+
+ // Release deferred data for those compilation units that no longer have deferred bindings
+ data->releaseDeferredData();
+
+ QQmlComponentPrivate::completeDeferred(ep, &state);
+ }
+}
+
+void tst_qqmllanguage::executeDeferredPropertiesOnce()
+{
+ QQmlComponent component(&engine, testFile("deferredProperties.qml"));
+ VERIFY_ERRORS(0);
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QObjectList innerObjsAtCreation = object->findChildren<QObject *>(QStringLiteral("innerobj"));
+ QVERIFY(innerObjsAtCreation.isEmpty());
+
+ QObjectList outerObjsAtCreation = object->findChildren<QObject *>(QStringLiteral("outerobj"));
+ QVERIFY(outerObjsAtCreation.isEmpty());
+
+ QObject *groupProperty = object->property("groupProperty").value<QObject *>();
+ QVERIFY(!groupProperty);
+
+ QQmlListProperty<QObject> listProperty = object->property("listProperty").value<QQmlListProperty<QObject>>();
+ QCOMPARE(listProperty.count(&listProperty), 0);
+
+ QQmlData *qmlData = QQmlData::get(object.data());
+ QVERIFY(qmlData);
+
+ QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml
+ QCOMPARE(qmlData->deferredData.first()->bindings.count(), 3); // "innerobj", "innerlist1", "innerlist2"
+ QCOMPARE(qmlData->deferredData.last()->bindings.count(), 3); // "outerobj", "outerlist1", "outerlist2"
+
+ // first execution creates the outer object
+ testExecuteDeferredOnce(QQmlProperty(object.data(), "groupProperty"));
+
+ QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml
+ QCOMPARE(qmlData->deferredData.first()->bindings.count(), 2); // "innerlist1", "innerlist2"
+ QCOMPARE(qmlData->deferredData.last()->bindings.count(), 2); // "outerlist1", "outerlist2"
+
+ QObjectList innerObjsAfterFirstExecute = object->findChildren<QObject *>(QStringLiteral("innerobj")); // MyDeferredListProperty.qml
+ QVERIFY(innerObjsAfterFirstExecute.isEmpty());
+
+ QObjectList outerObjsAfterFirstExecute = object->findChildren<QObject *>(QStringLiteral("outerobj")); // deferredListProperty.qml
+ QCOMPARE(outerObjsAfterFirstExecute.count(), 1);
+ QCOMPARE(outerObjsAfterFirstExecute.first()->property("wasCompleted"), QVariant(true));
+
+ groupProperty = object->property("groupProperty").value<QObject *>();
+ QCOMPARE(groupProperty, outerObjsAfterFirstExecute.first());
+
+ listProperty = object->property("listProperty").value<QQmlListProperty<QObject>>();
+ QCOMPARE(listProperty.count(&listProperty), 0);
+
+ // re-execution does nothing (to avoid overriding the property)
+ testExecuteDeferredOnce(QQmlProperty(object.data(), "groupProperty"));
+
+ QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml
+ QCOMPARE(qmlData->deferredData.first()->bindings.count(), 2); // "innerlist1", "innerlist2"
+ QCOMPARE(qmlData->deferredData.last()->bindings.count(), 2); // "outerlist1", "outerlist2"
+
+ QObjectList innerObjsAfterSecondExecute = object->findChildren<QObject *>(QStringLiteral("innerobj")); // MyDeferredListProperty.qml
+ QVERIFY(innerObjsAfterSecondExecute.isEmpty());
+
+ QObjectList outerObjsAfterSecondExecute = object->findChildren<QObject *>(QStringLiteral("outerobj")); // deferredListProperty.qml
+ QCOMPARE(outerObjsAfterFirstExecute, outerObjsAfterSecondExecute);
+
+ groupProperty = object->property("groupProperty").value<QObject *>();
+ QCOMPARE(groupProperty, outerObjsAfterFirstExecute.first());
+
+ listProperty = object->property("listProperty").value<QQmlListProperty<QObject>>();
+ QCOMPARE(listProperty.count(&listProperty), 0);
+
+ // execution of a list property should execute all outer list bindings
+ testExecuteDeferredOnce(QQmlProperty(object.data(), "listProperty"));
+
+ QCOMPARE(qmlData->deferredData.count(), 0);
+
+ listProperty = object->property("listProperty").value<QQmlListProperty<QObject>>();
+ QCOMPARE(listProperty.count(&listProperty), 2);
+
+ QCOMPARE(listProperty.at(&listProperty, 0)->objectName(), QStringLiteral("outerlist1")); // deferredListProperty.qml
+ QCOMPARE(listProperty.at(&listProperty, 0)->property("wasCompleted"), QVariant(true));
+ QCOMPARE(listProperty.at(&listProperty, 1)->objectName(), QStringLiteral("outerlist2")); // deferredListProperty.qml
+ QCOMPARE(listProperty.at(&listProperty, 1)->property("wasCompleted"), QVariant(true));
+}
+
void tst_qqmllanguage::noChildEvents()
{
QQmlComponent component(&engine);
diff --git a/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomSingletonType.qml b/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomSingletonType.qml
new file mode 100644
index 0000000000..85b8f5ac8b
--- /dev/null
+++ b/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomSingletonType.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.7
+import mytypes 1.0
+
+Item {
+ id: root
+ property string text: StaticProvider.singletonGetString()
+}
+
diff --git a/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomType.qml b/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomType.qml
new file mode 100644
index 0000000000..f6ee4e9b77
--- /dev/null
+++ b/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomType.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.7
+import mytypes 1.0
+
+Item {
+ id: root
+ Controller { id: controller; objectName: "controller" }
+}
+
diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
index 798e3fd386..58361b4b12 100644
--- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
+++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
@@ -60,6 +60,8 @@ private slots:
void isList();
void defaultObject();
+ void unregisterCustomType();
+ void unregisterCustomSingletonType();
};
class TestType : public QObject
@@ -330,6 +332,197 @@ void tst_qqmlmetatype::externalEnums()
}
+class Controller1 : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString string MEMBER m_string)
+ Q_PROPERTY(Controller1Enum enumVal MEMBER m_enumVal)
+public:
+ enum Controller1Enum {
+ ENUM_VALUE_1 = 1,
+ ENUM_VALUE_2 = 2
+ };
+ Q_ENUMS(Controller1Enum)
+
+ Controller1(QObject *parent = nullptr) : QObject(parent), m_string("Controller #1"),
+ m_enumVal(ENUM_VALUE_1)
+ {}
+private:
+ QString m_string;
+ Controller1Enum m_enumVal;
+};
+
+class Controller2 : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString string MEMBER m_string)
+ Q_PROPERTY(Controller2Enum enumVal MEMBER m_enumVal)
+public:
+ enum Controller2Enum {
+ ENUM_VALUE_1 = 111,
+ ENUM_VALUE_2 = 222
+ };
+ Q_ENUMS(Controller2Enum)
+
+ Controller2(QObject *parent = nullptr) : QObject(parent), m_string("Controller #2"),
+ m_enumVal(ENUM_VALUE_1)
+ {}
+private:
+ QString m_string;
+ Controller2Enum m_enumVal;
+};
+
+void tst_qqmlmetatype::unregisterCustomType()
+{
+ int controllerId = 0;
+ {
+ QQmlEngine engine;
+ QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0);
+ QVERIFY(!type.isValid());
+ controllerId = qmlRegisterType<Controller1>("mytypes", 1, 0, "Controller");
+ type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0);
+ QVERIFY(type.isValid());
+ QVERIFY(!type.isInterface());
+ QVERIFY(!type.isSingleton());
+ QVERIFY(!type.isComposite());
+ QQmlComponent c(&engine, testFileUrl("testUnregisterCustomType.qml"));
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(obj);
+ QObject *controller = obj->findChild<QObject *>("controller");
+ QVERIFY(qobject_cast<Controller1 *>(controller));
+ QVariant stringVal = controller->property("string");
+ QCOMPARE(stringVal.type(), QVariant::String);
+ QCOMPARE(stringVal.toString(), QStringLiteral("Controller #1"));
+ QVariant enumVal = controller->property("enumVal");
+ QCOMPARE(enumVal.type(), QVariant::Int);
+ QCOMPARE(enumVal.toInt(), 1);
+ }
+ qmlUnregisterType(controllerId);
+ {
+ QQmlEngine engine;
+ QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0);
+ QVERIFY(!type.isValid());
+ controllerId = qmlRegisterType<Controller2>("mytypes", 1, 0, "Controller");
+ type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0);
+ QVERIFY(type.isValid());
+ QVERIFY(!type.isInterface());
+ QVERIFY(!type.isSingleton());
+ QVERIFY(!type.isComposite());
+ QQmlComponent c(&engine, testFileUrl("testUnregisterCustomType.qml"));
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(obj);
+ QObject *controller = obj->findChild<QObject *>("controller");
+ QVERIFY(qobject_cast<Controller2 *>(controller));
+ QVariant stringVal = controller->property("string");
+ QCOMPARE(stringVal.type(), QVariant::String);
+ QCOMPARE(stringVal.toString(), QStringLiteral("Controller #2"));
+ QVariant enumVal = controller->property("enumVal");
+ QCOMPARE(enumVal.type(), QVariant::Int);
+ QCOMPARE(enumVal.toInt(), 111);
+ }
+ qmlUnregisterType(controllerId);
+ {
+ QQmlEngine engine;
+ QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0);
+ QVERIFY(!type.isValid());
+ controllerId = qmlRegisterType<Controller1>("mytypes", 1, 0, "Controller");
+ type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0);
+ QVERIFY(type.isValid());
+ QVERIFY(!type.isInterface());
+ QVERIFY(!type.isSingleton());
+ QVERIFY(!type.isComposite());
+ QQmlComponent c(&engine, testFileUrl("testUnregisterCustomType.qml"));
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(obj);
+ QObject *controller = obj->findChild<QObject *>("controller");
+ QVERIFY(qobject_cast<Controller1 *>(controller));
+ QVariant stringVal = controller->property("string");
+ QCOMPARE(stringVal.type(), QVariant::String);
+ QCOMPARE(stringVal.toString(), QStringLiteral("Controller #1"));
+ QVariant enumVal = controller->property("enumVal");
+ QCOMPARE(enumVal.type(), QVariant::Int);
+ QCOMPARE(enumVal.toInt(), 1);
+ }
+}
+
+class StaticProvider1 : public QObject
+{
+ Q_OBJECT
+public:
+ StaticProvider1(QObject *parent = nullptr) : QObject(parent) {}
+ Q_INVOKABLE QString singletonGetString() { return "StaticProvider #1"; }
+};
+
+static QObject* createStaticProvider1(QQmlEngine *, QJSEngine *)
+{
+ return new StaticProvider1;
+}
+
+class StaticProvider2 : public QObject
+{
+ Q_OBJECT
+public:
+ StaticProvider2(QObject *parent = nullptr) : QObject(parent) {}
+ Q_INVOKABLE QString singletonGetString() { return "StaticProvider #2"; }
+};
+
+static QObject* createStaticProvider2(QQmlEngine *, QJSEngine *)
+{
+ return new StaticProvider2;
+}
+
+void tst_qqmlmetatype::unregisterCustomSingletonType()
+{
+ int staticProviderId = 0;
+ {
+ QQmlEngine engine;
+ staticProviderId = qmlRegisterSingletonType<StaticProvider1>("mytypes", 1, 0, "StaticProvider", createStaticProvider1);
+ QQmlType type = QQmlMetaType::qmlType(QString("StaticProvider"), QString("mytypes"), 1, 0);
+ QVERIFY(type.isValid());
+ QVERIFY(!type.isInterface());
+ QVERIFY(type.isSingleton());
+ QVERIFY(!type.isComposite());
+ QQmlComponent c(&engine, testFileUrl("testUnregisterCustomSingletonType.qml"));
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(obj.data());
+ QVariant stringVal = obj->property("text");
+ QCOMPARE(stringVal.type(), QVariant::String);
+ QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1"));
+ }
+ qmlUnregisterType(staticProviderId);
+ {
+ QQmlEngine engine;
+ staticProviderId = qmlRegisterSingletonType<StaticProvider2>("mytypes", 1, 0, "StaticProvider", createStaticProvider2);
+ QQmlType type = QQmlMetaType::qmlType(QString("StaticProvider"), QString("mytypes"), 1, 0);
+ QVERIFY(type.isValid());
+ QVERIFY(!type.isInterface());
+ QVERIFY(type.isSingleton());
+ QVERIFY(!type.isComposite());
+ QQmlComponent c(&engine, testFileUrl("testUnregisterCustomSingletonType.qml"));
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(obj.data());
+ QVariant stringVal = obj->property("text");
+ QCOMPARE(stringVal.type(), QVariant::String);
+ QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #2"));
+ }
+ qmlUnregisterType(staticProviderId);
+ {
+ QQmlEngine engine;
+ staticProviderId = qmlRegisterSingletonType<StaticProvider1>("mytypes", 1, 0, "StaticProvider", createStaticProvider1);
+ QQmlType type = QQmlMetaType::qmlType(QString("StaticProvider"), QString("mytypes"), 1, 0);
+ QVERIFY(type.isValid());
+ QVERIFY(!type.isInterface());
+ QVERIFY(type.isSingleton());
+ QVERIFY(!type.isComposite());
+ QQmlComponent c(&engine, testFileUrl("testUnregisterCustomSingletonType.qml"));
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(obj.data());
+ QVariant stringVal = obj->property("text");
+ QCOMPARE(stringVal.type(), QVariant::String);
+ QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1"));
+ }
+}
+
QTEST_MAIN(tst_qqmlmetatype)
#include "tst_qqmlmetatype.moc"
diff --git a/tests/auto/qml/qqmltypeloader/data/Intercept.qml b/tests/auto/qml/qqmltypeloader/data/Intercept.qml
new file mode 100644
index 0000000000..b557b4b941
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/Intercept.qml
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import Fast 1.0
+
+Item {
+ Rectangle {
+ color: "red"
+ width: 100
+ height: 100
+ }
+ Fast {
+
+ }
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/test_intercept.qml b/tests/auto/qml/qqmltypeloader/data/test_intercept.qml
new file mode 100644
index 0000000000..0d64cb7e28
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/test_intercept.qml
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+ListView {
+ width: 400
+ height: 500
+ model: 2
+
+ id: test
+ property int created: 0
+ property int loaded: 0
+
+ delegate: Loader {
+ width: ListView.view.width
+ height: 100
+ asynchronous: true
+ source: index == 0 ? "Intercept.qml" : "GenericView.qml"
+
+ onLoaded: {
+ test.loaded++
+ }
+ Component.onCompleted: {
+ test.created++
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
index 68b450ab26..5ab729042f 100644
--- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
+++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
@@ -28,6 +28,7 @@
#include <QtTest/QtTest>
#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlnetworkaccessmanagerfactory.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
#include <QtQml/private/qqmlengine_p.h>
@@ -45,6 +46,7 @@ private slots:
void trimCache2();
void keepSingleton();
void keepRegistrations();
+ void intercept();
};
void tst_QQMLTypeLoader::testLoadComplete()
@@ -70,7 +72,7 @@ void tst_QQMLTypeLoader::loadComponentSynchronously()
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
QLatin1String(".*nonprotocol::1:1: QtObject is not a type.*")));
QQmlComponent component(&engine, testFileUrl("load_synchronous.qml"));
- QObject *o = component.create();
+ QScopedPointer<QObject> o(component.create());
QVERIFY(o);
}
@@ -111,7 +113,7 @@ void tst_QQMLTypeLoader::trimCache()
void tst_QQMLTypeLoader::trimCache2()
{
- QQuickView *window = new QQuickView();
+ QScopedPointer<QQuickView> window(new QQuickView());
window->setSource(testFileUrl("trim_cache2.qml"));
QQmlTypeLoader &loader = QQmlEnginePrivate::get(window->engine())->typeLoader;
// in theory if gc has already run this could be false
@@ -192,6 +194,201 @@ void tst_QQMLTypeLoader::keepRegistrations()
verifyTypes(true, false); // qmlRegisterType creates an undeletable type.
}
+class NetworkReply : public QNetworkReply
+{
+public:
+ NetworkReply()
+ {
+ open(QIODevice::ReadOnly);
+ }
+
+ void setData(const QByteArray &data)
+ {
+ if (isFinished())
+ return;
+ m_buffer = data;
+ emit readyRead();
+ setFinished(true);
+ emit finished();
+ }
+
+ void fail()
+ {
+ if (isFinished())
+ return;
+ m_buffer.clear();
+ setError(ContentNotFoundError, "content not found");
+ emit error(ContentNotFoundError);
+ setFinished(true);
+ emit finished();
+ }
+
+ qint64 bytesAvailable() const override
+ {
+ return m_buffer.size();
+ }
+
+ qint64 readData(char *data, qint64 maxlen) override
+ {
+ if (m_buffer.length() < maxlen)
+ maxlen = m_buffer.length();
+ std::memcpy(data, m_buffer.data(), maxlen);
+ m_buffer.remove(0, maxlen);
+ return maxlen;
+ }
+
+ void abort() override
+ {
+ if (isFinished())
+ return;
+ m_buffer.clear();
+ setFinished(true);
+ emit finished();
+ }
+
+private:
+ QByteArray m_buffer;
+};
+
+class NetworkAccessManager : public QNetworkAccessManager
+{
+ Q_OBJECT
+public:
+
+ NetworkAccessManager(QObject *parent) : QNetworkAccessManager(parent)
+ {
+ }
+
+ QNetworkReply *createRequest(Operation op, const QNetworkRequest &request,
+ QIODevice *outgoingData) override
+ {
+ QUrl url = request.url();
+ QString scheme = url.scheme();
+ if (op != GetOperation || !scheme.endsWith("+debug"))
+ return QNetworkAccessManager::createRequest(op, request, outgoingData);
+
+ scheme.chop(sizeof("+debug") - 1);
+ url.setScheme(scheme);
+
+ NetworkReply *reply = new NetworkReply;
+ QString filename = QQmlFile::urlToLocalFileOrQrc(url);
+ QTimer::singleShot(10, reply, [this, reply, filename]() {
+ if (filename.isEmpty()) {
+ reply->fail();
+ } else {
+ QFile file(filename);
+ if (file.open(QIODevice::ReadOnly)) {
+ emit loaded(filename);
+ reply->setData(transformQmldir(filename, file.readAll()));
+ } else
+ reply->fail();
+ }
+ });
+ return reply;
+ }
+
+ QByteArray transformQmldir(const QString &filename, const QByteArray &content)
+ {
+ if (!filename.endsWith("/qmldir"))
+ return content;
+
+ // Make qmldir plugin paths absolute, so that we don't try to load them over the network
+ QByteArray result;
+ QByteArray path = filename.toUtf8();
+ path.chop(sizeof("qmldir") - 1);
+ for (QByteArray line : content.split('\n')) {
+ if (line.isEmpty())
+ continue;
+ QList<QByteArray> segments = line.split(' ');
+ if (segments.startsWith("plugin")) {
+ if (segments.length() == 2) {
+ segments.append(path);
+ } else if (segments.length() == 3) {
+ if (!segments[2].startsWith('/'))
+ segments[2] = path + segments[2];
+ } else {
+ // Invalid plugin declaration. Ignore
+ }
+ result.append(segments.join(' '));
+ } else {
+ result.append(line);
+ }
+ result.append('\n');
+ }
+ return result;
+ }
+
+signals:
+ void loaded(const QString &filename);
+};
+
+class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
+{
+public:
+ QStringList loadedFiles;
+
+ QNetworkAccessManager *create(QObject *parent) override
+ {
+ NetworkAccessManager *manager = new NetworkAccessManager(parent);
+ QObject::connect(manager, &NetworkAccessManager::loaded, [this](const QString &filename) {
+ loadedFiles.append(filename);
+ });
+ return manager;
+ }
+};
+
+class UrlInterceptor : public QQmlAbstractUrlInterceptor
+{
+public:
+ QUrl intercept(const QUrl &path, DataType type) override
+ {
+ Q_UNUSED(type);
+ if (!QQmlFile::isLocalFile(path))
+ return path;
+
+ QUrl result = path;
+ QString scheme = result.scheme();
+ if (!scheme.endsWith("+debug"))
+ result.setScheme(scheme + "+debug");
+ return result;
+ }
+};
+
+void tst_QQMLTypeLoader::intercept()
+{
+ qmlClearTypeRegistrations();
+
+ QQmlEngine engine;
+ engine.addImportPath(dataDirectory());
+ engine.addImportPath(QT_TESTCASE_BUILDDIR);
+
+ UrlInterceptor interceptor;
+ NetworkAccessManagerFactory factory;
+
+ engine.setUrlInterceptor(&interceptor);
+ engine.setNetworkAccessManagerFactory(&factory);
+
+ QQmlComponent component(&engine, testFileUrl("test_intercept.qml"));
+
+ QVERIFY(component.status() != QQmlComponent::Ready);
+ QTRY_VERIFY2(component.status() == QQmlComponent::Ready,
+ component.errorString().toUtf8().constData());
+
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(o.data());
+
+ QTRY_COMPARE(o->property("created").toInt(), 2);
+ QTRY_COMPARE(o->property("loaded").toInt(), 2);
+
+ QVERIFY(factory.loadedFiles.length() >= 6);
+ QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/test_intercept.qml"));
+ QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/Intercept.qml"));
+ QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/Fast/qmldir"));
+ QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/Fast/Fast.qml"));
+ QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/GenericView.qml"));
+ QVERIFY(factory.loadedFiles.contains(QLatin1String(QT_TESTCASE_BUILDDIR) + "/Slow/qmldir"));
+}
+
QTEST_MAIN(tst_QQMLTypeLoader)
#include "tst_qqmltypeloader.moc"