aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qml')
-rw-r--r--tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp40
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp13
-rw-r--r--tests/auto/qml/qmllint/data/Enumerei/Main.qml22
-rw-r--r--tests/auto/qml/qmllint/data/Enumerei/plugins.qmltypes44
-rw-r--r--tests/auto/qml/qmllint/data/Enumerei/qmldir3
-rw-r--r--tests/auto/qml/qmllint/data/enumValid.qml11
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp1
-rw-r--r--tests/auto/qml/qmltyperegistrar/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qmltyperegistrar/brokenEnums.json57
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp54
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h10
-rw-r--r--tests/auto/qml/qqmlcomponent/lifecyclewatcher.h25
-rw-r--r--tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp50
-rw-r--r--tests/auto/qml/qqmldelegatemodel/data/proxyModelWithDelayedSourceModelInListView.qml30
-rw-r--r--tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp73
-rw-r--r--tests/auto/qml/qqmlengine/tst_qqmlengine.cpp37
-rw-r--r--tests/auto/qml/qqmlimport/tst_qqmlimport.cpp10
-rw-r--r--tests/auto/qml/qqmllanguage/data/TextItem.qml7
-rw-r--r--tests/auto/qml/qqmllanguage/data/Wrap.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.cpp4
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp15
-rw-r--r--tests/auto/qml/qqmltypeloader/data/SlowImporter/A.qml2
-rw-r--r--tests/auto/qml/qqmltypeloader/data/SlowImporter/qmldir2
-rw-r--r--tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp61
-rw-r--r--tests/auto/qml/qv4mm/data/forInOnProxyMarksTarget.qml23
-rw-r--r--tests/auto/qml/qv4mm/tst_qv4mm.cpp28
26 files changed, 617 insertions, 12 deletions
diff --git a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp
index 0c4fd568a9..802adaee14 100644
--- a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp
+++ b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp
@@ -26,6 +26,8 @@ public:
private:
ConnectResult startQmlProcess(const QString &qmlFile);
void serveRequest(const QString &path);
+ void serveFile(const QString &path, const QByteArray &contents);
+
QList<QQmlDebugClient *> createClients() override;
void verifyProcessOutputContains(const QString &string) const;
@@ -42,6 +44,7 @@ private slots:
void connect();
void load();
+ void loadFromQrc();
void rerun();
void blacklist();
void error();
@@ -70,8 +73,7 @@ void tst_QQmlPreview::serveRequest(const QString &path)
} else {
QFile file(path);
if (file.open(QIODevice::ReadOnly)) {
- m_files.append(path);
- m_client->sendFile(path, file.readAll());
+ serveFile(path, file.readAll());
} else {
m_filesNotFound.append(path);
m_client->sendError(path);
@@ -79,6 +81,12 @@ void tst_QQmlPreview::serveRequest(const QString &path)
}
}
+void tst_QQmlPreview::serveFile(const QString &path, const QByteArray &contents)
+{
+ m_files.append(path);
+ m_client->sendFile(path, contents);
+}
+
QList<QQmlDebugClient *> tst_QQmlPreview::createClients()
{
m_client = new QQmlPreviewClient(m_connection);
@@ -162,6 +170,34 @@ void tst_QQmlPreview::load()
QVERIFY(m_serviceErrors.isEmpty());
}
+void tst_QQmlPreview::loadFromQrc()
+{
+ // One of the configuration files built into the "qml" executable.
+ const QString fromQrc(":/qt-project.org/imports/QmlRuntime/Config/default.qml");
+
+ QCOMPARE(QQmlDebugTest::connectTo(
+ QLibraryInfo::path(QLibraryInfo::BinariesPath) + "/qml",
+ QStringLiteral("QmlPreview"), fromQrc, true),
+ ConnectSuccess);
+
+ QVERIFY(m_client);
+ QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled);
+
+ serveFile(fromQrc, R"(
+ import QtQuick
+ Item {
+ Component.onCompleted: console.log("default.qml replaced")
+ }
+ )");
+
+ m_client->triggerLoad(QUrl("qrc" + fromQrc));
+ verifyProcessOutputContains("default.qml replaced");
+
+ m_process->stop();
+ QTRY_COMPARE(m_client->state(), QQmlDebugClient::NotConnected);
+ QVERIFY(m_serviceErrors.isEmpty());
+}
+
void tst_QQmlPreview::rerun()
{
const QString file("window.qml");
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index f9bd5c28aa..00854ccb43 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -319,6 +319,8 @@ private slots:
void consoleLogSequence();
+ void multiMatchingRegularExpression();
+
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
Q_INVOKABLE void throwingCppMethod2();
@@ -6415,6 +6417,17 @@ void tst_QJSEngine::consoleLogSequence()
QCOMPARE(stringListFetchCount, 1);
}
+void tst_QJSEngine::multiMatchingRegularExpression()
+{
+ QJSEngine engine;
+ const QJSValue result = engine.evaluate(R"(
+ "33312345.897".replace(/\./g, ",").replace(/\B(?=(\d{3})+(?!\d))/g, ".")
+ )");
+
+ QVERIFY(result.isString());
+ QCOMPARE(result.toString(), "33.312.345,897"_L1);
+}
+
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"
diff --git a/tests/auto/qml/qmllint/data/Enumerei/Main.qml b/tests/auto/qml/qmllint/data/Enumerei/Main.qml
new file mode 100644
index 0000000000..61c27e4670
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/Enumerei/Main.qml
@@ -0,0 +1,22 @@
+import QtQml 2.15
+import qtbug127308
+
+QtObject {
+ Component.onCompleted: {
+ console.log("Unscoped access:")
+ try { console.log("EnumTester::Scoped", EnumTester.S1, EnumTester.S2 ); } catch (a) { console.log("EnumTester::Scoped", a) }
+ try { console.log("EnumTester::Unscoped", EnumTester.U1, EnumTester.U2 ); } catch (b) { console.log("EnumTester::Unscoped", b) }
+ try { console.log("EnumTesterScoped::Scoped", EnumTesterScoped.S1, EnumTesterScoped.S2 ); } catch (c) { console.log("EnumTesterScoped::Scoped", c) }
+ try { console.log("EnumTesterScoped::Unscoped", EnumTesterScoped.U1, EnumTesterScoped.U2 ); } catch (d) { console.log("EnumTesterScoped::Unscoped", d) }
+ try { console.log("EnumTesterUnscoped::Scoped", EnumTesterUnscoped.S1, EnumTesterUnscoped.S2 ); } catch (e) { console.log("EnumTesterUnscoped::Scoped", e) }
+ try { console.log("EnumTesterUnscoped::Unscoped", EnumTesterUnscoped.U1, EnumTesterUnscoped.U2 ); } catch (f) { console.log("EnumTesterUnscoped::Unscoped", f) }
+ console.log()
+ console.log("Scoped access:")
+ try { console.log("EnumTester::Scoped", EnumTester.Scoped.S1, EnumTester.Scoped.S2 ); } catch (g) { console.log("EnumTester::Scoped", g) }
+ try { console.log("EnumTester::Unscoped", EnumTester.Unscoped.U1, EnumTester.Unscoped.U2 ); } catch (h) { console.log("EnumTester::Unscoped", h) }
+ try { console.log("EnumTesterScoped::Scoped", EnumTesterScoped.Scoped.S1, EnumTesterScoped.Scoped.S2 ); } catch (i) { console.log("EnumTesterScoped::Scoped", i) }
+ try { console.log("EnumTesterScoped::Unscoped", EnumTesterScoped.Unscoped.U1, EnumTesterScoped.Unscoped.U2 ); } catch (j) { console.log("EnumTesterScoped::Unscoped", j) }
+ try { console.log("EnumTesterUnscoped::Scoped", EnumTesterUnscoped.Scoped.S1, EnumTesterUnscoped.Scoped.S2 ); } catch (k) { console.log("EnumTesterUnscoped::Scoped", k) }
+ try { console.log("EnumTesterUnscoped::Unscoped", EnumTesterUnscoped.Unscoped.U1, EnumTesterUnscoped.Unscoped.U2 ); } catch (l) { console.log("EnumTesterUnscoped::Unscoped", l) }
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/Enumerei/plugins.qmltypes b/tests/auto/qml/qmllint/data/Enumerei/plugins.qmltypes
new file mode 100644
index 0000000000..212215362a
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/Enumerei/plugins.qmltypes
@@ -0,0 +1,44 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by qmltyperegistrar.
+
+Module {
+ Component {
+ file: "main.h"
+ name: "EnumTester"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: ["Enumerei/EnumTester 1.0"]
+ exportMetaObjectRevisions: [256]
+ Enum {
+ name: "Unscoped"
+ values: ["U1", "U2"]
+ }
+ Enum {
+ name: "Scoped"
+ isScoped: true
+ values: ["S1", "S2"]
+ }
+ }
+ Component {
+ file: "main.h"
+ name: "EnumTesterScoped"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: ["Enumerei/EnumTesterScoped 1.0"]
+ enforcesScopedEnums: true
+ exportMetaObjectRevisions: [256]
+ Enum {
+ name: "Unscoped"
+ values: ["U1", "U2"]
+ }
+ Enum {
+ name: "Scoped"
+ isScoped: true
+ values: ["S1", "S2"]
+ }
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/Enumerei/qmldir b/tests/auto/qml/qmllint/data/Enumerei/qmldir
new file mode 100644
index 0000000000..aa031dd7e8
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/Enumerei/qmldir
@@ -0,0 +1,3 @@
+module Enumerei
+typeinfo plugins.qmltypes
+
diff --git a/tests/auto/qml/qmllint/data/enumValid.qml b/tests/auto/qml/qmllint/data/enumValid.qml
new file mode 100644
index 0000000000..32971df070
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/enumValid.qml
@@ -0,0 +1,11 @@
+import QtQml
+import Enumerei
+
+QtObject {
+ property int a: EnumTester.S2
+ property int b: EnumTester.U2
+ property int c: EnumTesterScoped.U2
+
+ property int d: EnumTester.Scoped.S2
+ property int e: EnumTesterScoped.Scoped.S2
+}
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
index 56e31dba8f..690a3a272a 100644
--- a/tests/auto/qml/qmllint/tst_qmllint.cpp
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -1288,6 +1288,7 @@ void TestQmllint::cleanQmlCode_data()
QTest::newRow("listConversion") << QStringLiteral("listConversion.qml");
QTest::newRow("groupedAttachedLayout") << QStringLiteral("groupedAttachedLayout.qml");
QTest::newRow("constInvokable") << QStringLiteral("useConstInvokable.qml");
+ QTest::newRow("scopedAndUnscopedEnums") << QStringLiteral("enumValid.qml");
}
void TestQmllint::cleanQmlCode()
diff --git a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
index c36bc7bff8..ab89394fc8 100644
--- a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
+++ b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
@@ -91,6 +91,7 @@ qt_internal_add_resource(tst_qmltyperegistrar "resources"
PREFIX
"/"
FILES
+ brokenEnums.json
duplicatedExports.json
)
diff --git a/tests/auto/qml/qmltyperegistrar/brokenEnums.json b/tests/auto/qml/qmltyperegistrar/brokenEnums.json
new file mode 100644
index 0000000000..e54a58f9b7
--- /dev/null
+++ b/tests/auto/qml/qmltyperegistrar/brokenEnums.json
@@ -0,0 +1,57 @@
+[
+ {
+ "classes": [
+ {
+ "className": "QObject",
+ "object": true,
+ "qualifiedClassName": "QObject"
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "auto"
+ },
+ {
+ "name": "RegisterEnumClassesUnscoped",
+ "value": "true"
+ }
+ ],
+ "className": "EnumsExplicitlyUnscoped",
+ "lineNumber": 878,
+ "object": true,
+ "qualifiedClassName": "EnumsExplicitlyUnscoped",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "QObject"
+ }
+ ]
+ },
+ {
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "auto"
+ },
+ {
+ "name": "RegisterEnumClassesUnscoped",
+ "value": "horst"
+ }
+ ],
+ "className": "EnumScopingConfused",
+ "lineNumber": 885,
+ "object": true,
+ "qualifiedClassName": "EnumScopingConfused",
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "QObject"
+ }
+ ]
+ }
+ ],
+ "inputFile": "tst_qmltyperegistrar.h",
+ "outputRevision": 68
+ }
+]
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
index 980e3503a2..c2e4ef0397 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
@@ -417,6 +417,47 @@ void tst_qmltyperegistrar::duplicateExportWarnings()
r.write(output);
}
+void tst_qmltyperegistrar::enumWarnings()
+{
+ QmlTypeRegistrar r;
+ r.setModuleVersions(QTypeRevision::fromVersion(1, 1), {}, false);
+ QString moduleName = "tstmodule";
+ QString targetNamespace = "tstnamespace";
+ r.setModuleNameAndNamespace(moduleName, targetNamespace);
+
+ const auto expectWarning = [](const char *message) {
+ QTest::ignoreMessage(QtWarningMsg, message);
+ };
+
+ expectWarning("Warning: tst_qmltyperegistrar.h:: "
+ "Unrecognized value for RegisterEnumClassesUnscoped: horst");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: "
+ "Setting RegisterEnumClassesUnscoped to true has no effect.");
+
+ QTest::failOnWarning(QRegularExpression(".*"));
+
+
+ MetaTypesJsonProcessor processor(true);
+
+ QVERIFY(processor.processTypes({ ":/brokenEnums.json" }));
+ processor.postProcessTypes();
+ processor.postProcessForeignTypes();
+
+ QVector<QJsonObject> types = processor.types();
+ QVector<QJsonObject> typesforeign = processor.foreignTypes();
+ r.setTypes(types, typesforeign);
+
+ QString outputData;
+ QTextStream output(&outputData, QIODeviceBase::ReadWrite);
+
+ r.write(output);
+
+ QTemporaryFile pluginTypes;
+ QVERIFY(pluginTypes.open());
+
+ r.generatePluginTypes(pluginTypes.fileName());
+}
+
void tst_qmltyperegistrar::clonedSignal()
{
QVERIFY(qmltypesData.contains(R"(Signal {
@@ -671,4 +712,17 @@ void tst_qmltyperegistrar::constReturnType()
})"));
}
+void tst_qmltyperegistrar::enumsExplicitlyScoped()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "EnumsExplicitlyScoped"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: ["QmlTypeRegistrarTest/EnumsExplicitlyScoped 1.0"]
+ enforcesScopedEnums: true
+ exportMetaObjectRevisions: [256]
+ })"));
+}
+
QTEST_MAIN(tst_qmltyperegistrar)
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
index 15d2a92f8e..fdca40632b 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
@@ -565,6 +565,13 @@ public:
Q_INVOKABLE const QObject *getObject() { return nullptr; }
};
+class EnumsExplicitlyScoped : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
+};
+
class tst_qmltyperegistrar : public QObject
{
Q_OBJECT
@@ -619,8 +626,11 @@ private slots:
void listSignal();
void foreignNamespaceFromGadget();
+ void enumWarnings();
void constReturnType();
+ void enumsExplicitlyScoped();
+
private:
QByteArray qmltypesData;
};
diff --git a/tests/auto/qml/qqmlcomponent/lifecyclewatcher.h b/tests/auto/qml/qqmlcomponent/lifecyclewatcher.h
index 738fd86942..3d3bdfd562 100644
--- a/tests/auto/qml/qqmlcomponent/lifecyclewatcher.h
+++ b/tests/auto/qml/qqmlcomponent/lifecyclewatcher.h
@@ -15,10 +15,29 @@ class LifeCycleWatcher : public QObject, public QQmlParserStatus, public QQmlFin
QML_ELEMENT
Q_INTERFACES(QQmlParserStatus)
Q_INTERFACES(QQmlFinalizerHook)
+ Q_PROPERTY(QString text MEMBER text)
public:
- void classBegin() override {states.push_back(1); }
- void componentComplete() override {states.push_back(2);};
- void componentFinalized() override { states.push_back(3); }
+ void classBegin() override
+ {
+ states.push_back(1);
+ observedTexts.push_back(text);
+ }
+
+ void componentComplete() override
+ {
+ states.push_back(2);
+ observedTexts.push_back(text);
+ }
+
+ void componentFinalized() override
+ {
+ states.push_back(3);
+ observedTexts.push_back(text);
+ }
+
+ QString text;
QList<int> states;
+ QStringList observedTexts;
};
+
#endif
diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
index 1ccf7a6f23..59703d5c36 100644
--- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
+++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
@@ -1361,8 +1361,7 @@ void tst_qqmlcomponent::loadFromModule()
void tst_qqmlcomponent::loadFromModuleLifecycle()
{
QQmlEngine engine;
- QList<int> loadFromModuleOrder;
- QList<int> plainLoadOrder;
+ const QString text = "text"_L1;
const QList<int> expected {1, 2, 3};
{
QQmlComponent component(&engine);
@@ -1371,19 +1370,58 @@ void tst_qqmlcomponent::loadFromModuleLifecycle()
std::unique_ptr<QObject> root{ component.create() };
LifeCycleWatcher *watcher = qobject_cast<LifeCycleWatcher *>(root.get());
QVERIFY(watcher);
- loadFromModuleOrder = watcher->states;
- QCOMPARE(loadFromModuleOrder, expected);
+ QCOMPARE(watcher->states, expected);
+ QCOMPARE(watcher->observedTexts, QStringList(3));
+
+ const QString loaded = "load from module"_L1;
+ root.reset(component.createWithInitialProperties(QVariantMap{{text, loaded}}));
+ watcher = qobject_cast<LifeCycleWatcher *>(root.get());
+ QVERIFY(watcher);
+ QCOMPARE(watcher->states, expected);
+ QCOMPARE(watcher->observedTexts, QStringList({QString(), loaded, loaded}));
}
+
{
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(watcher->states, expected);
+ QCOMPARE(watcher->observedTexts, QStringList(3));
+
+ const QString loaded = "load from data"_L1;
+ root.reset(component.createWithInitialProperties(QVariantMap{{text, loaded}}));
+ watcher = qobject_cast<LifeCycleWatcher *>(root.get());
+ QVERIFY(watcher);
+ QCOMPARE(watcher->states, expected);
+ QCOMPARE(watcher->observedTexts, QStringList({QString(), loaded, loaded}));
}
- QCOMPARE(loadFromModuleOrder, plainLoadOrder);
+
+ {
+ QQmlComponent component(&engine);
+ const QString compiled = "inline"_L1;
+ component.setData("import test; LifeCycleWatcher { text: 'inline' }", {});
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ std::unique_ptr<QObject> root{ component.create() };
+ LifeCycleWatcher *watcher = qobject_cast<LifeCycleWatcher *>(root.get());
+ QVERIFY(watcher);
+ QCOMPARE(watcher->states, expected);
+ QCOMPARE(watcher->observedTexts, QStringList({QString(), compiled, compiled}));
+
+ const QString loaded = "overridden"_L1;
+ std::unique_ptr<QObject> withProperties(
+ component.createWithInitialProperties(QVariantMap{{text, loaded}}));
+ watcher = qobject_cast<LifeCycleWatcher *>(withProperties.get());
+ QVERIFY(watcher);
+ QCOMPARE(watcher->states, expected);
+ QCOMPARE(watcher->observedTexts, QStringList({QString(), loaded, loaded}));
+ }
+
+
}
struct CallVerifyingIncubtor : QQmlIncubator
diff --git a/tests/auto/qml/qqmldelegatemodel/data/proxyModelWithDelayedSourceModelInListView.qml b/tests/auto/qml/qqmldelegatemodel/data/proxyModelWithDelayedSourceModelInListView.qml
new file mode 100644
index 0000000000..b6733bd38c
--- /dev/null
+++ b/tests/auto/qml/qqmldelegatemodel/data/proxyModelWithDelayedSourceModelInListView.qml
@@ -0,0 +1,30 @@
+import QtQuick
+import Test
+
+Window {
+ id: root
+ title: listView.count
+
+ property alias listView: listView
+ property ProxySourceModel connectionModel: null
+
+ Component {
+ id: modelComponent
+ ProxySourceModel {}
+ }
+
+ ListView {
+ id: listView
+ anchors.fill: parent
+
+ delegate: Text {
+ text: model.Name
+ }
+
+ model: ProxyModel {
+ sourceModel: root.connectionModel
+ }
+ }
+
+ Component.onCompleted: root.connectionModel = modelComponent.createObject(root)
+}
diff --git a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
index e9c22ca1e6..336d0bd679 100644
--- a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
+++ b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtTest/qtest.h>
+#include <QtCore/qsortfilterproxymodel.h>
#include <QtCore/QConcatenateTablesProxyModel>
#include <QtCore/qtimer.h>
#include <QtGui/QStandardItemModel>
@@ -44,6 +45,7 @@ private slots:
void persistedItemsStayInCache();
void doNotUnrefObjectUnderConstruction();
void clearCacheDuringInsertion();
+ void proxyModelWithDelayedSourceModelInListView();
};
class BaseAbstractItemModel : public QAbstractItemModel
@@ -501,6 +503,77 @@ void tst_QQmlDelegateModel::clearCacheDuringInsertion()
QTRY_COMPARE(object->property("testModel").toInt(), 0);
}
+class ProxySourceModel : public QAbstractListModel
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ explicit ProxySourceModel(QObject *parent = nullptr)
+ : QAbstractListModel(parent)
+ {
+ for (int i = 0; i < rows; ++i) {
+ beginInsertRows(QModelIndex(), i, i);
+ endInsertRows();
+ }
+ }
+
+ ~ProxySourceModel() override = default;
+
+ int rowCount(const QModelIndex &) const override
+ {
+ return rows;
+ }
+
+ QVariant data(const QModelIndex &, int ) const override
+ {
+ return "Hello";
+ }
+
+ QHash<int, QByteArray> roleNames() const override
+ {
+ QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
+ roles[Qt::UserRole + 1] = "Name";
+
+ return roles;
+ }
+
+ static const int rows = 1;
+};
+
+class ProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(QAbstractItemModel *sourceModel READ sourceModel WRITE setSourceModel)
+
+public:
+ explicit ProxyModel(QObject *parent = nullptr)
+ : QSortFilterProxyModel(parent)
+ {
+ }
+
+ ~ProxyModel() override = default;
+};
+
+// Checks that the correct amount of delegates are created when using a proxy
+// model whose source model is set after a delay.
+void tst_QQmlDelegateModel::proxyModelWithDelayedSourceModelInListView()
+{
+ qmlRegisterTypesAndRevisions<ProxySourceModel>("Test", 1);
+ qmlRegisterTypesAndRevisions<ProxyModel>("Test", 1);
+
+ QQuickApplicationHelper helper(this, "proxyModelWithDelayedSourceModelInListView.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickWindow *window = helper.window;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ auto *listView = window->property("listView").value<QQuickListView *>();
+ QVERIFY(listView);
+ const auto delegateModel = QQuickItemViewPrivate::get(listView)->model;
+ QTRY_COMPARE(listView->count(), 1);
+}
+
QTEST_MAIN(tst_QQmlDelegateModel)
#include "tst_qqmldelegatemodel.moc"
diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
index b82a1f4174..bf68de0d2f 100644
--- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
+++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
@@ -79,6 +79,7 @@ private slots:
void lockedRootObject();
void crossReferencingSingletonsDeletion();
void bindingInstallUseAfterFree();
+ void attachedObjectOfUnregistered();
public slots:
QObject *createAQObjectForOwnershipTest ()
@@ -1708,6 +1709,42 @@ void tst_qqmlengine::bindingInstallUseAfterFree()
QVERIFY(o);
}
+class UnregisteredAttached : public QObject
+{
+ Q_OBJECT
+public:
+ UnregisteredAttached(QObject *parent = nullptr) : QObject(parent) {}
+};
+
+class Unregistered : public QObject
+{
+ Q_OBJECT
+ QML_ATTACHED(UnregisteredAttached)
+public:
+ static UnregisteredAttached *qmlAttachedProperties(QObject *obj)
+ {
+ return new UnregisteredAttached(obj);
+ }
+};
+
+void tst_qqmlengine::attachedObjectOfUnregistered()
+{
+ QObject o;
+
+ QObject *a = qmlAttachedPropertiesObject<Unregistered>(&o);
+ QVERIFY(a);
+ QVERIFY(qobject_cast<UnregisteredAttached *>(a));
+
+ QObject *b = qmlAttachedPropertiesObject<Unregistered>(&o);
+ QCOMPARE(a, b);
+
+ QObject o2;
+ QObject *c = qmlAttachedPropertiesObject<Unregistered>(&o2);
+ QVERIFY(c);
+ QVERIFY(qobject_cast<UnregisteredAttached *>(c));
+ QVERIFY(c != a);
+}
+
QTEST_MAIN(tst_qqmlengine)
#include "tst_qqmlengine.moc"
diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
index 92da003ad4..d57dfe2956 100644
--- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
+++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
@@ -40,6 +40,7 @@ private slots:
void implicitWithDependencies();
void qualifiedScriptImport();
void invalidImportUrl();
+ void sanitizeUNCPath();
};
void tst_QQmlImport::cleanup()
@@ -150,6 +151,15 @@ void tst_QQmlImport::invalidImportUrl()
":2 Cannot resolve URL for import \"file://./MyModuleName\"\n"));
}
+void tst_QQmlImport::sanitizeUNCPath()
+{
+ QString wildUNCPath = QStringLiteral("//Server2/Sh%re/foO/qmldir");
+ QQmlImportDatabase::sanitizeUNCPath(&wildUNCPath);
+
+ // It lowercases the "server" component of the path. The rest is left as-is
+ QCOMPARE(wildUNCPath, QStringLiteral("//server2/Sh%re/foO/qmldir"));
+}
+
void tst_QQmlImport::testDesignerSupported()
{
QQuickView *window = new QQuickView();
diff --git a/tests/auto/qml/qqmllanguage/data/TextItem.qml b/tests/auto/qml/qqmllanguage/data/TextItem.qml
new file mode 100644
index 0000000000..1f6f171b41
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/TextItem.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Text {
+ property bool testBool: false
+ font.family: "Ar" + "iallll"
+ onTestBoolChanged: font.pixelSize = 16;
+}
diff --git a/tests/auto/qml/qqmllanguage/data/Wrap.qml b/tests/auto/qml/qqmllanguage/data/Wrap.qml
new file mode 100644
index 0000000000..365350f16e
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/Wrap.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+TextItem {
+ font.pixelSize: height * 0.9
+ testBool: true
+}
diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp
index 12fe042c20..c65cbe329d 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.cpp
+++ b/tests/auto/qml/qqmllanguage/testtypes.cpp
@@ -4,6 +4,10 @@
#include <private/qv4qmlcontext_p.h>
+#include <QtQml/qqmlextensionplugin.h>
+
+Q_IMPORT_QML_PLUGIN(testhelperPlugin)
+
static QObject *myTypeObjectSingleton(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine);
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 7992896506..b7dff336af 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -6222,7 +6222,7 @@ class EnumTester : public QObject
public:
enum Types
{
- FIRST = 0,
+ FIRST = 42,
SECOND,
THIRD
};
@@ -6236,13 +6236,18 @@ void tst_qqmllanguage::qualifiedScopeInCustomParser()
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData("import QtQml.Models 2.12\n"
+ "import QtQml\n"
"import scoped.custom.test 1.0 as BACKEND\n"
"ListModel {\n"
+ " id: root\n"
+ " property int num: -1\n"
" ListElement { text: \"a\"; type: BACKEND.EnumTester.FIRST }\n"
+ " Component.onCompleted: { root.num = root.get(0).type }\n"
"}\n", QUrl());
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QScopedPointer<QObject> obj(component.create());
QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("num").toInt(), 42);
}
void tst_qqmllanguage::checkUncreatableNoReason()
@@ -8262,6 +8267,14 @@ void tst_qqmllanguage::overrideInnerBinding()
QCOMPARE(o->property("width").toReal(), 20.0);
QCOMPARE(o->property("innerWidth").toReal(), 20.0);
+
+ QQmlComponent c2(&e, testFileUrl("Wrap.qml"));
+ QVERIFY2(c2.isReady(), qPrintable(c2.errorString()));
+ o.reset(c2.create());
+ QVERIFY(!o.isNull());
+
+ QFont font = qvariant_cast<QFont>(o->property("font"));
+ QCOMPARE(font.family(), "Ariallll");
}
QTEST_MAIN(tst_qqmllanguage)
diff --git a/tests/auto/qml/qqmltypeloader/data/SlowImporter/A.qml b/tests/auto/qml/qqmltypeloader/data/SlowImporter/A.qml
new file mode 100644
index 0000000000..421a0918ed
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/SlowImporter/A.qml
@@ -0,0 +1,2 @@
+import Slow
+SlowStuff {}
diff --git a/tests/auto/qml/qqmltypeloader/data/SlowImporter/qmldir b/tests/auto/qml/qqmltypeloader/data/SlowImporter/qmldir
new file mode 100644
index 0000000000..bf2eff032d
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/SlowImporter/qmldir
@@ -0,0 +1,2 @@
+module SlowImporter
+A 1.0 A.qml
diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
index 717c900c00..cb4610d01b 100644
--- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
+++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
@@ -34,6 +34,7 @@ private slots:
void trimCache3();
void keepSingleton();
void keepRegistrations();
+ void importAndDestroy();
void intercept();
void redirect();
void qmlSingletonWithinModule();
@@ -48,6 +49,7 @@ private slots:
void declarativeCppAndQmlDir();
void signalHandlersAreCompatible();
void loadTypeOnShutdown();
+ void floodTypeLoaderEventQueue();
private:
void checkSingleton(const QString & dataDirectory);
@@ -418,6 +420,49 @@ public:
}
};
+void tst_QQMLTypeLoader::importAndDestroy()
+{
+#if defined Q_OS_ANDROID || defined Q_OS_IOS
+ QSKIP("Data directory is not in the host file system on Android and iOS");
+#endif
+ qmlClearTypeRegistrations();
+
+ QQmlEngine engine;
+ NetworkAccessManagerFactory factory;
+ engine.setNetworkAccessManagerFactory(&factory);
+ QQmlComponent component(&engine);
+
+ // We redirect the import through the network access manager to make it asynchronous.
+ // Otherwise the type loader will just directly call back into the main thread and we
+ // won't get a chance to do mischief before initializeEngine gets called for the "Slow"
+ // module. Note that the "Slow" module needs to be loaded from a "local" URL since plugins
+ // can only be loaded locally.
+
+ // Detour through testFileUrl to get the path right on windows ('C:' and things like that)
+ QUrl url = testFileUrl("SlowImporter");
+ url.setScheme(url.scheme() + QLatin1String("+debug"));
+
+ component.setData(QString::fromLatin1(R"(
+ import '%1'
+ A {}
+ )").arg(url.toString()).toUtf8(), QUrl());
+
+ while (!QQmlMetaType::qmlType(
+ QStringLiteral("SlowStuff"), QStringLiteral("Slow"), QTypeRevision())
+ .isValid()) {
+ // busy wait for type to be registered
+ QVERIFY2(!component.isError(), qPrintable(component.errorString()));
+ }
+
+ // Now the type loader thread is likely waiting for the main thread to process the
+ // initializeEngine callback. We destroy the engine here to trigger the situation where the main
+ // thread needs to wake the type loader thread one more time to process the isShutdown flag.
+ // If it fails to do so, the type loader thread waits indefinitely for the main thread and the
+ // engine dtor in turn waits indefinitely for the type loader thread to terminate.
+
+ // The point of this test is that it _should not_ deadlock here.
+}
+
void tst_QQMLTypeLoader::intercept()
{
#ifdef Q_OS_ANDROID
@@ -768,6 +813,22 @@ void tst_QQMLTypeLoader::loadTypeOnShutdown()
QVERIFY(dead2);
}
+void tst_QQMLTypeLoader::floodTypeLoaderEventQueue()
+{
+ QQmlEngine engine;
+
+ // Flood the typeloader with useless messages.
+ for (int i = 0; i < 1000; ++i) {
+ QQmlComponent c(&engine);
+ c.setData(QString::fromLatin1(R"(
+ import "barf:/not/actually/there%1"
+ SomeElement {}
+ )").arg(i).toUtf8(), QUrl::fromLocalFile(QString::fromLatin1("foo%1.qml").arg(i)));
+ QVERIFY(!c.isReady());
+ // Should not crash when destrying the QQmlComponent.
+ }
+}
+
QTEST_MAIN(tst_QQMLTypeLoader)
#include "tst_qqmltypeloader.moc"
diff --git a/tests/auto/qml/qv4mm/data/forInOnProxyMarksTarget.qml b/tests/auto/qml/qv4mm/data/forInOnProxyMarksTarget.qml
new file mode 100644
index 0000000000..23e3820f2c
--- /dev/null
+++ b/tests/auto/qml/qv4mm/data/forInOnProxyMarksTarget.qml
@@ -0,0 +1,23 @@
+import QtQml
+
+QtObject {
+ property bool wasInUseBeforeRevoke: false
+ property bool wasInUseAfterRevoke: false
+
+ Component.onCompleted: {
+ let handler = {};
+ let target = {prop1: 1, prop2: 2};
+
+ let proxy = Proxy.revocable(target, handler);
+ wasInUseBeforeRevoke = __inUse(target)
+ target = null;
+
+ for (var prop in proxy.proxy) {
+ prop[4] = 10;
+ proxy.revoke()
+ gc()
+ wasInUseAfterRevoke = __inUse()
+ break
+ }
+ }
+}
diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
index e5f8951825..832abfa4a0 100644
--- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp
+++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
@@ -27,6 +27,7 @@ private slots:
void accessParentOnDestruction();
void cleanInternalClasses();
void createObjectsOnDestruction();
+ void forInOnProxyMarksTarget();
};
tst_qv4mm::tst_qv4mm()
@@ -206,6 +207,33 @@ void tst_qv4mm::createObjectsOnDestruction()
QCOMPARE(obj->property("ok").toBool(), true);
}
+
+QV4::ReturnedValue method_in_use(const QV4::FunctionObject *, const QV4::Value *, const QV4::Value *argv, int argc) {
+ static QV4::Value::HeapBasePtr target = nullptr;
+
+ if (argc == 1) {
+ target = argv[0].heapObject();
+ }
+
+ Q_ASSERT(target);
+ return QV4::Encode(target->inUse());
+}
+
+void tst_qv4mm::forInOnProxyMarksTarget() {
+ QQmlEngine engine;
+ auto *v4 = engine.handle();
+ auto globalObject = v4->globalObject;
+ globalObject->defineDefaultProperty(QStringLiteral("__inUse"), method_in_use);
+
+ QQmlComponent comp(&engine, testFileUrl("forInOnProxyMarksTarget.qml"));
+ QVERIFY(comp.isReady());
+ std::unique_ptr<QObject> root {comp.create()};
+
+ QVERIFY(root);
+ QVERIFY(root->property("wasInUseBeforeRevoke").toBool());
+ QVERIFY(root->property("wasInUseAfterRevoke").toBool());
+}
+
QTEST_MAIN(tst_qv4mm)
#include "tst_qv4mm.moc"