/**************************************************************************** ** ** Copyright (C) 2016 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$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include "../../shared/util.h" class tst_qqmlmetatype : public QQmlDataTest { Q_OBJECT public: tst_qqmlmetatype() {} private slots: void initTestCase(); void qmlParserStatusCast(); void qmlPropertyValueSourceCast(); void qmlPropertyValueInterceptorCast(); void qmlType(); void invalidQmlTypeName(); void prettyTypeName(); void registrationType(); void compositeType(); void externalEnums(); void isList(); void defaultObject(); void unregisterCustomType(); void unregisterCustomSingletonType(); void normalizeUrls(); void unregisterAttachedProperties(); void revisionedGroupedProperties(); void enumsInRecursiveImport_data(); void enumsInRecursiveImport(); }; class TestType : public QObject { Q_OBJECT Q_PROPERTY(int foo READ foo) Q_CLASSINFO("DefaultProperty", "foo") public: int foo() { return 0; } }; QML_DECLARE_TYPE(TestType); class TestType2 : public QObject { Q_OBJECT }; class TestType3 : public QObject { Q_OBJECT }; class ExternalEnums : public QObject { Q_OBJECT Q_ENUMS(QStandardPaths::StandardLocation QStandardPaths::LocateOptions) public: ExternalEnums(QObject *parent = nullptr) : QObject(parent) {} static QObject *create(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(scriptEngine); return new ExternalEnums(engine); } }; QML_DECLARE_TYPE(ExternalEnums); QObject *testTypeProvider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine); Q_UNUSED(scriptEngine); return new TestType(); } class ParserStatusTestType : public QObject, public QQmlParserStatus { Q_OBJECT void classBegin(){} void componentComplete(){} Q_CLASSINFO("DefaultProperty", "foo") // Missing default property Q_INTERFACES(QQmlParserStatus) }; QML_DECLARE_TYPE(ParserStatusTestType); class ValueSourceTestType : public QObject, public QQmlPropertyValueSource { Q_OBJECT Q_INTERFACES(QQmlPropertyValueSource) public: virtual void setTarget(const QQmlProperty &) {} }; QML_DECLARE_TYPE(ValueSourceTestType); class ValueInterceptorTestType : public QObject, public QQmlPropertyValueInterceptor { Q_OBJECT Q_INTERFACES(QQmlPropertyValueInterceptor) public: virtual void setTarget(const QQmlProperty &) {} virtual void write(const QVariant &) {} }; QML_DECLARE_TYPE(ValueInterceptorTestType); void tst_qqmlmetatype::initTestCase() { QQmlDataTest::initTestCase(); qmlRegisterType("Test", 1, 0, "TestType"); qmlRegisterSingletonType("Test", 1, 0, "TestTypeSingleton", testTypeProvider); qmlRegisterType("Test", 1, 0, "ParserStatusTestType"); qmlRegisterType("Test", 1, 0, "ValueSourceTestType"); qmlRegisterType("Test", 1, 0, "ValueInterceptorTestType"); QUrl testTypeUrl(testFileUrl("CompositeType.qml")); qmlRegisterType(testTypeUrl, "Test", 1, 0, "TestTypeComposite"); } void tst_qqmlmetatype::qmlParserStatusCast() { QVERIFY(!QQmlMetaType::qmlType(QVariant::Int).isValid()); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId()).isValid()); QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId()).parserStatusCast(), -1); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId()).isValid()); QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId()).parserStatusCast(), -1); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId()).isValid()); int cast = QQmlMetaType::qmlType(qMetaTypeId()).parserStatusCast(); QVERIFY(cast != -1); QVERIFY(cast != 0); ParserStatusTestType t; QVERIFY(reinterpret_cast((QObject *)&t) != reinterpret_cast((QQmlParserStatus *)&t)); QQmlParserStatus *status = reinterpret_cast(reinterpret_cast((QObject *)&t) + cast); QCOMPARE(status, (QQmlParserStatus*)&t); } void tst_qqmlmetatype::qmlPropertyValueSourceCast() { QVERIFY(!QQmlMetaType::qmlType(QVariant::Int).isValid()); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId()).isValid()); QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId()).propertyValueSourceCast(), -1); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId()).isValid()); QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId()).propertyValueSourceCast(), -1); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId()).isValid()); int cast = QQmlMetaType::qmlType(qMetaTypeId()).propertyValueSourceCast(); QVERIFY(cast != -1); QVERIFY(cast != 0); ValueSourceTestType t; QVERIFY(reinterpret_cast((QObject *)&t) != reinterpret_cast((QQmlPropertyValueSource *)&t)); QQmlPropertyValueSource *source = reinterpret_cast(reinterpret_cast((QObject *)&t) + cast); QCOMPARE(source, (QQmlPropertyValueSource*)&t); } void tst_qqmlmetatype::qmlPropertyValueInterceptorCast() { QVERIFY(!QQmlMetaType::qmlType(QVariant::Int).isValid()); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId()).isValid()); QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId()).propertyValueInterceptorCast(), -1); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId()).isValid()); QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId()).propertyValueInterceptorCast(), -1); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId()).isValid()); int cast = QQmlMetaType::qmlType(qMetaTypeId()).propertyValueInterceptorCast(); QVERIFY(cast != -1); QVERIFY(cast != 0); ValueInterceptorTestType t; QVERIFY(reinterpret_cast((QObject *)&t) != reinterpret_cast((QQmlPropertyValueInterceptor *)&t)); QQmlPropertyValueInterceptor *interceptor = reinterpret_cast(reinterpret_cast((QObject *)&t) + cast); QCOMPARE(interceptor, (QQmlPropertyValueInterceptor*)&t); } void tst_qqmlmetatype::qmlType() { QQmlType type = QQmlMetaType::qmlType(QString("ParserStatusTestType"), QString("Test"), 1, 0); QVERIFY(type.isValid()); QVERIFY(type.module() == QLatin1String("Test")); QVERIFY(type.elementName() == QLatin1String("ParserStatusTestType")); QCOMPARE(type.qmlTypeName(), QLatin1String("Test/ParserStatusTestType")); type = QQmlMetaType::qmlType("Test/ParserStatusTestType", 1, 0); QVERIFY(type.isValid()); QVERIFY(type.module() == QLatin1String("Test")); QVERIFY(type.elementName() == QLatin1String("ParserStatusTestType")); QCOMPARE(type.qmlTypeName(), QLatin1String("Test/ParserStatusTestType")); } void tst_qqmlmetatype::invalidQmlTypeName() { QTest::ignoreMessage(QtWarningMsg, "Invalid QML element name \"testtype\"; type names must begin with an uppercase letter"); QTest::ignoreMessage(QtWarningMsg, "Invalid QML element name \"Test$Type\""); QTest::ignoreMessage(QtWarningMsg, "Invalid QML element name \"EndingInSlash/\""); QCOMPARE(qmlRegisterType("TestNamespace", 1, 0, "Test$Type"), -1); // should fail due to invalid QML type name. QCOMPARE(qmlRegisterType("Test", 1, 0, "EndingInSlash/"), -1); QCOMPARE(qmlRegisterType("Test", 1, 0, "testtype"), -1); } void tst_qqmlmetatype::prettyTypeName() { TestType2 obj2; QCOMPARE(QQmlMetaType::prettyTypeName(&obj2), QString("TestType2")); QVERIFY(qmlRegisterType("Test", 1, 0, "") >= 0); QCOMPARE(QQmlMetaType::prettyTypeName(&obj2), QString("TestType2")); TestType3 obj3; QCOMPARE(QQmlMetaType::prettyTypeName(&obj3), QString("TestType3")); QVERIFY(qmlRegisterType("Test", 1, 0, "OtherName") >= 0); QCOMPARE(QQmlMetaType::prettyTypeName(&obj3), QString("OtherName")); } void tst_qqmlmetatype::isList() { QCOMPARE(QQmlMetaType::isList(QVariant::Invalid), false); QCOMPARE(QQmlMetaType::isList(QVariant::Int), false); QQmlListProperty list; QCOMPARE(QQmlMetaType::isList(qMetaTypeId >()), true); } void tst_qqmlmetatype::defaultObject() { QVERIFY(!QQmlMetaType::defaultProperty(&QObject::staticMetaObject).name()); QVERIFY(!QQmlMetaType::defaultProperty(&ParserStatusTestType::staticMetaObject).name()); QCOMPARE(QString(QQmlMetaType::defaultProperty(&TestType::staticMetaObject).name()), QString("foo")); QObject o; TestType t; ParserStatusTestType p; QVERIFY(QQmlMetaType::defaultProperty((QObject *)nullptr).name() == nullptr); QVERIFY(!QQmlMetaType::defaultProperty(&o).name()); QVERIFY(!QQmlMetaType::defaultProperty(&p).name()); QCOMPARE(QString(QQmlMetaType::defaultProperty(&t).name()), QString("foo")); } void tst_qqmlmetatype::registrationType() { QQmlType type = QQmlMetaType::qmlType(QString("TestType"), QString("Test"), 1, 0); QVERIFY(type.isValid()); QVERIFY(!type.isInterface()); QVERIFY(!type.isSingleton()); QVERIFY(!type.isComposite()); type = QQmlMetaType::qmlType(QString("TestTypeSingleton"), QString("Test"), 1, 0); QVERIFY(type.isValid()); QVERIFY(!type.isInterface()); QVERIFY(type.isSingleton()); QVERIFY(!type.isComposite()); type = QQmlMetaType::qmlType(QString("TestTypeComposite"), QString("Test"), 1, 0); QVERIFY(type.isValid()); QVERIFY(!type.isInterface()); QVERIFY(!type.isSingleton()); QVERIFY(type.isComposite()); } void tst_qqmlmetatype::compositeType() { QQmlEngine engine; //Loading the test file also loads all composite types it imports QQmlComponent c(&engine, testFileUrl("testImplicitComposite.qml")); QObject* obj = c.create(); QVERIFY(obj); QQmlType type = QQmlMetaType::qmlType(QString("ImplicitType"), QString(""), 1, 0); QVERIFY(type.isValid()); QVERIFY(type.module().isEmpty()); QCOMPARE(type.elementName(), QLatin1String("ImplicitType")); QCOMPARE(type.qmlTypeName(), QLatin1String("ImplicitType")); QCOMPARE(type.sourceUrl(), testFileUrl("ImplicitType.qml")); } void tst_qqmlmetatype::externalEnums() { QQmlEngine engine; qmlRegisterSingletonType("x.y.z", 1, 0, "ExternalEnums", ExternalEnums::create); QQmlComponent c(&engine, testFileUrl("testExternalEnums.qml")); QObject *obj = c.create(); QVERIFY(obj); QVariant a = obj->property("a"); QCOMPARE(a.type(), QVariant::Int); QCOMPARE(a.toInt(), int(QStandardPaths::DocumentsLocation)); QVariant b = obj->property("b"); QCOMPARE(b.type(), QVariant::Int); QCOMPARE(b.toInt(), int(QStandardPaths::DocumentsLocation)); } class Controller1 : public QObject { Q_OBJECT Q_PROPERTY(QString string MEMBER m_string) Q_PROPERTY(Controller1Enum enumVal MEMBER m_enumVal) public: enum Controller1Enum { ENUM_VALUE_1 = 1, ENUM_VALUE_2 = 2 }; Q_ENUMS(Controller1Enum) Controller1(QObject *parent = nullptr) : QObject(parent), m_string("Controller #1"), m_enumVal(ENUM_VALUE_1) {} private: QString m_string; Controller1Enum m_enumVal; }; class Controller2 : public QObject { Q_OBJECT Q_PROPERTY(QString string MEMBER m_string) Q_PROPERTY(Controller2Enum enumVal MEMBER m_enumVal) public: enum Controller2Enum { ENUM_VALUE_1 = 111, ENUM_VALUE_2 = 222 }; Q_ENUMS(Controller2Enum) Controller2(QObject *parent = nullptr) : QObject(parent), m_string("Controller #2"), m_enumVal(ENUM_VALUE_1) {} private: QString m_string; Controller2Enum m_enumVal; }; void tst_qqmlmetatype::unregisterCustomType() { int controllerId = 0; { QQmlEngine engine; QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); QVERIFY(!type.isValid()); controllerId = qmlRegisterType("mytypes", 1, 0, "Controller"); type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); QVERIFY(type.isValid()); QVERIFY(!type.isInterface()); QVERIFY(!type.isSingleton()); QVERIFY(!type.isComposite()); QQmlComponent c(&engine, testFileUrl("testUnregisterCustomType.qml")); QScopedPointer obj(c.create()); QVERIFY(obj); QObject *controller = obj->findChild("controller"); QVERIFY(qobject_cast(controller)); QVariant stringVal = controller->property("string"); QCOMPARE(stringVal.type(), QVariant::String); QCOMPARE(stringVal.toString(), QStringLiteral("Controller #1")); QVariant enumVal = controller->property("enumVal"); QCOMPARE(enumVal.type(), QVariant::Int); QCOMPARE(enumVal.toInt(), 1); } QQmlMetaType::unregisterType(controllerId); { QQmlEngine engine; QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); QVERIFY(!type.isValid()); controllerId = qmlRegisterType("mytypes", 1, 0, "Controller"); type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); QVERIFY(type.isValid()); QVERIFY(!type.isInterface()); QVERIFY(!type.isSingleton()); QVERIFY(!type.isComposite()); QQmlComponent c(&engine, testFileUrl("testUnregisterCustomType.qml")); QScopedPointer obj(c.create()); QVERIFY(obj); QObject *controller = obj->findChild("controller"); QVERIFY(qobject_cast(controller)); QVariant stringVal = controller->property("string"); QCOMPARE(stringVal.type(), QVariant::String); QCOMPARE(stringVal.toString(), QStringLiteral("Controller #2")); QVariant enumVal = controller->property("enumVal"); QCOMPARE(enumVal.type(), QVariant::Int); QCOMPARE(enumVal.toInt(), 111); } QQmlMetaType::unregisterType(controllerId); { QQmlEngine engine; QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); QVERIFY(!type.isValid()); controllerId = qmlRegisterType("mytypes", 1, 0, "Controller"); type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); QVERIFY(type.isValid()); QVERIFY(!type.isInterface()); QVERIFY(!type.isSingleton()); QVERIFY(!type.isComposite()); QQmlComponent c(&engine, testFileUrl("testUnregisterCustomType.qml")); QScopedPointer obj(c.create()); QVERIFY(obj); QObject *controller = obj->findChild("controller"); QVERIFY(qobject_cast(controller)); QVariant stringVal = controller->property("string"); QCOMPARE(stringVal.type(), QVariant::String); QCOMPARE(stringVal.toString(), QStringLiteral("Controller #1")); QVariant enumVal = controller->property("enumVal"); QCOMPARE(enumVal.type(), QVariant::Int); QCOMPARE(enumVal.toInt(), 1); } } class StaticProvider1 : public QObject { Q_OBJECT public: StaticProvider1(QObject *parent = nullptr) : QObject(parent) {} Q_INVOKABLE QString singletonGetString() { return "StaticProvider #1"; } }; static QObject* createStaticProvider1(QQmlEngine *, QJSEngine *) { return new StaticProvider1; } class StaticProvider2 : public QObject { Q_OBJECT public: StaticProvider2(QObject *parent = nullptr) : QObject(parent) {} Q_INVOKABLE QString singletonGetString() { return "StaticProvider #2"; } }; static QObject* createStaticProvider2(QQmlEngine *, QJSEngine *) { return new StaticProvider2; } void tst_qqmlmetatype::unregisterCustomSingletonType() { int staticProviderId = 0; { QQmlEngine engine; staticProviderId = qmlRegisterSingletonType("mytypes", 1, 0, "StaticProvider", createStaticProvider1); QQmlType type = QQmlMetaType::qmlType(QString("StaticProvider"), QString("mytypes"), 1, 0); QVERIFY(type.isValid()); QVERIFY(!type.isInterface()); QVERIFY(type.isSingleton()); QVERIFY(!type.isComposite()); QQmlComponent c(&engine, testFileUrl("testUnregisterCustomSingletonType.qml")); QScopedPointer obj(c.create()); QVERIFY(obj.data()); QVariant stringVal = obj->property("text"); QCOMPARE(stringVal.type(), QVariant::String); QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1")); } QQmlMetaType::unregisterType(staticProviderId); { QQmlEngine engine; staticProviderId = qmlRegisterSingletonType("mytypes", 1, 0, "StaticProvider", createStaticProvider2); QQmlType type = QQmlMetaType::qmlType(QString("StaticProvider"), QString("mytypes"), 1, 0); QVERIFY(type.isValid()); QVERIFY(!type.isInterface()); QVERIFY(type.isSingleton()); QVERIFY(!type.isComposite()); QQmlComponent c(&engine, testFileUrl("testUnregisterCustomSingletonType.qml")); QScopedPointer obj(c.create()); QVERIFY(obj.data()); QVariant stringVal = obj->property("text"); QCOMPARE(stringVal.type(), QVariant::String); QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #2")); } QQmlMetaType::unregisterType(staticProviderId); { QQmlEngine engine; staticProviderId = qmlRegisterSingletonType("mytypes", 1, 0, "StaticProvider", createStaticProvider1); QQmlType type = QQmlMetaType::qmlType(QString("StaticProvider"), QString("mytypes"), 1, 0); QVERIFY(type.isValid()); QVERIFY(!type.isInterface()); QVERIFY(type.isSingleton()); QVERIFY(!type.isComposite()); QQmlComponent c(&engine, testFileUrl("testUnregisterCustomSingletonType.qml")); QScopedPointer obj(c.create()); QVERIFY(obj.data()); QVariant stringVal = obj->property("text"); QCOMPARE(stringVal.type(), QVariant::String); QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1")); } } void tst_qqmlmetatype::normalizeUrls() { const QUrl url("qrc:///tstqqmlmetatype/data/CompositeType.qml"); QVERIFY(!QQmlMetaType::qmlType(url).isValid()); const auto registrationId = qmlRegisterType(url, "Test", 1, 0, "ResourceCompositeType"); QVERIFY(QQmlMetaType::qmlType(url, /*includeNonFileImports=*/true).isValid()); QUrl normalizedURL("qrc:/tstqqmlmetatype/data/CompositeType.qml"); QVERIFY(QQmlMetaType::qmlType(normalizedURL, /*includeNonFileImports=*/true).isValid()); QQmlMetaType::unregisterType(registrationId); QVERIFY(!QQmlMetaType::qmlType(url, /*includeNonFileImports=*/true).isValid()); } void tst_qqmlmetatype::unregisterAttachedProperties() { qmlClearTypeRegistrations(); const QUrl dummy("qrc:///doesnotexist.qml"); { QQmlEngine e; QQmlComponent c(&e); c.setData("import QtQuick 2.2\n Item { }", dummy); const QQmlType attachedType = QQmlMetaType::qmlType("QtQuick/KeyNavigation", 2, 2); QCOMPARE(attachedType.attachedPropertiesType(QQmlEnginePrivate::get(&e)), attachedType.metaObject()); QVERIFY(c.create()); } qmlClearTypeRegistrations(); { QQmlEngine e; QQmlComponent c(&e); // The extra import shuffles the type IDs around, so that we // get a different ID for the attached properties. If the attached // properties aren't properly cleared, this will crash. c.setData("import QtQml.StateMachine 1.0 \n" "import QtQuick 2.2 \n" "Item { KeyNavigation.up: null }", dummy); const QQmlType attachedType = QQmlMetaType::qmlType("QtQuick/KeyNavigation", 2, 2); QCOMPARE(attachedType.attachedPropertiesType(QQmlEnginePrivate::get(&e)), attachedType.metaObject()); QVERIFY(c.create()); } } class Grouped : public QObject { Q_OBJECT Q_PROPERTY(int prop READ prop WRITE setProp NOTIFY propChanged REVISION 1) public: int prop() const { return m_prop; } void setProp(int prop) { if (prop != m_prop) { m_prop = prop; emit propChanged(prop); } } signals: Q_REVISION(1) void propChanged(int prop); private: int m_prop = 0; }; class MyItem : public QObject { Q_OBJECT Q_PROPERTY(Grouped *grouped READ grouped CONSTANT) public: MyItem() : m_grouped(new Grouped) {} Grouped *grouped() const { return m_grouped.data(); } private: QScopedPointer m_grouped; }; void tst_qqmlmetatype::revisionedGroupedProperties() { qmlClearTypeRegistrations(); qmlRegisterType("GroupedTest", 1, 0, "MyItem"); qmlRegisterType("GroupedTest", 1, 1, "MyItem"); qmlRegisterUncreatableType("GroupedTest", 1, 0, "Grouped", "Grouped"); qmlRegisterUncreatableType("GroupedTest", 1, 1, "Grouped", "Grouped"); { QQmlEngine engine; QQmlComponent valid(&engine, testFileUrl("revisionedGroupedPropertiesValid.qml")); QVERIFY(valid.isReady()); QScopedPointer obj(valid.create()); QVERIFY(!obj.isNull()); } { QQmlEngine engine; QQmlComponent invalid(&engine, testFileUrl("revisionedGroupedPropertiesInvalid.qml")); QVERIFY(invalid.isError()); } } void tst_qqmlmetatype::enumsInRecursiveImport_data() { QTest::addColumn("importPath"); QTest::addColumn("componentUrl"); QTest::addRow("data directory") << dataDirectory() << testFileUrl("enumsInRecursiveImport.qml"); // The qrc case behaves differently because we failed to detect the recursion in type loading // due to varying numbers of slashes after the "qrc:" in the URLs. QTest::addRow("resources") << QStringLiteral("qrc:/data") << QUrl("qrc:/data/enumsInRecursiveImport.qml"); } void tst_qqmlmetatype::enumsInRecursiveImport() { QFETCH(QString, importPath); QFETCH(QUrl, componentUrl); qmlClearTypeRegistrations(); QQmlEngine engine; engine.addImportPath(importPath); QQmlComponent c(&engine, componentUrl); QVERIFY(c.isReady()); QScopedPointer obj(c.create()); QVERIFY(!obj.isNull()); QTRY_COMPARE(obj->property("color").toString(), QString("green")); } QTEST_MAIN(tst_qqmlmetatype) #include "tst_qqmlmetatype.moc"