aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp')
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp888
1 files changed, 853 insertions, 35 deletions
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
index 798aab03b1..822caea0d0 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 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$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmltyperegistrar.h"
#include <QtTest/qtest.h>
@@ -32,6 +7,10 @@
#include <QtCore/qfile.h>
#include <QtQml/QQmlEngine>
#include <QtQml/QQmlComponent>
+#include <QtQml/qqmlprivate.h>
+
+#include "hppheader.hpp"
+#include "UnregisteredTypes/uncreatable.h"
void tst_qmltyperegistrar::initTestCase()
{
@@ -60,6 +39,30 @@ void tst_qmltyperegistrar::qmltypesHasReadAndWrite()
QVERIFY(qmltypesData.contains(R"(write: "setEieiei")"));
}
+void tst_qmltyperegistrar::qmltypesHasNotify()
+{
+ QVERIFY(qmltypesData.contains(R"(notify: "eieieiChanged")"));
+}
+
+void tst_qmltyperegistrar::qmltypesHasPropertyIndex()
+{
+ qsizetype start = qmltypesData.indexOf("notify: \"eieieiChanged\"");
+ qsizetype end = qmltypesData.indexOf("}", start);
+ // [start, end) - range in which index information of eieiei should exist
+ QVERIFY(qmltypesData.indexOf("index: 0", start) < end); // belongs to eieiei
+
+ start = qmltypesData.indexOf("read: \"eieiei2\"");
+ end = qmltypesData.indexOf("}", start);
+ QVERIFY(qmltypesData.indexOf("index: 1", start) < end); // belongs to eieiei2
+
+ HppClass eieieiClass;
+ const QMetaObject *mo = eieieiClass.metaObject();
+ QVERIFY(mo);
+ // NB: add 0 and 1 as relative indices "parsed" from qmltypesData
+ QCOMPARE(mo->indexOfProperty("eieiei"), mo->propertyOffset() + 0);
+ QCOMPARE(mo->indexOfProperty("eieiei2"), mo->propertyOffset() + 1);
+}
+
void tst_qmltyperegistrar::qmltypesHasFileNames()
{
QVERIFY(qmltypesData.contains("file: \"hppheader.hpp\""));
@@ -79,8 +82,8 @@ void tst_qmltyperegistrar::superAndForeignTypes()
QVERIFY(qmltypesData.contains("values: [\"Pixel\", \"Centimeter\", \"Inch\", \"Point\"]"));
QVERIFY(qmltypesData.contains("name: \"SizeGadget\""));
QVERIFY(qmltypesData.contains("prototype: \"SizeEnums\""));
- QVERIFY(qmltypesData.contains("Property { name: \"height\"; type: \"int\"; read: \"height\"; write: \"setHeight\" }"));
- QVERIFY(qmltypesData.contains("Property { name: \"width\"; type: \"int\"; read: \"width\"; write: \"setWidth\" }"));
+ QVERIFY(qmltypesData.contains("Property { name: \"height\"; type: \"int\"; read: \"height\"; write: \"setHeight\"; index: 0; isFinal: true }"));
+ QVERIFY(qmltypesData.contains("Property { name: \"width\"; type: \"int\"; read: \"width\"; write: \"setWidth\"; index: 0; isFinal: true }"));
QVERIFY(qmltypesData.contains("Method { name: \"sizeToString\"; type: \"QString\" }"));
QCOMPARE(qmltypesData.count("extension: \"SizeValueType\""), 1);
}
@@ -93,8 +96,7 @@ void tst_qmltyperegistrar::accessSemantics()
void tst_qmltyperegistrar::isBindable()
{
- // TODO: readonly?
- QVERIFY(qmltypesData.contains(R"(Property { name: "someProperty"; bindable: "bindableSomeProperty"; type: "int"; isReadonly: true)"));
+ QVERIFY(qmltypesData.contains(R"(Property { name: "someProperty"; type: "int"; bindable: "bindableSomeProperty"; index: 0 })"));
}
void tst_qmltyperegistrar::restrictToImportVersion()
@@ -114,8 +116,8 @@ void tst_qmltyperegistrar::pastMajorVersions()
void tst_qmltyperegistrar::implementsInterfaces()
{
- QVERIFY(qmltypesData.contains("interfaces: [\"Interface\"]"));
- QVERIFY(qmltypesData.contains("interfaces: [\"Interface\", \"Interface2\"]"));
+ QVERIFY(qmltypesData.contains("interfaces: [\"Interface1\"]"));
+ QVERIFY(qmltypesData.contains("interfaces: [\"Interface1\", \"Interface2\"]"));
}
void tst_qmltyperegistrar::namespacedElement()
@@ -143,9 +145,9 @@ void tst_qmltyperegistrar::metaTypesRegistered()
auto verifyMetaType = [](const char *name, const char *className) {
const auto foundMetaType = QMetaType::fromName(name);
- QVERIFY(foundMetaType.isValid());
+ QVERIFY2(foundMetaType.isValid(), name);
QCOMPARE(foundMetaType.name(), name);
- QVERIFY(foundMetaType.metaObject());
+ QVERIFY2(foundMetaType.metaObject(), name);
QCOMPARE(foundMetaType.metaObject()->className(), className);
};
@@ -153,6 +155,7 @@ void tst_qmltyperegistrar::metaTypesRegistered()
verifyMetaType("Ooo*", "Ooo");
verifyMetaType("Bbb*", "Bbb");
verifyMetaType("Ccc*", "Ccc");
+ verifyMetaType("SelfExtensionHack", "SelfExtensionHack");
}
void tst_qmltyperegistrar::multiExtensions()
@@ -205,4 +208,819 @@ void tst_qmltyperegistrar::requiredProperty()
QCOMPARE(qmltypesData.count("isRequired: true"), 1);
}
+void tst_qmltyperegistrar::hiddenAccessor()
+{
+ const auto start = qmltypesData.indexOf("name: \"hiddenRead\""); // rely on name being 1st field
+ QVERIFY(start != -1);
+ const auto end = qmltypesData.indexOf("}", start); // enclosing '}' of hiddenRead property
+ QVERIFY(end != -1);
+ QVERIFY(start < end);
+
+ const auto hiddenReadData = QByteArrayView(qmltypesData).sliced(start, end - start);
+ // QVERIFY(hiddenReadData.contains("name: \"hiddenRead\"")); // tested above by start != -1
+ QVERIFY(hiddenReadData.contains("type: \"QString\""));
+ QVERIFY(hiddenReadData.contains("read: \"hiddenRead\""));
+ QVERIFY(hiddenReadData.contains("privateClass: \"HiddenAccessorsPrivate\""));
+ QVERIFY(hiddenReadData.contains("isReadonly: true"));
+}
+
+void tst_qmltyperegistrar::finalProperty()
+{
+ QCOMPARE(qmltypesData.count("name: \"FinalProperty\""), 1);
+ QCOMPARE(qmltypesData.count(
+ "Property { name: \"fff\"; type: \"int\"; index: 0; isFinal: true }"),
+ 1);
+}
+
+void tst_qmltyperegistrar::parentProperty()
+{
+ QCOMPARE(qmltypesData.count("parentProperty: \"ppp\""), 1);
+}
+
+void tst_qmltyperegistrar::namespacesAndValueTypes()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+ c.setData("import QmlTypeRegistrarTest\nLocal {}", QUrl());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer o(c.create());
+ QVERIFY(!o.isNull());
+
+ auto check = [&](QMetaType m1, QMetaType m2) {
+ QVERIFY(m1.isValid());
+ QVERIFY(m2.isValid());
+
+ // Does not actually help if we have two types with equal IDs. It only compares the IDs.
+ QVERIFY(m1 == m2);
+ QCOMPARE(m1.id(), m2.id());
+
+ // If we had a bogus namespace value type, it wouldn't be able to create the type.
+ void *v1 = m1.create();
+ QVERIFY(v1 != nullptr);
+ m1.destroy(v1);
+
+ void *v2 = m2.create();
+ QVERIFY(v2 != nullptr);
+ m2.destroy(v2);
+
+ QMetaType m3(m1.id());
+ QVERIFY(m3.isValid());
+ void *v3 = m3.create();
+ QVERIFY(v3 != nullptr);
+ m3.destroy(v3);
+ };
+
+ check(QMetaType::fromName("ValueTypeWithEnum1"), QMetaType::fromType<ValueTypeWithEnum1>());
+ check(QMetaType::fromName("ValueTypeWithEnum2"), QMetaType::fromType<ValueTypeWithEnum2>());
+}
+
+void tst_qmltyperegistrar::namespaceExtendedNamespace()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+ c.setData("import QtQml\n"
+ "import QmlTypeRegistrarTest\n"
+ "QtObject {\n"
+ " property int b: ForeignNamespace.B\n"
+ " property int f: ForeignNamespace.F\n"
+ "}", QUrl());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("b").toInt(), int(ExtensionValueType::B));
+ QCOMPARE(o->property("f").toInt(), int(BaseNamespace::F));
+}
+
+void tst_qmltyperegistrar::deferredNames()
+{
+ QVERIFY(qmltypesData.contains("deferredNames: [\"\"]"));
+ QVERIFY(qmltypesData.contains("deferredNames: [\"A\", \"B\", \"C\"]"));
+}
+
+void tst_qmltyperegistrar::immediateNames()
+{
+ QVERIFY(qmltypesData.contains("immediateNames: [\"\"]"));
+ QVERIFY(qmltypesData.contains("immediateNames: [\"A\", \"B\", \"C\"]"));
+}
+
+void tst_qmltyperegistrar::derivedFromForeignPrivate()
+{
+ QVERIFY(qmltypesData.contains("file: \"private/foreign_p.h\""));
+}
+
+void tst_qmltyperegistrar::methodReturnType()
+{
+ QVERIFY(qmltypesData.contains("createAThing"));
+ QVERIFY(!qmltypesData.contains("QQmlComponent*"));
+ QVERIFY(qmltypesData.contains("type: \"QQmlComponent\""));
+}
+
+void tst_qmltyperegistrar::addRemoveVersion_data()
+{
+ QTest::addColumn<QTypeRevision>("importVersion");
+ for (int i = 0; i < 20; ++i)
+ QTest::addRow("v1.%d.qml", i) << QTypeRevision::fromVersion(1, i);
+}
+
+void tst_qmltyperegistrar::addRemoveVersion()
+{
+ QFETCH(QTypeRevision, importVersion);
+
+ const bool creatable
+ = importVersion > QTypeRevision::fromVersion(1, 2)
+ && importVersion < QTypeRevision::fromVersion(1, 18);
+ const bool thingAccessible = importVersion > QTypeRevision::fromVersion(1, 3);
+
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+ c.setData(QStringLiteral("import QmlTypeRegistrarTest %1.%2\n"
+ "Versioned {\n"
+ " property int thing: revisioned\n"
+ "}")
+ .arg(importVersion.majorVersion()).arg(importVersion.minorVersion()).toUtf8(),
+ QUrl(QTest::currentDataTag()));
+ if (!creatable) {
+ QVERIFY(c.isError());
+ return;
+ }
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ if (!thingAccessible) {
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ qPrintable(QStringLiteral("%1:3: ReferenceError: revisioned is not defined")
+ .arg(QTest::currentDataTag())));
+ }
+ QScopedPointer o(c.create());
+ QVERIFY(!o.isNull());
+ QCOMPARE(o->property("thing").toInt(), thingAccessible ? 24 : 0);
+}
+
+void tst_qmltyperegistrar::addInMinorVersion()
+{
+ QVERIFY(qmltypesData.contains("exports: [\"QmlTypeRegistrarTest/MinorVersioned 1.5\"]"));
+ QVERIFY(qmltypesData.contains("exports: [\"QmlTypeRegistrarTest/MinorVersioned 1.2\"]"));
+}
+
+#ifdef QT_QUICK_LIB
+void tst_qmltyperegistrar::foreignRevisionedProperty()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+ c.setData("import QmlTypeRegistrarTest\n"
+ "ForeignRevisionedProperty {\n"
+ " activeFocusOnTab: true\n"
+ "}",
+ QUrl());
+
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer o(c.create());
+ QVERIFY(!o.isNull());
+}
+#endif
+
+void tst_qmltyperegistrar::typeInModuleMajorVersionZero()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+ c.setData(QStringLiteral("import VersionZero\n"
+ "TypeInModuleMajorVersionZero {}\n").toUtf8(),
+ QUrl(QTest::currentDataTag()));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+}
+
+void tst_qmltyperegistrar::resettableProperty()
+{
+ QVERIFY(qmltypesData.contains(R"(reset: "resetFoo")"));
+}
+
+void tst_qmltyperegistrar::duplicateExportWarnings()
+{
+ QmlTypeRegistrar r;
+ QString moduleName = "tstmodule";
+ QString targetNamespace = "tstnamespace";
+ r.setModuleNameAndNamespace(moduleName, targetNamespace);
+
+ MetaTypesJsonProcessor processor(true);
+ QVERIFY(processor.processTypes({ ":/duplicatedExports.json" }));
+ processor.postProcessTypes();
+ QVector<MetaType> types = processor.types();
+ QVector<MetaType> typesforeign = processor.foreignTypes();
+ r.setTypes(types, typesforeign);
+
+ const auto expectWarning = [](const char *message) {
+ QTest::ignoreMessage(QtWarningMsg, message);
+ };
+ expectWarning("Warning: duplicatedExports.h:: ExportedQmlElement is registered multiple times "
+ "by the following C++ classes: ExportedQmlElement, ExportedQmlElement2 "
+ "(added in 1.2), ExportedQmlElementDifferentVersion (added in 1.0) "
+ "(removed in 1.7)");
+ expectWarning("Warning: duplicatedExports.h:: SameNameSameExport is registered multiple times "
+ "by the following C++ classes: SameNameSameExport, SameNameSameExport2 "
+ "(added in 1.2), SameNameSameExportDifferentVersion (added in 1.0)");
+
+ QString outputData;
+ QTextStream output(&outputData, QIODeviceBase::ReadWrite);
+ r.write(output, "tst_qmltyperegistrar_qmltyperegistrations.cpp");
+}
+
+void tst_qmltyperegistrar::consistencyWarnings()
+{
+ QmlTypeRegistrar r;
+ r.setModuleVersions(QTypeRevision::fromVersion(1, 1), {}, false);
+ QString moduleName = "tstmodule";
+ QString targetNamespace = "tstnamespace";
+ r.setModuleNameAndNamespace(moduleName, targetNamespace);
+
+ MetaTypesJsonProcessor processor(true);
+
+ QVERIFY(processor.processTypes({ ":/missingTypes.json" }));
+ processor.postProcessTypes();
+
+ const auto expectWarning = [](const char *message) {
+ QTest::ignoreMessage(QtWarningMsg, message);
+ };
+
+ expectWarning("Warning: tst_qmltyperegistrar.h:: "
+ "NotQObject is used as base type but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotQObject is used as base type "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: Invisible is declared as foreign type, "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotQByteArray is used as sequence value type "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotAPropertyType is used as property type "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotAnArgumentType is used as argument type "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotAReturnType is used as return type "
+ "but cannot be found.");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: NotAnUnderlyingType is used as enum type "
+ "but cannot be found.");
+
+ processor.postProcessForeignTypes();
+
+ QVector<MetaType> types = processor.types();
+ QVector<MetaType> typesforeign = processor.foreignTypes();
+ r.setTypes(types, typesforeign);
+
+ QString outputData;
+ QTextStream output(&outputData, QIODeviceBase::ReadWrite);
+
+ expectWarning("Warning: tst_qmltyperegistrar.h:: AddedInLateVersion is trying to register "
+ "property revisioned with future version 1.4 when module version is only 1.1");
+ expectWarning("Warning: tst_qmltyperegistrar.h:: ExcessiveVersion is trying to register "
+ "property palette with future version 6.0 when module version is only 1.1");
+
+ r.write(output, "tst_qmltyperegistrar_qmltyperegistrations.cpp");
+
+ QTemporaryFile pluginTypes;
+ QVERIFY(pluginTypes.open());
+
+ expectWarning("Warning: tst_qmltyperegistrar.h:: Refusing to generate non-lowercase name "
+ "Invisible for unknown foreign type");
+
+ r.generatePluginTypes(pluginTypes.fileName());
+}
+
+void tst_qmltyperegistrar::clonedSignal()
+{
+ QVERIFY(qmltypesData.contains(R"(Signal {
+ name: "clonedSignal"
+ Parameter { name: "i"; type: "int" }
+ })"));
+
+ QVERIFY(qmltypesData.contains(R"(Signal { name: "clonedSignal"; isCloned: true })"));
+}
+
+void tst_qmltyperegistrar::hasIsConstantInParameters()
+{
+ QVERIFY(qmltypesData.contains(R"( Signal {
+ name: "mySignal"
+ Parameter { name: "myObject"; type: "QObject"; isPointer: true }
+ Parameter { name: "myConstObject"; type: "QObject"; isPointer: true; isConstant: true }
+ Parameter { name: "myConstObject2"; type: "QObject"; isPointer: true; isConstant: true }
+ Parameter { name: "myObject2"; type: "QObject"; isPointer: true }
+ Parameter { name: "myConstObject3"; type: "QObject"; isPointer: true; isConstant: true }
+ }
+)"));
+
+ QVERIFY(qmltypesData.contains(R"(Signal {
+ name: "myVolatileSignal"
+ Parameter { name: "a"; type: "volatile QObject"; isPointer: true; isConstant: true }
+ Parameter { name: "b"; type: "volatile QObject"; isPointer: true; isConstant: true }
+ Parameter { name: "nonConst"; type: "volatile QObject"; isPointer: true }
+ }
+)"));
+}
+
+void tst_qmltyperegistrar::uncreatable()
+{
+ using namespace QQmlPrivate;
+
+ // "normal" constructible types
+ QVERIFY(QmlMetaType<Creatable>::hasAcceptableCtors());
+ QVERIFY(QmlMetaType<Creatable2>::hasAcceptableCtors());
+
+ // good singletons
+ QCOMPARE((singletonConstructionMode<SingletonCreatable, SingletonCreatable>()),
+ SingletonConstructionMode::Factory);
+ QCOMPARE((singletonConstructionMode<SingletonCreatable2, SingletonCreatable2>()),
+ SingletonConstructionMode::Constructor);
+ QCOMPARE((singletonConstructionMode<SingletonCreatable2, SingletonCreatable2>()),
+ SingletonConstructionMode::Constructor);
+ QCOMPARE((singletonConstructionMode<SingletonForeign, SingletonLocalCreatable>()),
+ SingletonConstructionMode::FactoryWrapper);
+
+ // bad singletons
+ QCOMPARE((singletonConstructionMode<SingletonIncreatable, SingletonIncreatable>()),
+ SingletonConstructionMode::None);
+ QCOMPARE((singletonConstructionMode<SingletonIncreatable2, SingletonIncreatable2>()),
+ SingletonConstructionMode::None);
+ QCOMPARE((singletonConstructionMode<SingletonIncreatable3, SingletonIncreatable3>()),
+ SingletonConstructionMode::None);
+ QCOMPARE((singletonConstructionMode<SingletonIncreatable4, SingletonIncreatable4>()),
+ SingletonConstructionMode::None);
+ QCOMPARE((singletonConstructionMode<SingletonIncreatableExtended,
+ SingletonIncreatableExtended>()),
+ SingletonConstructionMode::None);
+ QCOMPARE((singletonConstructionMode<SingletonForeign, SingletonLocalUncreatable1>()),
+ SingletonConstructionMode::None);
+ QCOMPARE((singletonConstructionMode<SingletonForeign, SingletonLocalUncreatable2>()),
+ SingletonConstructionMode::None);
+#if QT_DEPRECATED_SINCE(6, 4)
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "Singleton SingletonIncreatable needs to be a concrete class with either a "
+ "default constructor or, when adding a default constructor is infeasible, "
+ "a public static create(QQmlEngine *, QJSEngine *) method.");
+ qmlRegisterTypesAndRevisions<SingletonIncreatable>("A", 1);
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "Singleton SingletonIncreatable2 needs to be a concrete class with either a "
+ "default constructor or, when adding a default constructor is infeasible, "
+ "a public static create(QQmlEngine *, QJSEngine *) method.");
+ qmlRegisterTypesAndRevisions<SingletonIncreatable2>("A", 1);
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "Singleton SingletonIncreatable3 needs to be a concrete class with either a "
+ "default constructor or, when adding a default constructor is infeasible, "
+ "a public static create(QQmlEngine *, QJSEngine *) method.");
+ qmlRegisterTypesAndRevisions<SingletonIncreatable3>("A", 1);
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "Singleton SingletonIncreatable4 needs to be a concrete class with either a "
+ "default constructor or, when adding a default constructor is infeasible, "
+ "a public static create(QQmlEngine *, QJSEngine *) method.");
+ qmlRegisterTypesAndRevisions<SingletonIncreatable4>("A", 1);
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "Singleton SingletonIncreatableExtended needs to be a concrete class with either a "
+ "default constructor or, when adding a default constructor is infeasible, "
+ "a public static create(QQmlEngine *, QJSEngine *) method.");
+ qmlRegisterTypesAndRevisions<SingletonIncreatableExtended>("A", 1);
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "Singleton SingletonForeign needs to be a concrete class with either a "
+ "default constructor or, when adding a default constructor is infeasible, "
+ "a public static create(QQmlEngine *, QJSEngine *) method.");
+ qmlRegisterTypesAndRevisions<SingletonLocalUncreatable1>("A", 1);
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "Singleton SingletonForeign needs to be a concrete class with either a "
+ "default constructor or, when adding a default constructor is infeasible, "
+ "a public static create(QQmlEngine *, QJSEngine *) method.");
+ qmlRegisterTypesAndRevisions<SingletonLocalUncreatable2>("A", 1);
+#endif
+
+ // QML_UNCREATABLE types
+ QVERIFY(!QmlMetaType<BadUncreatable>::hasAcceptableCtors());
+ QVERIFY(!QmlMetaType<BadUncreatableExtended>::hasAcceptableCtors());
+ QVERIFY(!QmlMetaType<GoodUncreatable>::hasAcceptableCtors());
+ QVERIFY(!QmlMetaType<UncreatableNeedsForeign>::hasAcceptableCtors());
+ QVERIFY(!QmlMetaType<GoodUncreatableExtended>::hasAcceptableCtors());
+#if QT_DEPRECATED_SINCE(6, 4)
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "BadUncreatable is neither a default constructible QObject, nor a default- "
+ "and copy-constructible Q_GADGET, nor marked as uncreatable.\n"
+ "You should not use it as a QML type.");
+ qmlRegisterTypesAndRevisions<BadUncreatable>("A", 1);
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "BadUncreatableExtended is neither a default constructible QObject, nor a default- "
+ "and copy-constructible Q_GADGET, nor marked as uncreatable.\n"
+ "You should not use it as a QML type.");
+ qmlRegisterTypesAndRevisions<BadUncreatableExtended>("A", 1);
+#endif
+
+ const auto oldHandler = qInstallMessageHandler(
+ [](QtMsgType, const QMessageLogContext &, const QString &message) {
+ QFAIL(qPrintable(message));
+ });
+ const auto guard = qScopeGuard([oldHandler](){qInstallMessageHandler(oldHandler); });
+
+ // These should not print any messages.
+
+ qmlRegisterTypesAndRevisions<Creatable>("A", 1);
+ qmlRegisterTypesAndRevisions<Creatable2>("A", 1);
+
+ qmlRegisterTypesAndRevisions<SingletonCreatable>("A", 1);
+ qmlRegisterTypesAndRevisions<SingletonCreatable2>("A", 1);
+ qmlRegisterTypesAndRevisions<SingletonCreatable3>("A", 1);
+
+ qmlRegisterTypesAndRevisions<GoodUncreatable>("A", 1);
+ qmlRegisterTypesAndRevisions<GoodUncreatable2>("A", 1);
+ qmlRegisterTypesAndRevisions<GoodUncreatableExtended>("A", 1);
+}
+
+void tst_qmltyperegistrar::singletonVersions()
+{
+ QQmlEngine engine;
+ qmlRegisterTypesAndRevisions<SingletonVesion0>("A", 0);
+ qmlRegisterTypesAndRevisions<SingletonVesion1>("B", 1);
+
+ QQmlComponent c(&engine);
+ c.setData("import QtQuick\n"
+ "import A\n"
+ "import B\n"
+ "QtObject {\n"
+ " property QtObject v0: SingletonVesion0\n"
+ " property QtObject v1: SingletonVesion1\n"
+ "}", QUrl());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY2(!obj->property("v0").isNull(), "Singleton version 0 is not registered");
+ QVERIFY2(!obj->property("v1").isNull(), "Singleton version 1 is not registered");
+}
+
+void tst_qmltyperegistrar::baseVersionInQmltypes()
+{
+ // Since it has no QML_ADDED_IN_VERSION, WithMethod was added in .0 of the current version.
+ // The current version is 1.1, so it's 1.0.
+ QVERIFY(qmltypesData.contains("exports: [\"QmlTypeRegistrarTest/WithMethod 1.0\"]"));
+}
+
+void tst_qmltyperegistrar::unconstructibleValueType()
+{
+ QVERIFY(qmltypesData.contains(
+ R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "Unconstructible"
+ accessSemantics: "value"
+ exports: ["QmlTypeRegistrarTest/unconstructible 1.0"]
+ isCreatable: false
+ exportMetaObjectRevisions: [256]
+ })"));
+}
+
+void tst_qmltyperegistrar::constructibleValueType()
+{
+ QVERIFY(qmltypesData.contains(
+ R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "Constructible"
+ accessSemantics: "value"
+ exports: ["QmlTypeRegistrarTest/constructible 1.0"]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ Method {
+ name: "Constructible"
+ isConstructor: true
+ Parameter { name: "i"; type: "int" }
+ }
+ Method { name: "Constructible"; isCloned: true; isConstructor: true }
+ })"));
+}
+
+void tst_qmltyperegistrar::structuredValueType()
+{
+ QVERIFY(qmltypesData.contains(
+ R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "Structured"
+ accessSemantics: "value"
+ exports: ["QmlTypeRegistrarTest/structured 1.0"]
+ isCreatable: true
+ isStructured: true
+ exportMetaObjectRevisions: [256]
+ Property { name: "i"; type: "int"; index: 0; isFinal: true }
+ })"));
+}
+
+void tst_qmltyperegistrar::anonymousAndUncreatable()
+{
+ QVERIFY(qmltypesData.contains(
+ R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "AnonymousAndUncreatable"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ })"));
+}
+
+void tst_qmltyperegistrar::omitInvisible()
+{
+ // If it cannot resolve the type a QML_FOREIGN refers to, it should not generate anything.
+ QVERIFY(qmltypesData.contains(
+ R"(Component { file: "tst_qmltyperegistrar.h"; name: "Invisible"; accessSemantics: "none" })"));
+}
+
+void tst_qmltyperegistrar::typedEnum()
+{
+ QVERIFY(qmltypesData.contains(
+ R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "TypedEnum"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: ["QmlTypeRegistrarTest/TypedEnum 1.0"]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ Enum {
+ name: "UChar"
+ type: "uchar"
+ values: ["V0"]
+ }
+ Enum {
+ name: "Int8_T"
+ type: "int8_t"
+ values: ["V1"]
+ }
+ Enum {
+ name: "UInt8_T"
+ type: "uint8_t"
+ values: ["V2"]
+ }
+ Enum {
+ name: "Int16_T"
+ type: "int16_t"
+ values: ["V3"]
+ }
+ Enum {
+ name: "UInt16_T"
+ type: "uint16_t"
+ values: ["V4"]
+ }
+ Enum {
+ name: "Int32_T"
+ type: "int32_t"
+ values: ["V5"]
+ }
+ Enum {
+ name: "UInt32_T"
+ type: "uint32_t"
+ values: ["V6"]
+ }
+ Enum {
+ name: "S"
+ type: "qint16"
+ values: ["A", "B", "C"]
+ }
+ Enum {
+ name: "T"
+ type: "quint16"
+ values: ["D", "E", "F"]
+ }
+ Enum {
+ name: "U"
+ type: "qint8"
+ values: ["G", "H", "I"]
+ }
+ Enum {
+ name: "V"
+ type: "quint8"
+ values: ["J", "K", "L"]
+ }
+ })"));
+}
+
+void tst_qmltyperegistrar::listSignal()
+{
+ QVERIFY(qmltypesData.contains(
+ R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "ListSignal"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ Signal {
+ name: "objectListHappened"
+ Parameter { type: "QList<QObject*>" }
+ }
+ })"));
+}
+
+void tst_qmltyperegistrar::withNamespace()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "Bar"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ Property {
+ name: "outerBarProp"
+ type: "int"
+ read: "bar"
+ index: 0
+ isReadonly: true
+ isConstant: true
+ }
+ })"));
+
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "Testing::Bar"
+ accessSemantics: "reference"
+ prototype: "Testing::Foo"
+ exports: ["QmlTypeRegistrarTest/Bar 1.0"]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ Property { name: "barProp"; type: "int"; read: "bar"; index: 0; isReadonly: true; isConstant: true }
+ })"));
+
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "Testing::Foo"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ Property { name: "fooProp"; type: "int"; read: "foo"; index: 0; isReadonly: true; isConstant: true }
+ })"));
+
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "Testing::Inner::Baz"
+ accessSemantics: "reference"
+ prototype: "Testing::Bar"
+ extension: "Bar"
+ exports: ["QmlTypeRegistrarTest/Baz 1.0"]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ attachedType: "Testing::Foo"
+ })"));
+}
+
+void tst_qmltyperegistrar::sequenceRegistration()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "std::vector<QByteArray>"
+ accessSemantics: "sequence"
+ valueType: "QByteArray"
+ })"));
+}
+
+void tst_qmltyperegistrar::valueTypeSelfReference()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "QPersistentModelIndex"
+ accessSemantics: "value"
+ extension: "QPersistentModelIndexValueType"
+ })"));
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "QPersistentModelIndexValueType"
+ accessSemantics: "value"
+ Property { name: "row"; type: "int"; read: "row"; index: 0; isReadonly: true; isFinal: true }
+ })"));
+}
+
+void tst_qmltyperegistrar::foreignNamespaceFromGadget()
+{
+ QQmlEngine engine;
+ {
+ QQmlComponent c(&engine);
+ c.setData(QStringLiteral(R"(
+ import QtQml
+ import QmlTypeRegistrarTest
+ QtObject {
+ objectName: 'b' + NetworkManager.B
+ }
+ )").toUtf8(), QUrl("foreignNamespaceFromGadget.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QCOMPARE(o->objectName(), QStringLiteral("b1"));
+ }
+
+ {
+ QQmlComponent c(&engine);
+ c.setData(QStringLiteral(R"(
+ import QtQml
+ import QmlTypeRegistrarTest
+ QtObject {
+ objectName: 'b' + NotNamespaceForeign.B
+ }
+ )").toUtf8(), QUrl("foreignNamespaceFromGadget2.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QCOMPARE(o->objectName(), QStringLiteral("b1"));
+ }
+}
+
+void tst_qmltyperegistrar::nameExplosion_data()
+{
+ QTest::addColumn<QByteArray>("qml");
+ QTest::addRow("Name1") << QByteArray("import QmlTypeRegistrarTest\nName1{}");
+ QTest::addRow("Name2") << QByteArray("import QmlTypeRegistrarTest\nName2{}");
+ QTest::addRow("NameExplosion") << QByteArray("import QmlTypeRegistrarTest\nNameExplosion{}");
+}
+
+void tst_qmltyperegistrar::nameExplosion()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "NameExplosion"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: [
+ "QmlTypeRegistrarTest/Name1 1.0",
+ "QmlTypeRegistrarTest/Name2 1.0",
+ "QmlTypeRegistrarTest/NameExplosion 1.0"
+ ]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ })"));
+
+ QFETCH(QByteArray, qml);
+
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+
+ c.setData(qml, QUrl());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+}
+
+void tst_qmltyperegistrar::javaScriptExtension()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "JavaScriptExtension"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ extension: "SymbolPrototype"
+ extensionIsJavaScript: true
+ exports: ["QmlTypeRegistrarTest/JavaScriptExtension 1.0"]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ })"));
+}
+
+void tst_qmltyperegistrar::relatedAddedInVersion()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "AddedIn1_0"
+ accessSemantics: "reference"
+ prototype: "AddedIn1_5"
+ exports: [
+ "QmlTypeRegistrarTest/AddedIn1_0 1.0",
+ "QmlTypeRegistrarTest/AddedIn1_0 1.5"
+ ]
+ isCreatable: true
+ exportMetaObjectRevisions: [256, 261]
+ })"));
+}
+
+void tst_qmltyperegistrar::longNumberTypes()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "LongNumberTypes"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: ["QmlTypeRegistrarTest/LongNumberTypes 1.0"]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ Property { name: "a"; type: "qint64"; index: 0 }
+ Property { name: "b"; type: "int64_t"; index: 1 }
+ Property { name: "c"; type: "quint64"; index: 2 }
+ Property { name: "d"; type: "uint64_t"; index: 3 }
+ })"));
+}
+
+void tst_qmltyperegistrar::enumList() {
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "QList<NetworkManager::NM>"
+ accessSemantics: "sequence"
+ valueType: "NetworkManager::NM"
+ })"));
+}
+
+void tst_qmltyperegistrar::constReturnType()
+{
+ QVERIFY(qmltypesData.contains(R"(Component {
+ file: "tst_qmltyperegistrar.h"
+ name: "ConstInvokable"
+ accessSemantics: "reference"
+ prototype: "QObject"
+ exports: ["QmlTypeRegistrarTest/ConstInvokable 1.0"]
+ isCreatable: true
+ exportMetaObjectRevisions: [256]
+ Method { name: "getObject"; type: "QObject"; isPointer: true; isConstant: true }
+ })"));
+}
+
QTEST_MAIN(tst_qmltyperegistrar)