aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml/qqmltypeloader
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qml/qqmltypeloader')
-rw-r--r--tests/auto/qml/qqmltypeloader/data/Base.qml3
-rw-r--r--tests/auto/qml/qqmltypeloader/data/ComponentWithIncubator.qml9
-rw-r--r--tests/auto/qml/qqmltypeloader/data/CppRegisteredSingleton1.qml6
-rw-r--r--tests/auto/qml/qqmltypeloader/data/CppRegisteredSingleton2.qml7
-rw-r--r--tests/auto/qml/qqmltypeloader/data/Fast/Fast.qml4
-rw-r--r--tests/auto/qml/qqmltypeloader/data/Fast/qmldir1
-rw-r--r--tests/auto/qml/qqmltypeloader/data/Intercept.qml41
-rw-r--r--tests/auto/qml/qqmltypeloader/data/Load.qml11
-rw-r--r--tests/auto/qml/qqmltypeloader/data/Singleton.qml7
-rw-r--r--tests/auto/qml/qqmltypeloader/data/ValueSource.qml7
-rw-r--r--tests/auto/qml/qqmltypeloader/data/implicitcomponent.qml10
-rw-r--r--tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/Singleton1.qml7
-rw-r--r--tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/Singleton2.qml7
-rw-r--r--tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/qmldir2
-rw-r--r--tests/auto/qml/qqmltypeloader/data/multisingletonuser.qml7
-rw-r--r--tests/auto/qml/qqmltypeloader/data/redirected/Imported.qml5
-rw-r--r--tests/auto/qml/qqmltypeloader/data/redirected/Redirected.qml5
-rw-r--r--tests/auto/qml/qqmltypeloader/data/redirected/qmldir1
-rw-r--r--tests/auto/qml/qqmltypeloader/data/singletonuser.qml6
-rw-r--r--tests/auto/qml/qqmltypeloader/data/test_intercept.qml53
-rw-r--r--tests/auto/qml/qqmltypeloader/data/trim_cache3.qml14
-rw-r--r--tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp391
-rw-r--r--tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.pro7
23 files changed, 606 insertions, 5 deletions
diff --git a/tests/auto/qml/qqmltypeloader/data/Base.qml b/tests/auto/qml/qqmltypeloader/data/Base.qml
new file mode 100644
index 0000000000..431c659424
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/Base.qml
@@ -0,0 +1,3 @@
+import QtQml 2.0
+
+QtObject {}
diff --git a/tests/auto/qml/qqmltypeloader/data/ComponentWithIncubator.qml b/tests/auto/qml/qqmltypeloader/data/ComponentWithIncubator.qml
new file mode 100644
index 0000000000..b3610831df
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/ComponentWithIncubator.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+
+Item {
+ Repeater {
+ model: 3
+ Item {}
+ }
+}
+
diff --git a/tests/auto/qml/qqmltypeloader/data/CppRegisteredSingleton1.qml b/tests/auto/qml/qqmltypeloader/data/CppRegisteredSingleton1.qml
new file mode 100644
index 0000000000..f4ad5e5f7a
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/CppRegisteredSingleton1.qml
@@ -0,0 +1,6 @@
+pragma Singleton
+import QtQuick 2.0
+
+Item {
+ property bool ok: true
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/CppRegisteredSingleton2.qml b/tests/auto/qml/qqmltypeloader/data/CppRegisteredSingleton2.qml
new file mode 100644
index 0000000000..55dd57517f
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/CppRegisteredSingleton2.qml
@@ -0,0 +1,7 @@
+pragma Singleton
+import QtQuick 2.0
+import cppsingletonmodule 1.0
+
+Item {
+ property bool ok: CppRegisteredSingleton1.ok
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/Fast/Fast.qml b/tests/auto/qml/qqmltypeloader/data/Fast/Fast.qml
new file mode 100644
index 0000000000..e879577e10
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/Fast/Fast.qml
@@ -0,0 +1,4 @@
+import QtQml 2.0
+
+QtObject {
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/Fast/qmldir b/tests/auto/qml/qqmltypeloader/data/Fast/qmldir
new file mode 100644
index 0000000000..a6a4c9e6c7
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/Fast/qmldir
@@ -0,0 +1 @@
+Fast 1.0 Fast.qml
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/Load.qml b/tests/auto/qml/qqmltypeloader/data/Load.qml
new file mode 100644
index 0000000000..0b893bb5cd
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/Load.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+
+Item {
+ property int xy: loader.xy
+ Loader {
+ id: loader
+ asynchronous: true
+ source: 'Base.qml'
+ property int xy: item.xy
+ }
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/Singleton.qml b/tests/auto/qml/qqmltypeloader/data/Singleton.qml
new file mode 100644
index 0000000000..3a1b1c1493
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/Singleton.qml
@@ -0,0 +1,7 @@
+pragma Singleton
+import QtQml 2.0
+import modulewithsingleton 1.0
+
+QtObject {
+ property bool ok: true
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/ValueSource.qml b/tests/auto/qml/qqmltypeloader/data/ValueSource.qml
new file mode 100644
index 0000000000..19e6e730c8
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/ValueSource.qml
@@ -0,0 +1,7 @@
+pragma Singleton
+import QtQuick 2.6
+
+Item {
+ id: valueSource
+ property int something: 10
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/implicitcomponent.qml b/tests/auto/qml/qqmltypeloader/data/implicitcomponent.qml
new file mode 100644
index 0000000000..9cebc88c8b
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/implicitcomponent.qml
@@ -0,0 +1,10 @@
+import QtQml 2.2
+
+QtObject {
+ property Component some: QtObject {
+ property int rrr: 2
+ property Component onemore: QtObject {
+ property int brrrr: -1
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/Singleton1.qml b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/Singleton1.qml
new file mode 100644
index 0000000000..34eca59f86
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/Singleton1.qml
@@ -0,0 +1,7 @@
+pragma Singleton
+import QtQuick 2.0
+import "."
+
+Item {
+ property bool ok: true
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/Singleton2.qml b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/Singleton2.qml
new file mode 100644
index 0000000000..607d85d7fb
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/Singleton2.qml
@@ -0,0 +1,7 @@
+pragma Singleton
+import QtQuick 2.0
+import "."
+
+Item {
+ property bool ok: Singleton1.ok
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/qmldir b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/qmldir
new file mode 100644
index 0000000000..71b889a12d
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/qmldir
@@ -0,0 +1,2 @@
+singleton Singleton1 1.0 Singleton1.qml
+singleton Singleton2 1.0 Singleton2.qml
diff --git a/tests/auto/qml/qqmltypeloader/data/multisingletonuser.qml b/tests/auto/qml/qqmltypeloader/data/multisingletonuser.qml
new file mode 100644
index 0000000000..b80e2c5223
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/multisingletonuser.qml
@@ -0,0 +1,7 @@
+import QtQml 2.0
+import multisingletonmodule 1.0
+import cppsingletonmodule 1.0
+
+QtObject {
+ property bool ok: Singleton2.ok && CppRegisteredSingleton2.ok
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/redirected/Imported.qml b/tests/auto/qml/qqmltypeloader/data/redirected/Imported.qml
new file mode 100644
index 0000000000..62954fe1b2
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/redirected/Imported.qml
@@ -0,0 +1,5 @@
+import QtQml 2.0
+
+QtObject {
+ property int xy: 323232
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/redirected/Redirected.qml b/tests/auto/qml/qqmltypeloader/data/redirected/Redirected.qml
new file mode 100644
index 0000000000..40fec5ed31
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/redirected/Redirected.qml
@@ -0,0 +1,5 @@
+import QtQml 2.0
+
+Imported {
+
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/redirected/qmldir b/tests/auto/qml/qqmltypeloader/data/redirected/qmldir
new file mode 100644
index 0000000000..8eb1fa5c18
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/redirected/qmldir
@@ -0,0 +1 @@
+Imported 1.0 Imported.qml
diff --git a/tests/auto/qml/qqmltypeloader/data/singletonuser.qml b/tests/auto/qml/qqmltypeloader/data/singletonuser.qml
new file mode 100644
index 0000000000..79ca47e12f
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/singletonuser.qml
@@ -0,0 +1,6 @@
+import QtQml 2.0
+import modulewithsingleton 1.0
+
+QtObject {
+ property bool ok: Singleton.ok
+}
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/data/trim_cache3.qml b/tests/auto/qml/qqmltypeloader/data/trim_cache3.qml
new file mode 100644
index 0000000000..219c7d3bcb
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/trim_cache3.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+
+Item {
+ width: 400
+ height: 400
+
+ property alias source: loader.source
+
+ Loader {
+ id: loader
+ source: "ComponentWithIncubator.qml"
+ }
+}
+
diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
index 3d3a7ff725..0c4abf19f4 100644
--- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
+++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
@@ -28,10 +28,15 @@
#include <QtTest/QtTest>
#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlnetworkaccessmanagerfactory.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
+#if QT_CONFIG(process)
+#include <QtCore/qprocess.h>
+#endif
#include <QtQml/private/qqmlengine_p.h>
#include <QtQml/private/qqmltypeloader_p.h>
+#include "../../shared/testhttpserver.h"
#include "../../shared/util.h"
class tst_QQMLTypeLoader : public QQmlDataTest
@@ -43,6 +48,14 @@ private slots:
void loadComponentSynchronously();
void trimCache();
void trimCache2();
+ void trimCache3();
+ void keepSingleton();
+ void keepRegistrations();
+ void intercept();
+ void redirect();
+ void qmlSingletonWithinModule();
+ void multiSingletonModule();
+ void implicitComponentModule();
};
void tst_QQMLTypeLoader::testLoadComplete()
@@ -56,7 +69,7 @@ void tst_QQMLTypeLoader::testLoadComplete()
QVERIFY(QTest::qWaitForWindowExposed(window));
QObject *rootObject = window->rootObject();
- QTRY_VERIFY(rootObject != 0);
+ QTRY_VERIFY(rootObject != nullptr);
QTRY_COMPARE(rootObject->property("created").toInt(), 2);
QTRY_COMPARE(rootObject->property("loaded").toInt(), 2);
delete window;
@@ -68,7 +81,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);
}
@@ -80,7 +93,7 @@ void tst_QQMLTypeLoader::trimCache()
QUrl url = testFileUrl("trim_cache.qml");
url.setQuery(QString::number(i));
- QQmlTypeData *data = loader.getType(url);
+ QQmlTypeData *data = loader.getType(url).take();
// Run an event loop to receive the callback that release()es.
QTRY_COMPARE(data->count(), 2);
@@ -109,7 +122,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
@@ -120,6 +133,376 @@ void tst_QQMLTypeLoader::trimCache2()
QCOMPARE(loader.isTypeLoaded(testFileUrl("MyComponent2.qml")), false);
}
+// test trimming the cache of an item that contains sub-items created via incubation
+void tst_QQMLTypeLoader::trimCache3()
+{
+ QScopedPointer<QQuickView> window(new QQuickView());
+ window->setSource(testFileUrl("trim_cache3.qml"));
+ QQmlTypeLoader &loader = QQmlEnginePrivate::get(window->engine())->typeLoader;
+ QCOMPARE(loader.isTypeLoaded(testFileUrl("ComponentWithIncubator.qml")), true);
+
+ QQmlProperty::write(window->rootObject(), "source", QString());
+
+ // handle our deleteLater and cleanup
+ QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ QCoreApplication::processEvents();
+ window->engine()->collectGarbage();
+
+ window->engine()->trimComponentCache();
+
+ QCOMPARE(loader.isTypeLoaded(testFileUrl("ComponentWithIncubator.qml")), false);
+}
+
+static void checkSingleton(const QString &dataDirectory)
+{
+ QQmlEngine engine;
+ engine.addImportPath(dataDirectory);
+ QQmlComponent component(&engine);
+ component.setData("import ClusterDemo 1.0\n"
+ "import QtQuick 2.6\n"
+ "import \"..\"\n"
+ "Item { property int t: ValueSource.something }",
+ QUrl::fromLocalFile(dataDirectory + "/abc/Xyz.qml"));
+ QCOMPARE(component.status(), QQmlComponent::Ready);
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(o.data());
+ QCOMPARE(o->property("t").toInt(), 10);
+}
+
+void tst_QQMLTypeLoader::keepSingleton()
+{
+ qmlRegisterSingletonType(testFileUrl("ValueSource.qml"), "ClusterDemo", 1, 0, "ValueSource");
+ checkSingleton(dataDirectory());
+ QQmlMetaType::freeUnusedTypesAndCaches();
+ checkSingleton(dataDirectory());
+}
+
+class TestObject : public QObject
+{
+ Q_OBJECT
+public:
+ TestObject(QObject *parent = nullptr) : QObject(parent) {}
+};
+
+QML_DECLARE_TYPE(TestObject)
+
+static void verifyTypes(bool shouldHaveTestObject, bool shouldHaveFast)
+{
+ bool hasTestObject = false;
+ bool hasFast = false;
+ for (const QQmlType &type : QQmlMetaType::qmlAllTypes()) {
+ if (type.elementName() == QLatin1String("Fast"))
+ hasFast = true;
+ else if (type.elementName() == QLatin1String("TestObject"))
+ hasTestObject = true;
+ }
+ QCOMPARE(hasTestObject, shouldHaveTestObject);
+ QCOMPARE(hasFast, shouldHaveFast);
+}
+
+void tst_QQMLTypeLoader::keepRegistrations()
+{
+ verifyTypes(false, false);
+ qmlRegisterType<TestObject>("Test", 1, 0, "TestObject");
+ verifyTypes(true, false);
+
+ {
+ QQmlEngine engine;
+ engine.addImportPath(dataDirectory());
+ QQmlComponent component(&engine);
+ component.setData("import Fast 1.0\nFast {}", QUrl());
+ QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8().constData());
+ QCOMPARE(component.status(), QQmlComponent::Ready);
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(o.data());
+ verifyTypes(true, true);
+ }
+
+ verifyTypes(true, false); // Fast is gone again, even though an event was still scheduled.
+ QQmlMetaType::freeUnusedTypesAndCaches();
+ 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"));
+}
+
+void tst_QQMLTypeLoader::redirect()
+{
+ TestHTTPServer server;
+ QVERIFY2(server.listen(), qPrintable(server.errorString()));
+ QVERIFY(server.serveDirectory(dataDirectory()));
+ server.addRedirect("Base.qml", server.urlString("/redirected/Redirected.qml"));
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadUrl(server.urlString("/Load.qml"), QQmlComponent::Asynchronous);
+ QTRY_VERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ QObject *object = component.create();
+ QTRY_COMPARE(object->property("xy").toInt(), 323232);
+}
+
+void tst_QQMLTypeLoader::qmlSingletonWithinModule()
+{
+ qmlClearTypeRegistrations();
+ QQmlEngine engine;
+ qmlRegisterSingletonType(testFileUrl("Singleton.qml"), "modulewithsingleton", 1, 0, "Singleton");
+
+ QQmlComponent component(&engine, testFileUrl("singletonuser.qml"));
+ QCOMPARE(component.status(), QQmlComponent::Ready);
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QVERIFY(obj->property("ok").toBool());
+}
+
+static void checkCleanCacheLoad(const QString &testCase)
+{
+#if QT_CONFIG(process)
+ const char *skipKey = "QT_TST_QQMLTYPELOADER_SKIP_MISMATCH";
+ if (qEnvironmentVariableIsSet(skipKey))
+ return;
+ for (int i = 0; i < 5; ++i) {
+ QProcess child;
+ child.setProgram(QCoreApplication::applicationFilePath());
+ child.setArguments(QStringList(testCase));
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ env.insert(QLatin1String("QT_LOGGING_RULES"), QLatin1String("qt.qml.diskcache.debug=true"));
+ env.insert(QLatin1String(skipKey), QLatin1String("1"));
+ child.setProcessEnvironment(env);
+ child.start();
+ QVERIFY(child.waitForFinished());
+ QCOMPARE(child.exitCode(), 0);
+ QVERIFY(!child.readAllStandardOutput().contains("Checksum mismatch for cached version"));
+ QVERIFY(!child.readAllStandardError().contains("Checksum mismatch for cached version"));
+ }
+#else
+ Q_UNUSED(testCase);
+#endif
+}
+
+void tst_QQMLTypeLoader::multiSingletonModule()
+{
+ qmlClearTypeRegistrations();
+ QQmlEngine engine;
+ engine.addImportPath(testFile("imports"));
+
+ qmlRegisterSingletonType(testFileUrl("CppRegisteredSingleton1.qml"), "cppsingletonmodule",
+ 1, 0, "CppRegisteredSingleton1");
+ qmlRegisterSingletonType(testFileUrl("CppRegisteredSingleton2.qml"), "cppsingletonmodule",
+ 1, 0, "CppRegisteredSingleton2");
+
+ QQmlComponent component(&engine, testFileUrl("multisingletonuser.qml"));
+ QCOMPARE(component.status(), QQmlComponent::Ready);
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QVERIFY(obj->property("ok").toBool());
+
+ checkCleanCacheLoad(QLatin1String("multiSingletonModule"));
+}
+
+void tst_QQMLTypeLoader::implicitComponentModule()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("implicitcomponent.qml"));
+ QCOMPARE(component.status(), QQmlComponent::Ready);
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+
+ checkCleanCacheLoad(QLatin1String("implicitComponentModule"));
+}
+
QTEST_MAIN(tst_QQMLTypeLoader)
#include "tst_qqmltypeloader.moc"
diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.pro b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.pro
index 3a20e94741..0352561e03 100644
--- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.pro
+++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.pro
@@ -3,7 +3,12 @@ TARGET = tst_qqmltypeloader
QT += qml testlib qml-private quick
macx:CONFIG -= app_bundle
-SOURCES += tst_qqmltypeloader.cpp
+SOURCES += \
+ tst_qqmltypeloader.cpp \
+ ../../shared/testhttpserver.cpp
+
+HEADERS += \
+ ../../shared/testhttpserver.h
include (../../shared/util.pri)