diff options
Diffstat (limited to 'tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp')
-rw-r--r-- | tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp | 888 |
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) |