/**************************************************************************** ** ** Copyright (C) 2021 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 "tst_qmetatype.h" #include #ifdef Q_OS_LINUX # include #endif #include #include // mingw gcc 4.8 also takes way too long, letting the CI system abort the test #if defined(__MINGW32__) # define TST_QMETATYPE_BROKEN_COMPILER #endif Q_DECLARE_METATYPE(QMetaType::Type) namespace CheckTypeTraits { struct NoOperators { int x; }; using Nested = QVector>>; using Nested2 = QVector>>>; // basic types static_assert(QTypeTraits::has_operator_equal_v); static_assert(QTypeTraits::has_operator_less_than_v); static_assert(QTypeTraits::has_operator_equal_v); static_assert(QTypeTraits::has_operator_less_than_v); static_assert(QTypeTraits::has_operator_equal_v); static_assert(QTypeTraits::has_operator_less_than_v); // no comparison operators static_assert(!QTypeTraits::has_operator_equal_v); static_assert(!QTypeTraits::has_operator_less_than_v); // standard Qt types static_assert(QTypeTraits::has_operator_equal_v); static_assert(QTypeTraits::has_operator_less_than_v); static_assert(QTypeTraits::has_operator_equal_v); static_assert(!QTypeTraits::has_operator_less_than_v); // QList static_assert(QTypeTraits::has_operator_equal_v); static_assert(QTypeTraits::has_operator_less_than_v); static_assert(!QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); static_assert(QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); // QPair static_assert(QTypeTraits::has_operator_equal_v>); static_assert(QTypeTraits::has_operator_less_than_v>); static_assert(!QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); // QMap static_assert(QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); static_assert(!QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); // QHash static_assert(QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); static_assert(!QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); // QSharedPointer static_assert(QTypeTraits::has_operator_equal_v>); // smart pointer equality doesn't depend on T static_assert(QTypeTraits::has_operator_equal_v>); // std::vector static_assert(QTypeTraits::has_operator_equal_v>); static_assert(QTypeTraits::has_operator_less_than_v>); static_assert(!QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); static_assert(QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); // std::pair static_assert(QTypeTraits::has_operator_equal_v>); static_assert(QTypeTraits::has_operator_less_than_v>); static_assert(!QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); // std::tuple static_assert(QTypeTraits::has_operator_equal_v>); static_assert(QTypeTraits::has_operator_less_than_v>); static_assert(!QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); // std::map static_assert(QTypeTraits::has_operator_equal_v>); static_assert(QTypeTraits::has_operator_less_than_v>); static_assert(!QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); // std::optional static_assert(QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_equal_v>); // nested types static_assert(QTypeTraits::has_operator_equal_v); static_assert(!QTypeTraits::has_operator_less_than_v); static_assert(QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); static_assert(QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); static_assert(QTypeTraits::has_operator_equal_v); static_assert(!QTypeTraits::has_operator_less_than_v); static_assert(QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); static_assert(QTypeTraits::has_operator_equal_v>); static_assert(!QTypeTraits::has_operator_less_than_v>); } struct BaseGenericType { int m_typeId = -1; QMetaType m_metatype; virtual void *constructor(int typeId, void *where, const void *copy) = 0; virtual void staticMetacallFunction(QMetaObject::Call _c, int _id, void **_a) = 0; virtual void saveOperator(QDataStream & out) const = 0; virtual void loadOperator(QDataStream &in) = 0; virtual ~BaseGenericType() {} }; struct GenericGadgetType : BaseGenericType { void *constructor(int typeId, void *where, const void *copy) override { GenericGadgetType *ret = where ? new(where) GenericGadgetType : new GenericGadgetType; ret->m_typeId = typeId; if (copy) { Q_ASSERT(ret->m_typeId == reinterpret_cast(copy)->m_typeId); *ret = *reinterpret_cast(copy); } else { ret->properties = properties; } return ret; } void staticMetacallFunction(QMetaObject::Call _c, int _id, void **_a) override { if (_c == QMetaObject::ReadProperty) { if (_id < properties.size()) { const auto &prop = properties.at(_id); prop.metaType().destruct(_a[0]); prop.metaType().construct(_a[0], prop.constData()); } } else if (_c == QMetaObject::WriteProperty) { if (_id < properties.size()) { auto & prop = properties[_id]; prop = QVariant(prop.metaType(), _a[0]); } } } void saveOperator(QDataStream & out) const override { for (const auto &prop : properties) out << prop; } void loadOperator(QDataStream &in) override { for (auto &prop : properties) in >> prop; } QList properties; }; struct GenericPODType : BaseGenericType { // BaseGenericType interface void *constructor(int typeId, void *where, const void *copy) override { GenericPODType *ret = where ? new(where) GenericPODType : new GenericPODType; ret->m_typeId = typeId; if (copy) { Q_ASSERT(ret->m_typeId == reinterpret_cast(copy)->m_typeId); *ret = *reinterpret_cast(copy); } else { ret->podData = podData; } return ret; } void staticMetacallFunction(QMetaObject::Call _c, int _id, void **_a) override { Q_UNUSED(_c); Q_UNUSED(_id); Q_UNUSED(_a); Q_ASSERT(false); } void saveOperator(QDataStream &out) const override { out << podData; } void loadOperator(QDataStream &in) override { in >> podData; } QByteArray podData; }; using RegisteredType = QPair, std::shared_ptr>; static QHash s_managedTypes; static void GadgetsStaticMetacallFunction(QObject *_o, QMetaObject::Call _c, int _id, void **_a) { reinterpret_cast(_o)->staticMetacallFunction(_c, _id, _a); } static void GadgetTypedDestructor(int typeId, void *ptr) { QCOMPARE(typeId, reinterpret_cast(ptr)->m_typeId); reinterpret_cast(ptr)->~BaseGenericType(); } static void *GadgetTypedConstructor(int type, void *where, const void *copy) { auto it = s_managedTypes.find(type); if (it == s_managedTypes.end()) return nullptr; // crash the test return it->first->constructor(type, where, copy); } static void GadgetSaveOperator(const QtPrivate::QMetaTypeInterface *, QDataStream & out, const void *data) { reinterpret_cast(data)->saveOperator(out); } static void GadgetLoadOperator(const QtPrivate::QMetaTypeInterface *, QDataStream &in, void *data) { reinterpret_cast(data)->loadOperator(in); } struct Foo { int i; }; class CustomQObject : public QObject { Q_OBJECT public: CustomQObject(QObject *parent = nullptr) : QObject(parent) { } enum CustomQEnum { Val1, Val2 }; Q_ENUM(CustomQEnum) }; class CustomGadget { Q_GADGET }; class CustomGadget_NonDefaultConstructible { Q_GADGET public: CustomGadget_NonDefaultConstructible(int) {}; }; class CustomNonQObject {}; class GadgetDerived : public CustomGadget {}; // cannot use Q_GADGET due to moc limitations but wants to behave like // a Q_GADGET in Qml land template class GadgetDerivedAndTyped : public CustomGadget {}; Q_DECLARE_METATYPE(GadgetDerivedAndTyped) Q_DECLARE_METATYPE(GadgetDerivedAndTyped*) void tst_QMetaType::registerGadget(const char *name, const QList &gadgetProperties) { QMetaObjectBuilder gadgetBuilder; gadgetBuilder.setClassName(name); MetaObjectFlags metaObjectflags = DynamicMetaObject | PropertyAccessInStaticMetaCall; gadgetBuilder.setFlags(metaObjectflags); auto dynamicGadgetProperties = std::make_shared(); for (const auto &prop : gadgetProperties) { int propertyType = QMetaType::fromName(prop.type).id(); dynamicGadgetProperties->properties.push_back(QVariant(QMetaType(propertyType))); auto dynamicPropery = gadgetBuilder.addProperty(prop.name, prop.type); dynamicPropery.setWritable(true); dynamicPropery.setReadable(true); } auto meta = gadgetBuilder.toMetaObject(); meta->d.static_metacall = &GadgetsStaticMetacallFunction; meta->d.superdata = nullptr; const auto flags = QMetaType::IsGadget | QMetaType::NeedsConstruction | QMetaType::NeedsDestruction; struct TypeInfo : public QtPrivate::QMetaTypeInterface { QMetaObject *mo; }; auto typeInfo = new TypeInfo { { 0, alignof(GenericGadgetType), sizeof(GenericGadgetType), uint(flags), 0, [](const QtPrivate::QMetaTypeInterface *self) -> const QMetaObject * { return reinterpret_cast(self)->mo; }, name, [](const QtPrivate::QMetaTypeInterface *self, void *where) { GadgetTypedConstructor(self->typeId, where, nullptr); }, [](const QtPrivate::QMetaTypeInterface *self, void *where, const void *copy) { GadgetTypedConstructor(self->typeId, where, copy); }, [](const QtPrivate::QMetaTypeInterface *self, void *where, void *copy) { GadgetTypedConstructor(self->typeId, where, copy); }, [](const QtPrivate::QMetaTypeInterface *self, void *ptr) { GadgetTypedDestructor(self->typeId, ptr); }, nullptr, nullptr, nullptr, GadgetSaveOperator, GadgetLoadOperator, nullptr }, meta }; QMetaType gadgetMetaType(typeInfo); dynamicGadgetProperties->m_metatype = gadgetMetaType; int gadgetTypeId = QMetaType(typeInfo).id(); QVERIFY(gadgetTypeId > 0); s_managedTypes[gadgetTypeId] = qMakePair(dynamicGadgetProperties, std::shared_ptr{meta, [](QMetaObject *ptr){ ::free(ptr); }}); } void tst_QMetaType::defined() { QCOMPARE(int(QMetaTypeId2::Defined), 1); QCOMPARE(int(QMetaTypeId2::Defined), 0); QCOMPARE(int(QMetaTypeId2::Defined), 1); QCOMPARE(int(QMetaTypeId2::Defined), 0); QCOMPARE(int(QMetaTypeId2::Defined), 1); QCOMPARE(int(QMetaTypeId2::Defined), 1); QCOMPARE(int(QMetaTypeId2::Defined), 1); QVERIFY(!QMetaTypeId2::Defined); QVERIFY(!QMetaTypeId2::Defined); QVERIFY(int(QMetaTypeId2::Defined)); QVERIFY(!QMetaTypeId2::Defined); QVERIFY(!QMetaTypeId2::Defined); QVERIFY(!QMetaTypeId2::Defined); QVERIFY(!QMetaTypeId2::Defined); // registered with Q_DECLARE_METATYPE QVERIFY(QMetaTypeId2>::Defined); QVERIFY(QMetaTypeId2*>::Defined); } struct Bar { Bar() { // check re-entrancy if (!QMetaType::isRegistered(qRegisterMetaType("Foo"))) { qWarning("%s: re-entrancy test failed", Q_FUNC_INFO); ++failureCount; } } ~Bar() {} public: static int failureCount; }; int Bar::failureCount = 0; class MetaTypeTorturer: public QThread { Q_OBJECT protected: void run() override { Bar space[1]; space[0].~Bar(); const QByteArray postFix = '_' + QByteArray::number(reinterpret_cast(QThread::currentThreadId())); for (int i = 0; i < 1000; ++i) { const QByteArray name = "Bar" + QByteArray::number(i) + postFix; const char *nm = name.constData(); int tp = qRegisterMetaType(nm); #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) pthread_yield(); #endif QMetaType info(tp); if (!info.isValid()) { ++failureCount; qWarning() << "Wrong typeInfo returned for" << tp; } if (!info.isRegistered()) { ++failureCount; qWarning() << name << "is not a registered metatype"; } if (QMetaType::typeFlags(tp) != (QMetaType::NeedsConstruction | QMetaType::NeedsDestruction)) { ++failureCount; qWarning() << "Wrong typeInfo returned for" << tp; } if (!QMetaType::isRegistered(tp)) { ++failureCount; qWarning() << name << "is not a registered metatype"; } if (QMetaType::type(nm) != tp) { ++failureCount; qWarning() << "Wrong metatype returned for" << name; } void *buf1 = QMetaType::create(tp, 0); void *buf2 = QMetaType::create(tp, buf1); void *buf3 = info.create(tp, 0); void *buf4 = info.create(tp, buf1); QMetaType::construct(tp, space, 0); QMetaType::destruct(tp, space); QMetaType::construct(tp, space, buf1); QMetaType::destruct(tp, space); info.construct(space, 0); info.destruct(space); info.construct(space, buf1); info.destruct(space); if (!buf1) { ++failureCount; qWarning() << "Null buffer returned by QMetaType::create(tp, 0)"; } if (!buf2) { ++failureCount; qWarning() << "Null buffer returned by QMetaType::create(tp, buf)"; } if (!buf3) { ++failureCount; qWarning() << "Null buffer returned by info.create(tp, 0)"; } if (!buf4) { ++failureCount; qWarning() << "Null buffer returned by infocreate(tp, buf)"; } QMetaType::destroy(tp, buf1); QMetaType::destroy(tp, buf2); info.destroy(buf3); info.destroy(buf4); } new (space) Bar; } public: MetaTypeTorturer() : failureCount(0) { } int failureCount; }; void tst_QMetaType::threadSafety() { MetaTypeTorturer t1; MetaTypeTorturer t2; MetaTypeTorturer t3; t1.start(); t2.start(); t3.start(); QVERIFY(t1.wait()); QVERIFY(t2.wait()); QVERIFY(t3.wait()); QCOMPARE(t1.failureCount, 0); QCOMPARE(t2.failureCount, 0); QCOMPARE(t3.failureCount, 0); QCOMPARE(Bar::failureCount, 0); } namespace TestSpace { struct Foo { double d; public: ~Foo() {} }; struct QungTfu {}; } Q_DECLARE_METATYPE(TestSpace::Foo) #define ADD_TESTSPACE(F) TestSpace::F Q_DECLARE_METATYPE(ADD_TESTSPACE(QungTfu)) void tst_QMetaType::namespaces() { TestSpace::Foo nf = { 11.12 }; QVariant v = QVariant::fromValue(nf); QCOMPARE(qvariant_cast(v).d, 11.12); int qungTfuId = qRegisterMetaType(); QCOMPARE(QMetaType::typeName(qungTfuId), "TestSpace::QungTfu"); } void tst_QMetaType::id() { QCOMPARE(QMetaType(QMetaType::QString).id(), QMetaType::QString); QCOMPARE(QMetaType(::qMetaTypeId()).id(), ::qMetaTypeId()); QCOMPARE(QMetaType::fromType().id(), ::qMetaTypeId()); } void tst_QMetaType::qMetaTypeId() { QCOMPARE(::qMetaTypeId(), int(QMetaType::QString)); QCOMPARE(::qMetaTypeId(), int(QMetaType::Int)); QCOMPARE(::qMetaTypeId(), QMetaType::type("TestSpace::Foo")); QCOMPARE(::qMetaTypeId(), QMetaType::type("char")); QCOMPARE(::qMetaTypeId(), QMetaType::type("unsigned char")); QCOMPARE(::qMetaTypeId(), QMetaType::type("signed char")); QVERIFY(::qMetaTypeId() != ::qMetaTypeId()); QCOMPARE(::qMetaTypeId(), QMetaType::type("qint8")); } void tst_QMetaType::properties() { qRegisterMetaType >("QList"); QVariant v = property("prop"); QCOMPARE(v.typeName(), "QVariantList"); QList values = v.toList(); QCOMPARE(values.count(), 2); QCOMPARE(values.at(0).toInt(), 42); values << 43 << "world"; QVERIFY(setProperty("prop", values)); v = property("prop"); QCOMPARE(v.toList().count(), 4); } void tst_QMetaType::normalizedTypes() { int WhityIntId = ::qMetaTypeId >(); int WhityDoubleId = ::qMetaTypeId >(); QCOMPARE(QMetaType::type("Whity"), WhityIntId); QCOMPARE(QMetaType::type(" Whity < int > "), WhityIntId); QCOMPARE(QMetaType::type("Whity"), WhityIntId); QCOMPARE(QMetaType::type("Whity"), WhityDoubleId); QCOMPARE(QMetaType::type(" Whity< double > "), WhityDoubleId); QCOMPARE(QMetaType::type("Whity"), WhityDoubleId); QCOMPARE(qRegisterMetaType >(" Whity < int > "), WhityIntId); QCOMPARE(qRegisterMetaType >("Whity"), WhityIntId); QCOMPARE(qRegisterMetaType >("Whity "), WhityIntId); QCOMPARE(qRegisterMetaType >(" Whity < double > "), WhityDoubleId); QCOMPARE(qRegisterMetaType >("Whity"), WhityDoubleId); QCOMPARE(qRegisterMetaType >("Whity "), WhityDoubleId); } #define TYPENAME_DATA(MetaTypeName, MetaTypeId, RealType)\ QTest::newRow(#RealType) << int(QMetaType::MetaTypeName) << #RealType; void tst_QMetaType::typeName_data() { QTest::addColumn("aType"); QTest::addColumn("aTypeName"); QT_FOR_EACH_STATIC_TYPE(TYPENAME_DATA) QTest::newRow("QMetaType::UnknownType") << int(QMetaType::UnknownType) << static_cast(0); QTest::newRow("QMetaType::User-1") << (int(QMetaType::User) - 1) << static_cast(nullptr); QTest::newRow("QMetaType::FirstWidgetsType-1") << (int(QMetaType::FirstWidgetsType) - 1) << static_cast(nullptr); QTest::newRow("Whity") << ::qMetaTypeId >() << QString::fromLatin1("Whity"); QTest::newRow("Whity") << ::qMetaTypeId >() << QString::fromLatin1("Whity"); QTest::newRow("Testspace::Foo") << ::qMetaTypeId() << QString::fromLatin1("TestSpace::Foo"); QTest::newRow("-1") << -1 << QString(); QTest::newRow("-124125534") << -124125534 << QString(); QTest::newRow("124125534") << 124125534 << QString(); // automatic registration QTest::newRow("QHash") << ::qMetaTypeId>() << QString::fromLatin1("QHash"); QTest::newRow("QMap") << ::qMetaTypeId>() << QString::fromLatin1("QMap"); QTest::newRow("QList>") << ::qMetaTypeId>>() << QString::fromLatin1("QList>"); // automatic registration with automatic QList to QList aliasing QTest::newRow("QList") << ::qMetaTypeId>() << QString::fromLatin1("QList"); QTest::newRow("QList>") << ::qMetaTypeId>>() << QString::fromLatin1("QList>"); QTest::newRow("CustomQObject*") << ::qMetaTypeId() << QString::fromLatin1("CustomQObject*"); QTest::newRow("CustomGadget") << ::qMetaTypeId() << QString::fromLatin1("CustomGadget"); QTest::newRow("CustomGadget*") << ::qMetaTypeId() << QString::fromLatin1("CustomGadget*"); QTest::newRow("CustomQObject::CustomQEnum") << ::qMetaTypeId() << QString::fromLatin1("CustomQObject::CustomQEnum"); QTest::newRow("Qt::ArrowType") << ::qMetaTypeId() << QString::fromLatin1("Qt::ArrowType"); // template instance class derived from Q_GADGET enabled class QTest::newRow("GadgetDerivedAndTyped") << ::qMetaTypeId>() << QString::fromLatin1("GadgetDerivedAndTyped"); QTest::newRow("GadgetDerivedAndTyped*") << ::qMetaTypeId*>() << QString::fromLatin1("GadgetDerivedAndTyped*"); } void tst_QMetaType::typeName() { QFETCH(int, aType); QFETCH(QString, aTypeName); if (aType >= QMetaType::FirstWidgetsType) QSKIP("The test doesn't link against QtWidgets."); const char *rawname = QMetaType::typeName(aType); QString name = QString::fromLatin1(rawname); QCOMPARE(name, aTypeName); QCOMPARE(name.toLatin1(), QMetaObject::normalizedType(name.toLatin1().constData())); QCOMPARE(rawname == nullptr, aTypeName.isNull()); QMetaType mt(aType); if (mt.isValid()) { // Gui type are not valid QCOMPARE(QString::fromLatin1(QMetaType(aType).name()), aTypeName); } } void tst_QMetaType::type_data() { QTest::addColumn("aType"); QTest::addColumn("aTypeName"); #define TST_QMETATYPE_TYPE_DATA(MetaTypeName, MetaTypeId, RealType)\ QTest::newRow(#RealType) << int(QMetaType::MetaTypeName) << QByteArray( #RealType ); #define TST_QMETATYPE_TYPE_DATA_ALIAS(MetaTypeName, MetaTypeId, AliasType, RealTypeString)\ QTest::newRow(RealTypeString) << int(QMetaType::MetaTypeName) << QByteArray( #AliasType ); QTest::newRow("empty") << int(QMetaType::UnknownType) << QByteArray(); QT_FOR_EACH_STATIC_TYPE(TST_QMETATYPE_TYPE_DATA) QT_FOR_EACH_STATIC_ALIAS_TYPE(TST_QMETATYPE_TYPE_DATA_ALIAS) #undef TST_QMETATYPE_TYPE_DATA #undef TST_METATYPE_TYPE_DATA_ALIAS } void tst_QMetaType::type() { QFETCH(int, aType); QFETCH(QByteArray, aTypeName); if (aType >= QMetaType::FirstWidgetsType) QSKIP("The test doesn't link against QtWidgets."); // QMetaType::type(QByteArray) QCOMPARE(QMetaType::type(aTypeName), aType); // QMetaType::type(const char *) QCOMPARE(QMetaType::type(aTypeName.constData()), aType); } void tst_QMetaType::type_fromSubString_data() { QTest::addColumn("offset"); QTest::addColumn("size"); QTest::addColumn("expectedType"); // The test string is defined in the test function below QTest::newRow("int") << 0 << 3 << int(QMetaType::Int); QTest::newRow("boo") << 3 << 3 << 0; QTest::newRow("bool") << 3 << 4 << int(QMetaType::Bool); QTest::newRow("intbool") << 0 << 7 << 0; QTest::newRow("QMetaType::Type") << 7 << 15 << ::qMetaTypeId(); QTest::newRow("double") << 22 << 6 << int(QMetaType::Double); } void tst_QMetaType::type_fromSubString() { static const char *types = "intboolQMetaType::Typedoublexxx"; QFETCH(int, offset); QFETCH(int, size); QFETCH(int, expectedType); QByteArray ba = QByteArray::fromRawData(types + offset, size); QCOMPARE(QMetaType::type(ba), expectedType); } namespace { template struct static_assert_trigger { static_assert(( QMetaTypeId2::IsBuiltIn )); enum { value = true }; }; } #define CHECK_BUILTIN(MetaTypeName, MetaTypeId, RealType) static_assert_trigger< RealType >::value && static_assert(( FOR_EACH_CORE_METATYPE(CHECK_BUILTIN) true )); #undef CHECK_BUILTIN static_assert(( QMetaTypeId2 >::IsBuiltIn)); static_assert(( QMetaTypeId2 >::IsBuiltIn)); static_assert(( QMetaTypeId2::IsBuiltIn)); static_assert((!QMetaTypeId2::IsBuiltIn)); // QObject subclass static_assert((!QMetaTypeId2 >::IsBuiltIn)); static_assert((!QMetaTypeId2 >::IsBuiltIn)); static_assert((!QMetaTypeId2::IsBuiltIn)); void tst_QMetaType::create_data() { QTest::addColumn("type"); #define ADD_METATYPE_TEST_ROW(MetaTypeName, MetaTypeId, RealType) \ QTest::newRow(QMetaType::typeName(QMetaType::MetaTypeName)) << int(QMetaType::MetaTypeName); FOR_EACH_CORE_METATYPE(ADD_METATYPE_TEST_ROW) #undef ADD_METATYPE_TEST_ROW } template static void testCreateHelper() { typedef typename MetaEnumToType::Type Type; QMetaType info(ID); void *actual1 = QMetaType::create(ID); void *actual2 = info.create(); if (DefaultValueTraits::IsInitialized) { Type *expected = DefaultValueFactory::create(); QCOMPARE(*static_cast(actual1), *expected); QCOMPARE(*static_cast(actual2), *expected); delete expected; } QMetaType::destroy(ID, actual1); info.destroy(actual2); } template<> void testCreateHelper() { void *actual = QMetaType::create(QMetaType::Void); if (DefaultValueTraits::IsInitialized) { QVERIFY(DefaultValueFactory::create()); } QMetaType::destroy(QMetaType::Void, actual); } typedef void (*TypeTestFunction)(); void tst_QMetaType::create() { struct TypeTestFunctionGetter { static TypeTestFunction get(int type) { switch (type) { #define RETURN_CREATE_FUNCTION(MetaTypeName, MetaTypeId, RealType) \ case QMetaType::MetaTypeName: \ return testCreateHelper; FOR_EACH_CORE_METATYPE(RETURN_CREATE_FUNCTION) #undef RETURN_CREATE_FUNCTION } return 0; } }; QFETCH(int, type); TypeTestFunctionGetter::get(type)(); } template static void testCreateCopyHelper() { typedef typename MetaEnumToType::Type Type; Type *expected = TestValueFactory::create(); QMetaType info(ID); void *actual1 = QMetaType::create(ID, expected); void *actual2 = info.create(expected); QCOMPARE(*static_cast(actual1), *expected); QCOMPARE(*static_cast(actual2), *expected); QMetaType::destroy(ID, actual1); info.destroy(actual2); delete expected; } template<> void testCreateCopyHelper() { typedef MetaEnumToType::Type Type; Type *expected = TestValueFactory::create(); void *actual = QMetaType::create(QMetaType::Void, expected); QCOMPARE(static_cast(actual), expected); QMetaType::destroy(QMetaType::Void, actual); } void tst_QMetaType::createCopy_data() { create_data(); } void tst_QMetaType::createCopy() { struct TypeTestFunctionGetter { static TypeTestFunction get(int type) { switch (type) { #define RETURN_CREATE_COPY_FUNCTION(MetaTypeName, MetaTypeId, RealType) \ case QMetaType::MetaTypeName: \ return testCreateCopyHelper; FOR_EACH_CORE_METATYPE(RETURN_CREATE_COPY_FUNCTION) #undef RETURN_CREATE_COPY_FUNCTION } return 0; } }; QFETCH(int, type); TypeTestFunctionGetter::get(type)(); } template constexpr size_t getSize = sizeof(T); template<> constexpr size_t getSize = 0; void tst_QMetaType::sizeOf_data() { QTest::addColumn("type"); QTest::addColumn("size"); QTest::newRow("QMetaType::UnknownType") << int(QMetaType::UnknownType) << size_t(0); #define ADD_METATYPE_TEST_ROW(MetaTypeName, MetaTypeId, RealType) \ QTest::newRow(#RealType) << int(QMetaType::MetaTypeName) << getSize; FOR_EACH_CORE_METATYPE(ADD_METATYPE_TEST_ROW) #undef ADD_METATYPE_TEST_ROW QTest::newRow("Whity") << ::qMetaTypeId >() << sizeof(Whity); QTest::newRow("Whity") << ::qMetaTypeId >() << sizeof(Whity); QTest::newRow("Testspace::Foo") << ::qMetaTypeId() << sizeof(TestSpace::Foo); QTest::newRow("-1") << -1 << size_t(0); QTest::newRow("-124125534") << -124125534 << size_t(0); QTest::newRow("124125534") << 124125534 << size_t(0); } void tst_QMetaType::sizeOf() { QFETCH(int, type); QFETCH(size_t, size); QCOMPARE(size_t(QMetaType::sizeOf(type)), size); } void tst_QMetaType::sizeOfStaticLess_data() { sizeOf_data(); } void tst_QMetaType::sizeOfStaticLess() { QFETCH(int, type); QFETCH(size_t, size); QCOMPARE(size_t(QMetaType(type).sizeOf()), size); } template auto getAlignOf() { if constexpr (std::is_same_v) return 0; else return alignof(T); } void tst_QMetaType::alignOf_data() { QTest::addColumn("type"); QTest::addColumn("size"); QTest::newRow("QMetaType::UnknownType") << int(QMetaType::UnknownType) << size_t(0); #define ADD_METATYPE_TEST_ROW(MetaTypeName, MetaTypeId, RealType) \ QTest::newRow(#RealType) << int(QMetaType::MetaTypeName) << size_t(getAlignOf()); FOR_EACH_CORE_METATYPE(ADD_METATYPE_TEST_ROW) #undef ADD_METATYPE_TEST_ROW QTest::newRow("Whity") << ::qMetaTypeId >() << alignof(Whity); QTest::newRow("Whity") << ::qMetaTypeId >() << alignof(Whity); QTest::newRow("Testspace::Foo") << ::qMetaTypeId() << alignof(TestSpace::Foo); QTest::newRow("-1") << -1 << size_t(0); QTest::newRow("-124125534") << -124125534 << size_t(0); QTest::newRow("124125534") << 124125534 << size_t(0); } void tst_QMetaType::alignOf() { QFETCH(int, type); QFETCH(size_t, size); QCOMPARE(size_t(QMetaType(type).alignOf()), size); } class CustomObject : public QObject { Q_OBJECT public: CustomObject(QObject *parent = nullptr) : QObject(parent) { } }; Q_DECLARE_METATYPE(CustomObject*); struct SecondBase {}; class CustomMultiInheritanceObject : public QObject, SecondBase { Q_OBJECT public: CustomMultiInheritanceObject(QObject *parent = nullptr) : QObject(parent) { } }; Q_DECLARE_METATYPE(CustomMultiInheritanceObject*); class C { Q_DECL_UNUSED_MEMBER char _[4]; public: C() = default; C(const C&) {} }; class M { Q_DECL_UNUSED_MEMBER char _[4]; public: M() {} }; class P { Q_DECL_UNUSED_MEMBER char _[4]; }; QT_BEGIN_NAMESPACE #if defined(Q_CC_GNU) && Q_CC_GNU < 501 Q_DECLARE_TYPEINFO(M, Q_RELOCATABLE_TYPE); Q_DECLARE_TYPEINFO(P, Q_PRIMITIVE_TYPE); #endif QT_END_NAMESPACE // avoid the comma: typedef QPair QPairCC; typedef QPair QPairCM; typedef QPair QPairCP; typedef QPair QPairMC; typedef QPair QPairMM; typedef QPair QPairMP; typedef QPair QPairPC; typedef QPair QPairPM; typedef QPair QPairPP; Q_DECLARE_METATYPE(QPairCC) Q_DECLARE_METATYPE(QPairCM) Q_DECLARE_METATYPE(QPairCP) Q_DECLARE_METATYPE(QPairMC) Q_DECLARE_METATYPE(QPairMM) Q_DECLARE_METATYPE(QPairMP) Q_DECLARE_METATYPE(QPairPC) Q_DECLARE_METATYPE(QPairPM) Q_DECLARE_METATYPE(QPairPP) enum FlagsDataEnum {}; Q_DECLARE_METATYPE(FlagsDataEnum); void tst_QMetaType::flags_data() { QTest::addColumn("type"); QTest::addColumn("isMovable"); QTest::addColumn("isComplex"); QTest::addColumn("isPointerToQObject"); QTest::addColumn("isEnum"); QTest::addColumn("isQmlList"); #define ADD_METATYPE_TEST_ROW(MetaTypeName, MetaTypeId, RealType) \ QTest::newRow(#RealType) << MetaTypeId \ << bool(QTypeInfo::isRelocatable) \ << bool(QTypeInfo::isComplex) \ << bool(QtPrivate::IsPointerToTypeDerivedFromQObject::Value) \ << bool(std::is_enum::value) \ << false; QT_FOR_EACH_STATIC_CORE_CLASS(ADD_METATYPE_TEST_ROW) QT_FOR_EACH_STATIC_PRIMITIVE_POINTER(ADD_METATYPE_TEST_ROW) QT_FOR_EACH_STATIC_CORE_POINTER(ADD_METATYPE_TEST_ROW) #undef ADD_METATYPE_TEST_ROW QTest::newRow("TestSpace::Foo") << ::qMetaTypeId() << false << true << false << false << false; QTest::newRow("Whity") << ::qMetaTypeId >() << true << true << false << false << false; QTest::newRow("CustomMovable") << ::qMetaTypeId() << true << true << false << false << false; QTest::newRow("CustomObject*") << ::qMetaTypeId() << true << false << true << false << false; QTest::newRow("CustomMultiInheritanceObject*") << ::qMetaTypeId() << true << false << true << false << false; QTest::newRow("QPair") << ::qMetaTypeId >() << false << true << false << false << false; QTest::newRow("QPair") << ::qMetaTypeId >() << false << true << false << false << false; QTest::newRow("QPair") << ::qMetaTypeId >() << false << true << false << false << false; QTest::newRow("QPair") << ::qMetaTypeId >() << false << true << false << false << false; QTest::newRow("QPair") << ::qMetaTypeId >() << true << true << false << false << false; QTest::newRow("QPair") << ::qMetaTypeId >() << true << true << false << false << false; QTest::newRow("QPair") << ::qMetaTypeId >() << false << true << false << false << false; QTest::newRow("QPair") << ::qMetaTypeId >() << true << true << false << false << false; QTest::newRow("QPair") << ::qMetaTypeId >() << true << false << false << false << false; QTest::newRow("FlagsDataEnum") << ::qMetaTypeId() << true << false << false << true << false; // invalid ids. QTest::newRow("-1") << -1 << false << false << false << false << false; QTest::newRow("-124125534") << -124125534 << false << false << false << false << false; QTest::newRow("124125534") << 124125534 << false << false << false << false << false; } void tst_QMetaType::flags() { QFETCH(int, type); QFETCH(bool, isMovable); QFETCH(bool, isComplex); QFETCH(bool, isPointerToQObject); QFETCH(bool, isEnum); QFETCH(bool, isQmlList); QCOMPARE(bool(QMetaType::typeFlags(type) & QMetaType::NeedsConstruction), isComplex); QCOMPARE(bool(QMetaType::typeFlags(type) & QMetaType::NeedsDestruction), isComplex); QCOMPARE(bool(QMetaType::typeFlags(type) & QMetaType::RelocatableType), isMovable); QCOMPARE(bool(QMetaType::typeFlags(type) & QMetaType::PointerToQObject), isPointerToQObject); QCOMPARE(bool(QMetaType::typeFlags(type) & QMetaType::IsEnumeration), isEnum); QCOMPARE(bool(QMetaType::typeFlags(type) & QMetaType::IsQmlList), isQmlList); } void tst_QMetaType::flagsStaticLess_data() { flags_data(); } void tst_QMetaType::flagsStaticLess() { QFETCH(int, type); QFETCH(bool, isMovable); QFETCH(bool, isComplex); int flags = QMetaType(type).flags(); QCOMPARE(bool(flags & QMetaType::NeedsConstruction), isComplex); QCOMPARE(bool(flags & QMetaType::NeedsDestruction), isComplex); QCOMPARE(bool(flags & QMetaType::RelocatableType), isMovable); } void tst_QMetaType::flagsBinaryCompatibility6_0_data() { // Changing traits of a built-in type is illegal from BC point of view. // Traits are saved in code of an application and in the Qt library which means // that there may be a mismatch. // The test is loading data generated by this code: // // QList buffer; // buffer.reserve(2 * QMetaType::User); // for (quint32 i = 0; i < QMetaType::LastCoreType; ++i) { // if (QMetaType::isRegistered(i)) { // buffer.append(i); // buffer.append(quint32(QMetaType::typeFlags(i))); // } // } // QFile file("/tmp/typeFlags.bin"); // file.open(QIODevice::WriteOnly); // QDataStream ds(&file); // ds << buffer; // file.close(); QTest::addColumn("id"); QTest::addColumn("flags"); QFile file(QFINDTESTDATA("typeFlags.bin")); file.open(QIODevice::ReadOnly); QList buffer; QDataStream ds(&file); ds >> buffer; for (int i = 0; i < buffer.size(); i+=2) { const quint32 id = buffer.at(i); const quint32 flags = buffer.at(i + 1); if (id > QMetaType::LastCoreType) continue; // We do not link against QtGui, so we do longer consider such type as registered QVERIFY2(QMetaType::isRegistered(id), "A type could not be removed in BC way"); QTest::newRow(QMetaType::typeName(id)) << id << flags; } } void tst_QMetaType::flagsBinaryCompatibility6_0() { QFETCH(quint32, id); QFETCH(quint32, flags); quint32 mask_5_0 = 0x1fb; // Only compare the values that were already defined in 5.0 QCOMPARE(quint32(QMetaType::typeFlags(id)) & mask_5_0, flags & mask_5_0); } void tst_QMetaType::construct_data() { create_data(); } template static void testConstructHelper() { typedef typename MetaEnumToType::Type Type; QMetaType info(ID); int size = info.sizeOf(); void *storage1 = qMallocAligned(size, alignof(Type)); void *actual1 = QMetaType::construct(ID, storage1, /*copy=*/0); void *storage2 = qMallocAligned(size, alignof(Type)); void *actual2 = info.construct(storage2, /*copy=*/0); QCOMPARE(actual1, storage1); QCOMPARE(actual2, storage2); if (DefaultValueTraits::IsInitialized) { Type *expected = DefaultValueFactory::create(); QCOMPARE(*static_cast(actual1), *expected); QCOMPARE(*static_cast(actual2), *expected); delete expected; } QMetaType::destruct(ID, actual1); qFreeAligned(storage1); info.destruct(actual2); qFreeAligned(storage2); QVERIFY(QMetaType::construct(ID, 0, /*copy=*/0) == 0); QMetaType::destruct(ID, 0); QVERIFY(info.construct(0, /*copy=*/0) == 0); info.destruct(0); } template<> void testConstructHelper() { /*int size = */ QMetaType::sizeOf(QMetaType::Void); void *storage = 0; void *actual = QMetaType::construct(QMetaType::Void, storage, /*copy=*/0); QCOMPARE(actual, storage); if (DefaultValueTraits::IsInitialized) { QVERIFY(DefaultValueFactory::create()); } QMetaType::destruct(QMetaType::Void, actual); qFreeAligned(storage); QVERIFY(QMetaType::construct(QMetaType::Void, 0, /*copy=*/0) == 0); QMetaType::destruct(QMetaType::Void, 0); } void tst_QMetaType::construct() { struct TypeTestFunctionGetter { static TypeTestFunction get(int type) { switch (type) { #define RETURN_CONSTRUCT_FUNCTION(MetaTypeName, MetaTypeId, RealType) \ case QMetaType::MetaTypeName: \ return testConstructHelper; FOR_EACH_CORE_METATYPE(RETURN_CONSTRUCT_FUNCTION) #undef RETURN_CONSTRUCT_FUNCTION } return 0; } }; QFETCH(int, type); TypeTestFunctionGetter::get(type)(); } void tst_QMetaType::typedConstruct() { auto testMetaObjectWriteOnGadget = [](QVariant &gadget, const QList &properties) { auto metaObject = QMetaType::metaObjectForType(gadget.userType()); QVERIFY(metaObject != nullptr); QCOMPARE(metaObject->methodCount(), 0); QCOMPARE(metaObject->propertyCount(), properties.size()); for (int i = 0; i < metaObject->propertyCount(); ++i) { auto prop = metaObject->property(i); QCOMPARE(properties[i].name, prop.name()); QCOMPARE(properties[i].type, prop.typeName()); prop.writeOnGadget(gadget.data(), properties[i].testData); } }; auto testMetaObjectReadOnGadget = [](QVariant gadget, const QList &properties) { auto metaObject = QMetaType::metaObjectForType(gadget.userType()); QVERIFY(metaObject != nullptr); QCOMPARE(metaObject->methodCount(), 0); QCOMPARE(metaObject->propertyCount(), properties.size()); for (int i = 0; i < metaObject->propertyCount(); ++i) { auto prop = metaObject->property(i); QCOMPARE(properties[i].name, prop.name()); QCOMPARE(properties[i].type, prop.typeName()); if (!QMetaType::typeFlags(prop.userType()).testFlag(QMetaType::IsGadget)) QCOMPARE(properties[i].testData, prop.readOnGadget(gadget.constData())); } }; QList dynamicGadget1 = { {"int", "int_prop", 34526}, {"float", "float_prop", 1.23f}, {"QString", "string_prop", QString{"Test QString"}} }; registerGadget("DynamicGadget1", dynamicGadget1); QVariant testGadget1(QMetaType(QMetaType::type("DynamicGadget1"))); testMetaObjectWriteOnGadget(testGadget1, dynamicGadget1); testMetaObjectReadOnGadget(testGadget1, dynamicGadget1); QList dynamicGadget2 = { {"int", "int_prop", 512}, {"double", "double_prop", 4.56}, {"QString", "string_prop", QString{"Another String"}}, {"DynamicGadget1", "dynamicGadget1_prop", testGadget1} }; registerGadget("DynamicGadget2", dynamicGadget2); QVariant testGadget2(QMetaType(QMetaType::type("DynamicGadget2"))); testMetaObjectWriteOnGadget(testGadget2, dynamicGadget2); testMetaObjectReadOnGadget(testGadget2, dynamicGadget2); auto g2mo = QMetaType::metaObjectForType(testGadget2.userType()); auto dynamicGadget1_prop = g2mo->property(g2mo->indexOfProperty("dynamicGadget1_prop")); testMetaObjectReadOnGadget(dynamicGadget1_prop.readOnGadget(testGadget2.constData()), dynamicGadget1); // Register POD const QByteArray myPodTesData = "My POD test data"; const char podTypeName[] = "DynamicPOD"; auto dynamicGadgetProperties = std::make_shared(); dynamicGadgetProperties->podData = myPodTesData; const auto flags = QMetaType::NeedsConstruction | QMetaType::NeedsDestruction; using TypeInfo = QtPrivate::QMetaTypeInterface; auto typeInfo = new TypeInfo { 0, alignof(GenericGadgetType), sizeof(GenericGadgetType), uint(flags), 0, nullptr, podTypeName, [](const TypeInfo *self, void *where) { GadgetTypedConstructor(self->typeId, where, nullptr); }, [](const TypeInfo *self, void *where, const void *copy) { GadgetTypedConstructor(self->typeId, where, copy); }, [](const TypeInfo *self, void *where, void *copy) { GadgetTypedConstructor(self->typeId, where, copy); }, [](const TypeInfo *self, void *ptr) { GadgetTypedDestructor(self->typeId, ptr); }, nullptr, nullptr, nullptr, GadgetSaveOperator, GadgetLoadOperator, nullptr }; QMetaType metatype(typeInfo); dynamicGadgetProperties->m_metatype = metatype; int podTypeId = metatype.id(); QVERIFY(podTypeId > 0); s_managedTypes[podTypeId] = qMakePair(dynamicGadgetProperties, std::shared_ptr{}); // Test POD QCOMPARE(podTypeId, QMetaType::type(podTypeName)); QVariant podVariant{QMetaType(podTypeId)}; QCOMPARE(myPodTesData, static_cast(reinterpret_cast(podVariant.constData()))->podData); QVariant podVariant1{podVariant}; podVariant1.detach(); // Test stream operators static_cast(reinterpret_cast(podVariant.data()))->podData.clear(); QCOMPARE(myPodTesData, static_cast(reinterpret_cast(podVariant1.constData()))->podData); } template static void testConstructCopyHelper() { typedef typename MetaEnumToType::Type Type; Type *expected = TestValueFactory::create(); QMetaType info(ID); int size = QMetaType::sizeOf(ID); QCOMPARE(info.sizeOf(), size); void *storage1 = qMallocAligned(size, alignof(Type)); void *actual1 = QMetaType::construct(ID, storage1, expected); void *storage2 = qMallocAligned(size, alignof(Type)); void *actual2 = info.construct(storage2, expected); QCOMPARE(actual1, storage1); QCOMPARE(actual2, storage2); QCOMPARE(*static_cast(actual1), *expected); QCOMPARE(*static_cast(actual2), *expected); QMetaType::destruct(ID, actual1); qFreeAligned(storage1); info.destruct(actual2); qFreeAligned(storage2); QVERIFY(QMetaType::construct(ID, 0, expected) == 0); QVERIFY(info.construct(0, expected) == 0); delete expected; } template<> void testConstructCopyHelper() { typedef MetaEnumToType::Type Type; Type *expected = TestValueFactory::create(); /* int size = */QMetaType::sizeOf(QMetaType::Void); void *storage = 0; void *actual = QMetaType::construct(QMetaType::Void, storage, expected); QCOMPARE(actual, storage); QMetaType::destruct(QMetaType::Void, actual); qFreeAligned(storage); QVERIFY(QMetaType::construct(QMetaType::Void, 0, expected) == 0); } void tst_QMetaType::constructCopy_data() { create_data(); } void tst_QMetaType::constructCopy() { struct TypeTestFunctionGetter { static TypeTestFunction get(int type) { switch (type) { #define RETURN_CONSTRUCT_COPY_FUNCTION(MetaTypeName, MetaTypeId, RealType) \ case QMetaType::MetaTypeName: \ return testConstructCopyHelper; FOR_EACH_CORE_METATYPE(RETURN_CONSTRUCT_COPY_FUNCTION) #undef RETURN_CONSTRUCT_COPY_FUNCTION } return 0; } }; QFETCH(int, type); TypeTestFunctionGetter::get(type)(); } typedef QString CustomString; Q_DECLARE_METATYPE(CustomString) //this line is useless void tst_QMetaType::typedefs() { QCOMPARE(QMetaType::type("long long"), int(QMetaType::LongLong)); QCOMPARE(QMetaType::type("unsigned long long"), int(QMetaType::ULongLong)); QCOMPARE(QMetaType::type("qint8"), int(QMetaType::SChar)); QCOMPARE(QMetaType::type("quint8"), int(QMetaType::UChar)); QCOMPARE(QMetaType::type("qint16"), int(QMetaType::Short)); QCOMPARE(QMetaType::type("quint16"), int(QMetaType::UShort)); QCOMPARE(QMetaType::type("qint32"), int(QMetaType::Int)); QCOMPARE(QMetaType::type("quint32"), int(QMetaType::UInt)); QCOMPARE(QMetaType::type("qint64"), int(QMetaType::LongLong)); QCOMPARE(QMetaType::type("quint64"), int(QMetaType::ULongLong)); // make sure the qreal typeId is the type id of the type it's defined to QCOMPARE(QMetaType::type("qreal"), ::qMetaTypeId()); qRegisterMetaType("CustomString"); QCOMPARE(QMetaType::type("CustomString"), ::qMetaTypeId()); typedef Whity WhityDouble; qRegisterMetaType("WhityDouble"); QCOMPARE(QMetaType::type("WhityDouble"), ::qMetaTypeId()); } void tst_QMetaType::registerType() { // Built-in QCOMPARE(qRegisterMetaType("QString"), int(QMetaType::QString)); QCOMPARE(qRegisterMetaType("QString"), int(QMetaType::QString)); // Custom int fooId = qRegisterMetaType("TestSpace::Foo"); QVERIFY(fooId >= int(QMetaType::User)); QCOMPARE(qRegisterMetaType("TestSpace::Foo"), fooId); int movableId = qRegisterMetaType("CustomMovable"); QVERIFY(movableId >= int(QMetaType::User)); QCOMPARE(qRegisterMetaType("CustomMovable"), movableId); // Alias to built-in typedef QString MyString; QCOMPARE(qRegisterMetaType("MyString"), int(QMetaType::QString)); QCOMPARE(qRegisterMetaType("MyString"), int(QMetaType::QString)); QCOMPARE(QMetaType::type("MyString"), int(QMetaType::QString)); // Alias to custom type typedef CustomMovable MyMovable; typedef TestSpace::Foo MyFoo; QCOMPARE(qRegisterMetaType("MyMovable"), movableId); QCOMPARE(qRegisterMetaType("MyMovable"), movableId); QCOMPARE(QMetaType::type("MyMovable"), movableId); QCOMPARE(qRegisterMetaType("MyFoo"), fooId); QCOMPARE(qRegisterMetaType("MyFoo"), fooId); QCOMPARE(QMetaType::type("MyFoo"), fooId); } class IsRegisteredDummyType { }; void tst_QMetaType::isRegistered_data() { QTest::addColumn("typeId"); QTest::addColumn("registered"); // predefined/custom types QTest::newRow("QMetaType::Void") << int(QMetaType::Void) << true; QTest::newRow("QMetaType::Int") << int(QMetaType::Int) << true; int dummyTypeId = qRegisterMetaType("IsRegisteredDummyType"); QTest::newRow("IsRegisteredDummyType") << dummyTypeId << true; // unknown types QTest::newRow("-1") << -1 << false; QTest::newRow("-42") << -42 << false; QTest::newRow("IsRegisteredDummyType + 1") << (dummyTypeId + 1) << false; QTest::newRow("QMetaType::UnknownType") << int(QMetaType::UnknownType) << false; } void tst_QMetaType::isRegistered() { QFETCH(int, typeId); QFETCH(bool, registered); QCOMPARE(QMetaType::isRegistered(typeId), registered); } enum isEnumTest_Enum0 {}; struct isEnumTest_Struct0 { enum A{}; }; enum isEnumTest_Enum1 {}; struct isEnumTest_Struct1 {}; Q_DECLARE_METATYPE(isEnumTest_Struct1) Q_DECLARE_METATYPE(isEnumTest_Enum1) void tst_QMetaType::isEnum() { int type0 = qRegisterMetaType("int"); QVERIFY((QMetaType::typeFlags(type0) & QMetaType::IsEnumeration) == 0); int type1 = qRegisterMetaType("isEnumTest_Enum0"); QVERIFY((QMetaType::typeFlags(type1) & QMetaType::IsEnumeration) == QMetaType::IsEnumeration); int type2 = qRegisterMetaType("isEnumTest_Struct0"); QVERIFY((QMetaType::typeFlags(type2) & QMetaType::IsEnumeration) == 0); int type3 = qRegisterMetaType("isEnumTest_Enum0 *"); QVERIFY((QMetaType::typeFlags(type3) & QMetaType::IsEnumeration) == 0); int type4 = qRegisterMetaType("isEnumTest_Struct0::A"); QVERIFY((QMetaType::typeFlags(type4) & QMetaType::IsEnumeration) == QMetaType::IsEnumeration); int type5 = ::qMetaTypeId(); QVERIFY((QMetaType::typeFlags(type5) & QMetaType::IsEnumeration) == 0); int type6 = ::qMetaTypeId(); QVERIFY((QMetaType::typeFlags(type6) & QMetaType::IsEnumeration) == QMetaType::IsEnumeration); } void tst_QMetaType::isRegisteredStaticLess_data() { isRegistered_data(); } void tst_QMetaType::isRegisteredStaticLess() { QFETCH(int, typeId); QFETCH(bool, registered); QCOMPARE(QMetaType(typeId).isRegistered(), registered); } typedef QHash IntUIntHash; Q_DECLARE_METATYPE(IntUIntHash) typedef QMap IntUIntMap; Q_DECLARE_METATYPE(IntUIntMap) typedef QPair IntUIntPair; Q_DECLARE_METATYPE(IntUIntPair) struct CustomComparable { CustomComparable(int i_ = 0) :i(i_) { } bool operator==(const CustomComparable &other) const { return i == other.i; } int i; }; struct UnregisteredType {}; typedef QHash IntComparableHash; Q_DECLARE_METATYPE(IntComparableHash) typedef QMap IntComparableMap; Q_DECLARE_METATYPE(IntComparableMap) typedef QPair IntComparablePair; Q_DECLARE_METATYPE(IntComparablePair) typedef QHash IntIntHash; typedef int NaturalNumber; class AutoMetaTypeObject : public QObject { Q_OBJECT Q_PROPERTY(IntIntHash someHash READ someHash CONSTANT) Q_PROPERTY(NaturalNumber someInt READ someInt CONSTANT) public: AutoMetaTypeObject(QObject *parent = nullptr) : QObject(parent), m_int(42) { m_hash.insert(4, 2); } QHash someHash() const { return m_hash; } int someInt() const { return m_int; } private: QHash m_hash; int m_int; }; class MyObject : public QObject { Q_OBJECT public: MyObject(QObject *parent = nullptr) : QObject(parent) { } }; typedef MyObject* MyObjectPtr; Q_DECLARE_METATYPE(MyObjectPtr) #if !defined(TST_QMETATYPE_BROKEN_COMPILER) static QByteArray createTypeName(const char *begin, const char *va) { QByteArray tn(begin); const QList args = QByteArray(va).split(','); tn += args.first().trimmed(); if (args.size() > 1) { QList::const_iterator it = args.constBegin() + 1; const QList::const_iterator end = args.constEnd(); for (; it != end; ++it) { tn += ","; tn += it->trimmed(); } } if (tn.endsWith('>')) tn += ' '; tn += '>'; return tn; } #endif Q_DECLARE_METATYPE(const void*) void tst_QMetaType::automaticTemplateRegistration() { #define TEST_SEQUENTIAL_CONTAINER(CONTAINER, VALUE_TYPE) \ { \ CONTAINER innerContainer; \ innerContainer.push_back(42); \ QVERIFY(*QVariant::fromValue(innerContainer).value >().begin() == 42); \ QList > outerContainer; \ outerContainer << innerContainer; \ QVERIFY(*QVariant::fromValue(outerContainer).value > >().first().begin() == 42); \ } TEST_SEQUENTIAL_CONTAINER(QList, int) TEST_SEQUENTIAL_CONTAINER(std::vector, int) TEST_SEQUENTIAL_CONTAINER(std::list, int) { std::vector vecbool; vecbool.push_back(true); vecbool.push_back(false); vecbool.push_back(true); QVERIFY(QVariant::fromValue(vecbool).value>().front() == true); QList> vectorList; vectorList << vecbool; QVERIFY(QVariant::fromValue(vectorList).value>>().first().front() == true); } { QList unsignedList; unsignedList << 123; QVERIFY(QVariant::fromValue(unsignedList).value>().first() == 123); QList> vectorList; vectorList << unsignedList; QVERIFY(QVariant::fromValue(vectorList).value>>().first().first() == 123); } QCOMPARE(::qMetaTypeId(), (int)QMetaType::QVariantList); QCOMPARE(::qMetaTypeId >(), (int)QMetaType::QVariantList); TEST_SEQUENTIAL_CONTAINER(QList, QVariant) TEST_SEQUENTIAL_CONTAINER(std::vector, QVariant) TEST_SEQUENTIAL_CONTAINER(std::list, QVariant) { QList > sharedPointerList; QObject *testObject = new QObject; sharedPointerList << QSharedPointer(testObject); QVERIFY(QVariant::fromValue(sharedPointerList).value>>().first() == testObject); QList>> vectorList; vectorList << sharedPointerList; QVERIFY(QVariant::fromValue(vectorList).value>>>().first().first() == testObject); } { IntIntHash intIntHash; intIntHash.insert(4, 2); QCOMPARE(QVariant::fromValue(intIntHash).value().value(4), 2); AutoMetaTypeObject amto; qRegisterMetaType >("IntIntHash"); QVariant hashVariant = amto.property("someHash"); QCOMPARE(hashVariant.value().value(4), 2); qRegisterMetaType("NaturalNumber"); QVariant intVariant = amto.property("someInt"); QCOMPARE(intVariant.value(), 42); } { IntUIntHash intUIntHash; intUIntHash.insert(4, 2); QCOMPARE(QVariant::fromValue(intUIntHash).value().value(4), (uint)2); } { IntComparableHash intComparableHash; CustomComparable m; intComparableHash.insert(4, m); QCOMPARE(QVariant::fromValue(intComparableHash).value().value(4), m); } { QVariantHash variantHash; variantHash.insert(QStringLiteral("4"), 2); QCOMPARE(QVariant::fromValue(variantHash).value().value(QStringLiteral("4")), QVariant(2)); } { typedef QMap IntIntMap; IntIntMap intIntMap; intIntMap.insert(4, 2); QCOMPARE(QVariant::fromValue(intIntMap).value().value(4), 2); } { IntUIntMap intUIntMap; intUIntMap.insert(4, 2); QCOMPARE(QVariant::fromValue(intUIntMap).value().value(4), (uint)2); } { IntComparableMap intComparableMap; CustomComparable m; intComparableMap.insert(4, m); QCOMPARE(QVariant::fromValue(intComparableMap).value().value(4), m); } { QVariantMap variantMap; variantMap.insert(QStringLiteral("4"), 2); QCOMPARE(QVariant::fromValue(variantMap).value().value(QStringLiteral("4")), QVariant(2)); } { typedef std::map IntIntMap; IntIntMap intIntMap; intIntMap[4] = 2; QCOMPARE(QVariant::fromValue(intIntMap).value()[4], 2); } { typedef std::map StdIntUIntMap; StdIntUIntMap intUIntMap; intUIntMap[4] = 2; QCOMPARE(QVariant::fromValue(intUIntMap).value()[4], (uint)2); } { typedef std::map StdMapIntCustomObject ; StdMapIntCustomObject intComparableMap; CustomObject *o = nullptr; intComparableMap[4] = o; QCOMPARE(QVariant::fromValue(intComparableMap).value()[4], o); } { typedef std::map StdMapStringVariant; StdMapStringVariant variantMap; variantMap[QStringLiteral("4")] = 2; QCOMPARE(QVariant::fromValue(variantMap).value()[QStringLiteral("4")], QVariant(2)); } { typedef QPair IntIntPair; IntIntPair intIntPair = qMakePair(4, 2); QCOMPARE(QVariant::fromValue(intIntPair).value().first, 4); QCOMPARE(QVariant::fromValue(intIntPair).value().second, 2); } { IntUIntPair intUIntPair = qMakePair(4, 2u); QCOMPARE(QVariant::fromValue(intUIntPair).value().first, 4); QCOMPARE(QVariant::fromValue(intUIntPair).value().second, (uint)2); } { CustomComparable m; IntComparablePair intComparablePair = qMakePair(4, m); QCOMPARE(QVariant::fromValue(intComparablePair).value().first, 4); QCOMPARE(QVariant::fromValue(intComparablePair).value().second, m); } { typedef std::pair IntIntPair; IntIntPair intIntPair = std::make_pair(4, 2); QCOMPARE(QVariant::fromValue(intIntPair).value().first, 4); QCOMPARE(QVariant::fromValue(intIntPair).value().second, 2); } { typedef std::pair StdIntUIntPair; StdIntUIntPair intUIntPair = std::make_pair(4, 2); QCOMPARE(QVariant::fromValue(intUIntPair).value().first, 4); QCOMPARE(QVariant::fromValue(intUIntPair).value().second, (uint)2); } { typedef std::pair StdIntComparablePair; CustomQObject *o = nullptr; StdIntComparablePair intComparablePair = std::make_pair(4, o); QCOMPARE(QVariant::fromValue(intComparablePair).value().first, 4); QCOMPARE(QVariant::fromValue(intComparablePair).value().second, o); } { typedef QHash IntUnregisteredTypeHash; QVERIFY(qRegisterMetaType("IntUnregisteredTypeHash") > 0); } { typedef QList UnregisteredTypeList; QVERIFY(qRegisterMetaType("UnregisteredTypeList") > 0); } #if !defined(TST_QMETATYPE_BROKEN_COMPILER) #define FOR_EACH_STATIC_PRIMITIVE_TYPE(F) \ F(bool) \ F(int) \ F(qulonglong) \ F(double) \ F(short) \ F(char) \ F(ulong) \ F(uchar) \ F(float) \ F(QObject*) \ F(QString) \ F(CustomMovable) #define FOR_EACH_STATIC_PRIMITIVE_TYPE2(F, SecondaryRealName) \ F(uint, SecondaryRealName) \ F(qlonglong, SecondaryRealName) \ F(char, SecondaryRealName) \ F(uchar, SecondaryRealName) \ F(QObject*, SecondaryRealName) #define CREATE_AND_VERIFY_CONTAINER(CONTAINER, ...) \ { \ CONTAINER< __VA_ARGS__ > t; \ const QVariant v = QVariant::fromValue(t); \ QByteArray tn = createTypeName(#CONTAINER "<", #__VA_ARGS__); \ const int expectedType = ::qMetaTypeId >(); \ const int type = QMetaType::type(tn); \ QCOMPARE(type, expectedType); \ QCOMPARE((QMetaType::fromType>().id()), expectedType); \ } #define FOR_EACH_1ARG_TEMPLATE_TYPE(F, TYPE) \ F(QList, TYPE) \ F(QQueue, TYPE) \ F(QStack, TYPE) \ F(QSet, TYPE) #define PRINT_1ARG_TEMPLATE(RealName) \ FOR_EACH_1ARG_TEMPLATE_TYPE(CREATE_AND_VERIFY_CONTAINER, RealName) #define FOR_EACH_2ARG_TEMPLATE_TYPE(F, RealName1, RealName2) \ F(QHash, RealName1, RealName2) \ F(QMap, RealName1, RealName2) \ F(std::pair, RealName1, RealName2) #define PRINT_2ARG_TEMPLATE_INTERNAL(RealName1, RealName2) \ FOR_EACH_2ARG_TEMPLATE_TYPE(CREATE_AND_VERIFY_CONTAINER, RealName1, RealName2) #define PRINT_2ARG_TEMPLATE(RealName) \ FOR_EACH_STATIC_PRIMITIVE_TYPE2(PRINT_2ARG_TEMPLATE_INTERNAL, RealName) #define REGISTER_TYPEDEF(TYPE, ARG1, ARG2) \ qRegisterMetaType>(#TYPE "<" #ARG1 "," #ARG2 ">"); REGISTER_TYPEDEF(QHash, int, uint) REGISTER_TYPEDEF(QMap, int, uint) REGISTER_TYPEDEF(QPair, int, uint) FOR_EACH_STATIC_PRIMITIVE_TYPE( PRINT_1ARG_TEMPLATE ) FOR_EACH_STATIC_PRIMITIVE_TYPE( PRINT_2ARG_TEMPLATE ) CREATE_AND_VERIFY_CONTAINER(QList, QList>>>) CREATE_AND_VERIFY_CONTAINER(QList, void*) CREATE_AND_VERIFY_CONTAINER(QList, const void*) CREATE_AND_VERIFY_CONTAINER(QList, void*) CREATE_AND_VERIFY_CONTAINER(std::pair, void*, void*) CREATE_AND_VERIFY_CONTAINER(QHash, void*, void*) CREATE_AND_VERIFY_CONTAINER(QHash, const void*, const void*) #endif // !defined(TST_QMETATYPE_BROKEN_COMPILER) #define TEST_OWNING_SMARTPOINTER(SMARTPOINTER, ELEMENT_TYPE, FLAG_TEST, FROMVARIANTFUNCTION) \ { \ SMARTPOINTER < ELEMENT_TYPE > sp(new ELEMENT_TYPE); \ sp.data()->setObjectName("Test name"); \ QVariant v = QVariant::fromValue(sp); \ QCOMPARE(v.typeName(), #SMARTPOINTER "<" #ELEMENT_TYPE ">"); \ QVERIFY(QMetaType::typeFlags(::qMetaTypeId >()) & QMetaType::FLAG_TEST); \ SMARTPOINTER < QObject > extractedPtr = FROMVARIANTFUNCTION(v); \ QCOMPARE(extractedPtr.data()->objectName(), sp.data()->objectName()); \ } TEST_OWNING_SMARTPOINTER(QSharedPointer, QObject, SharedPointerToQObject, qSharedPointerFromVariant) TEST_OWNING_SMARTPOINTER(QSharedPointer, QFile, SharedPointerToQObject, qSharedPointerFromVariant) TEST_OWNING_SMARTPOINTER(QSharedPointer, QTemporaryFile, SharedPointerToQObject, qSharedPointerFromVariant) TEST_OWNING_SMARTPOINTER(QSharedPointer, MyObject, SharedPointerToQObject, qSharedPointerFromVariant) #undef TEST_OWNING_SMARTPOINTER #define TEST_NONOWNING_SMARTPOINTER(SMARTPOINTER, ELEMENT_TYPE, FLAG_TEST, FROMVARIANTFUNCTION) \ { \ ELEMENT_TYPE elem; \ SMARTPOINTER < ELEMENT_TYPE > sp(&elem); \ sp.data()->setObjectName("Test name"); \ QVariant v = QVariant::fromValue(sp); \ QCOMPARE(v.typeName(), #SMARTPOINTER "<" #ELEMENT_TYPE ">"); \ QVERIFY(QMetaType::typeFlags(::qMetaTypeId >()) & QMetaType::FLAG_TEST); \ SMARTPOINTER < QObject > extractedPtr = FROMVARIANTFUNCTION(v); \ QCOMPARE(extractedPtr.data()->objectName(), sp.data()->objectName()); \ } TEST_NONOWNING_SMARTPOINTER(QPointer, QObject, TrackingPointerToQObject, qPointerFromVariant) TEST_NONOWNING_SMARTPOINTER(QPointer, QFile, TrackingPointerToQObject, qPointerFromVariant) TEST_NONOWNING_SMARTPOINTER(QPointer, QTemporaryFile, TrackingPointerToQObject, qPointerFromVariant) TEST_NONOWNING_SMARTPOINTER(QPointer, MyObject, TrackingPointerToQObject, qPointerFromVariant) #undef TEST_NONOWNING_SMARTPOINTER #define TEST_WEAK_SMARTPOINTER(ELEMENT_TYPE, FLAG_TEST) \ { \ ELEMENT_TYPE elem; \ QSharedPointer < ELEMENT_TYPE > shared(new ELEMENT_TYPE); \ QWeakPointer < ELEMENT_TYPE > sp(shared); \ sp.toStrongRef()->setObjectName("Test name"); \ QVariant v = QVariant::fromValue(sp); \ QCOMPARE(v.typeName(), "QWeakPointer<" #ELEMENT_TYPE ">"); \ QVERIFY(QMetaType::typeFlags(::qMetaTypeId >()) & QMetaType::FLAG_TEST); \ } TEST_WEAK_SMARTPOINTER(QObject, WeakPointerToQObject) TEST_WEAK_SMARTPOINTER(QFile, WeakPointerToQObject) TEST_WEAK_SMARTPOINTER(QTemporaryFile, WeakPointerToQObject) TEST_WEAK_SMARTPOINTER(MyObject, WeakPointerToQObject) #undef TEST_WEAK_SMARTPOINTER } template struct StreamingTraits { enum { isStreamable = 1 }; // Streamable by default }; // Non-streamable types #define DECLARE_NONSTREAMABLE(Type) \ template<> struct StreamingTraits { enum { isStreamable = 0 }; }; DECLARE_NONSTREAMABLE(void) DECLARE_NONSTREAMABLE(void*) DECLARE_NONSTREAMABLE(QModelIndex) DECLARE_NONSTREAMABLE(QPersistentModelIndex) DECLARE_NONSTREAMABLE(QObject*) DECLARE_NONSTREAMABLE(QWidget*) #define DECLARE_WIDGETS_CLASS_NONSTREAMABLE(MetaTypeName, MetaTypeId, RealType) \ DECLARE_NONSTREAMABLE(RealType) QT_FOR_EACH_STATIC_WIDGETS_CLASS(DECLARE_WIDGETS_CLASS_NONSTREAMABLE) #undef DECLARE_WIDGETS_CLASS_NONSTREAMABLE #undef DECLARE_NONSTREAMABLE void tst_QMetaType::saveAndLoadBuiltin_data() { QTest::addColumn("type"); QTest::addColumn("isStreamable"); #define ADD_METATYPE_TEST_ROW(MetaTypeName, MetaTypeId, RealType) \ QTest::newRow(#RealType) << MetaTypeId << bool(StreamingTraits::isStreamable); QT_FOR_EACH_STATIC_TYPE(ADD_METATYPE_TEST_ROW) #undef ADD_METATYPE_TEST_ROW } void tst_QMetaType::saveAndLoadBuiltin() { QFETCH(int, type); QFETCH(bool, isStreamable); void *value = QMetaType::create(type); QByteArray ba; QDataStream stream(&ba, QIODevice::ReadWrite); QCOMPARE(QMetaType::save(stream, type, value), isStreamable); QCOMPARE(stream.status(), QDataStream::Ok); if (isStreamable) { QVERIFY(QMetaType(type).hasRegisteredDataStreamOperators()); QVERIFY(QMetaType::load(stream, type, value)); // Hmmm, shouldn't it return false? // std::nullptr_t is nullary: it doesn't actually read anything if (type != QMetaType::Nullptr) QCOMPARE(stream.status(), QDataStream::ReadPastEnd); } else { QVERIFY(!QMetaType(type).hasRegisteredDataStreamOperators()); } stream.device()->seek(0); stream.resetStatus(); QCOMPARE(QMetaType::load(stream, type, value), isStreamable); QCOMPARE(stream.status(), QDataStream::Ok); if (isStreamable) { QVERIFY(QMetaType::load(stream, type, value)); // Hmmm, shouldn't it return false? // std::nullptr_t is nullary: it doesn't actually read anything if (type != QMetaType::Nullptr) QCOMPARE(stream.status(), QDataStream::ReadPastEnd); } QMetaType::destroy(type, value); } struct CustomStreamableType { int a; }; QDataStream &operator<<(QDataStream &out, const CustomStreamableType &t) { out << t.a; return out; } QDataStream &operator>>(QDataStream &in, CustomStreamableType &t) { int a; in >> a; if (in.status() == QDataStream::Ok) t.a = a; return in; } Q_DECLARE_METATYPE(CustomStreamableType) void tst_QMetaType::saveAndLoadCustom() { CustomStreamableType t; t.a = 123; int id = ::qMetaTypeId(); QByteArray ba; QDataStream stream(&ba, QIODevice::ReadWrite); QVERIFY(QMetaType::save(stream, id, &t)); QCOMPARE(stream.status(), QDataStream::Ok); CustomStreamableType t2; t2.a = -1; QVERIFY(QMetaType::load(stream, id, &t2)); // Hmmm, shouldn't it return false? QCOMPARE(stream.status(), QDataStream::ReadPastEnd); QCOMPARE(t2.a, -1); stream.device()->seek(0); stream.resetStatus(); QVERIFY(QMetaType::load(stream, id, &t2)); QCOMPARE(stream.status(), QDataStream::Ok); QCOMPARE(t2.a, t.a); QVERIFY(QMetaType::load(stream, id, &t2)); // Hmmm, shouldn't it return false? QCOMPARE(stream.status(), QDataStream::ReadPastEnd); } class MyGadget { Q_GADGET; public: enum MyEnum { Val1, Val2, Val3 }; Q_ENUM(MyEnum) }; class MyQObjectFromGadget : public QObject, public MyGadget { Q_OBJECT public: MyQObjectFromGadget(QObject *parent = nullptr) : QObject(parent) {} }; Q_DECLARE_METATYPE(MyGadget); Q_DECLARE_METATYPE(MyGadget*); Q_DECLARE_METATYPE(const QMetaObject *); Q_DECLARE_METATYPE(Qt::ScrollBarPolicy); Q_DECLARE_METATYPE(MyGadget::MyEnum); Q_DECLARE_METATYPE(MyQObjectFromGadget*); void tst_QMetaType::metaObject_data() { QTest::addColumn("type"); QTest::addColumn("result"); QTest::addColumn("isGadget"); QTest::addColumn("isGadgetPtr"); QTest::addColumn("isQObjectPtr"); QTest::newRow("QObject") << int(QMetaType::QObjectStar) << &QObject::staticMetaObject << false << false << true; QTest::newRow("QFile*") << ::qMetaTypeId() << &QFile::staticMetaObject << false << false << true; QTest::newRow("MyObject*") << ::qMetaTypeId() << &MyObject::staticMetaObject << false << false << true; QTest::newRow("int") << int(QMetaType::Int) << static_cast(0) << false << false << false; QTest::newRow("QEasingCurve") << ::qMetaTypeId() << &QEasingCurve::staticMetaObject << true << false << false; QTest::newRow("MyGadget") << ::qMetaTypeId() << &MyGadget::staticMetaObject << true << false << false; QTest::newRow("MyGadget*") << ::qMetaTypeId() << &MyGadget::staticMetaObject << false << true << false; QTest::newRow("MyEnum") << ::qMetaTypeId() << &MyGadget::staticMetaObject << false << false << false; QTest::newRow("Qt::ScrollBarPolicy") << ::qMetaTypeId() << &Qt::staticMetaObject << false << false << false; QTest::newRow("MyQObjectFromGadget*") << ::qMetaTypeId() << &MyQObjectFromGadget::staticMetaObject << false << false << true; QTest::newRow("GadgetDerivedAndTyped") << ::qMetaTypeId>() << &GadgetDerivedAndTyped::staticMetaObject << true << false << false; QTest::newRow("GadgetDerivedAndTyped*") << ::qMetaTypeId*>() << &GadgetDerivedAndTyped::staticMetaObject << false << true << false; } void tst_QMetaType::metaObject() { QFETCH(int, type); QFETCH(const QMetaObject *, result); QFETCH(bool, isGadget); QFETCH(bool, isGadgetPtr); QFETCH(bool, isQObjectPtr); QCOMPARE(QMetaType::metaObjectForType(type), result); QMetaType mt(type); QCOMPARE(mt.metaObject(), result); QCOMPARE(!!(mt.flags() & QMetaType::IsGadget), isGadget); QCOMPARE(!!(mt.flags() & QMetaType::PointerToGadget), isGadgetPtr); QCOMPARE(!!(mt.flags() & QMetaType::PointerToQObject), isQObjectPtr); } #define METATYPE_ID_FUNCTION(Type, MetaTypeId, Name) \ case ::qMetaTypeId< Name >(): metaType = MetaTypeIdStruct::Value; break; #define REGISTER_METATYPE_FUNCTION(Type, MetaTypeId, Name) \ case qRegisterMetaType< Name >(): metaType = RegisterMetaTypeStruct::Value; break; template struct MetaTypeIdStruct { }; template struct RegisterMetaTypeStruct { }; #define METATYPE_ID_STRUCT(Type, MetaTypeId, Name) \ template<> \ struct MetaTypeIdStruct< ::qMetaTypeId< Name >()> \ { \ enum { Value = ::qMetaTypeId< Name >() }; \ }; #define REGISTER_METATYPE_STRUCT(Type, MetaTypeId, Name) \ template<> \ struct RegisterMetaTypeStruct()> \ { \ enum { Value = qRegisterMetaType< Name >() }; \ }; QT_FOR_EACH_STATIC_TYPE(METATYPE_ID_STRUCT) QT_FOR_EACH_STATIC_TYPE(REGISTER_METATYPE_STRUCT) template()> struct MetaTypeIdStructDefaultTemplateValue { enum { Value }; }; template()> struct RegisterMetaTypeStructDefaultTemplateValue { enum { Value }; }; void tst_QMetaType::constexprMetaTypeIds() { int id = 0; int metaType; switch(id) { QT_FOR_EACH_STATIC_TYPE(METATYPE_ID_FUNCTION) metaType = MetaTypeIdStructDefaultTemplateValue<>::Value; default:; } switch (id) { QT_FOR_EACH_STATIC_TYPE(REGISTER_METATYPE_FUNCTION) metaType = RegisterMetaTypeStructDefaultTemplateValue<>::Value; default:; } Q_UNUSED(metaType); } QTEST_MAIN(tst_QMetaType) #include "tst_qmetatype.moc"