From b184dd0a01fda019d5a0960f6d074b6391dba655 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Thu, 2 Feb 2012 14:20:24 +0100 Subject: Bring qmetaobjectbuilder in sync with moc qmetaobjectbuilder should generate meta-objects of the same version as moc; in the future, when the moc version is bumped, QMOB has to be adapted at the same time. QMOB was generating version 4 meta-objects. This patch makes it generate version 6 (the current version). This also fixes a bug with using qt_static_metacall with QMOB (setStaticMetacallFunction()); it was already using the version 6 qt_static_metacall signature, which isn't compatible with version 4. Also add tests that ensure that the QMOB-generated meta-object works with real objects; in particular we want to test the codepaths in Qt that check for version >= 4. Change-Id: I64a151ea5c947a6f8b7a00e85a39866446c735e9 Reviewed-by: Bradley T. Hughes --- .../qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp | 368 ++++++++++++++++++++- 1 file changed, 355 insertions(+), 13 deletions(-) (limited to 'tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp') diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp index 450d42703c..def1b74ea4 100644 --- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp +++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -47,7 +47,6 @@ class tst_QMetaObjectBuilder : public QObject { Q_OBJECT private slots: - void mocVersionCheck(); void create(); void className(); void superClass(); @@ -67,6 +66,14 @@ private slots: void serialize(); void removeNotifySignal(); + void usage_signal(); + void usage_property(); + void usage_slot(); + void usage_method(); + void usage_constructor(); + void usage_connect(); + void usage_templateConnect(); + private: static bool checkForSideEffects (const QMetaObjectBuilder& builder, @@ -130,18 +137,6 @@ signals: void propChanged(const QString&); }; -void tst_QMetaObjectBuilder::mocVersionCheck() -{ - // This test will fail when the moc version number is changed. - // It is intended as a reminder to also update QMetaObjectBuilder - // whenenver moc changes. Once QMetaObjectBuilder has been - // updated, this test can be changed to check for the next version. - int version = int(QObject::staticMetaObject.d.data[0]); - QVERIFY(version == 4 || version == 5 || version == 6); - version = int(staticMetaObject.d.data[0]); - QVERIFY(version == 4 || version == 5 || version == 6); -} - void tst_QMetaObjectBuilder::create() { QMetaObjectBuilder builder; @@ -1274,6 +1269,353 @@ bool tst_QMetaObjectBuilder::sameMetaObject return true; } + +// This class is used to test that the meta-object generated by QMOB can be +// used by a real object. +// The class manually implements the functions normally generated by moc, and +// creates the corresponding meta-object using QMOB. The autotests check that +// this object can be used by QObject/QMetaObject functionality (property +// access, signals & slots, constructing instances, ...). + +class TestObject : public QObject +{ + // Manually expanded from Q_OBJECT macro +public: + Q_OBJECT_CHECK + virtual const QMetaObject *metaObject() const; + virtual void *qt_metacast(const char *); + virtual int qt_metacall(QMetaObject::Call, int, void **); +private: + Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; + Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); + + //Q_PROPERTY(int intProp READ intProp WRITE setIntProp NOTIFY intPropChanged) +public: + TestObject(QObject *parent = 0); // Q_INVOKABLE + ~TestObject(); + + // Property accessors + int intProp() const; + void setIntProp(int v); + + void emitIntPropChanged(); + + int voidSlotIntArgument() const; + +// Q_INVOKABLE + QVariantList listInvokableQRealQString(qreal, const QString &); + +//public Q_SLOTS: + void voidSlotInt(int); + +//Q_SIGNALS: + void intPropChanged(int); + +private: + static QMetaObject *buildMetaObject(); + + QMetaObject *m_metaObject; + int m_intProp; + int m_voidSlotIntArg; +}; + +const QMetaObjectExtraData TestObject::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +TestObject::TestObject(QObject *parent) + : QObject(parent), m_metaObject(buildMetaObject()), + m_intProp(-1), m_voidSlotIntArg(-1) +{ +} + +TestObject::~TestObject() +{ + qFree(m_metaObject); +} + +QMetaObject *TestObject::buildMetaObject() +{ + QMetaObjectBuilder builder; + // NOTE: If you change the meta-object, remember to adapt qt_metacall and + // friends below accordingly. + + builder.setClassName("TestObject"); + + builder.setStaticMetacallFunction(qt_static_metacall); + + QMetaMethodBuilder intPropChanged = builder.addSignal("intPropChanged(int)"); + intPropChanged.setParameterNames(QList() << "newIntPropValue"); + + QMetaPropertyBuilder prop = builder.addProperty("intProp", "int"); + prop.setNotifySignal(intPropChanged); + + QMetaMethodBuilder voidSlotInt = builder.addSlot("voidSlotInt(int)"); + voidSlotInt.setParameterNames(QList() << "slotIntArg"); + + QMetaMethodBuilder listInvokableQRealQString = builder.addMethod("listInvokableQRealQString(qreal,QString)"); + listInvokableQRealQString.setReturnType("QVariantList"); + listInvokableQRealQString.setParameterNames(QList() << "qrealArg" << "qstringArg"); + + builder.addConstructor("TestObject(QObject*)"); + builder.addConstructor("TestObject()"); + + return builder.toMetaObject(); +} + +int TestObject::intProp() const +{ + return m_intProp; +} + +void TestObject::setIntProp(int value) +{ + if (m_intProp != value) { + m_intProp = value; + emit intPropChanged(value); + } +} + +void TestObject::emitIntPropChanged() +{ + emit intPropChanged(m_intProp); +} + +QVariantList TestObject::listInvokableQRealQString(qreal r, const QString &s) +{ + return QVariantList() << r << s; +} + +void TestObject::voidSlotInt(int value) +{ + m_voidSlotIntArg = value; +} + +int TestObject::voidSlotIntArgument() const +{ + return m_voidSlotIntArg; +} + +void TestObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::CreateInstance) { + switch (_id) { + case 0: { TestObject *_r = new TestObject((*reinterpret_cast< QObject*(*)>(_a[1]))); + if (_a[0]) *reinterpret_cast(_a[0]) = _r; } break; + case 1: { TestObject *_r = new TestObject(); + if (_a[0]) *reinterpret_cast(_a[0]) = _r; } break; + default: { + QMetaMethod ctor = _o->metaObject()->constructor(_id); + qFatal("You forgot to add a case for CreateInstance %s", ctor.signature()); + } + } + } else if (_c == QMetaObject::InvokeMetaMethod) { + Q_ASSERT(_o->metaObject()->cast(_o)); + TestObject *_t = static_cast(_o); + switch (_id) { + case 0: _t->intPropChanged((*reinterpret_cast< int(*)>(_a[1]))); break; + case 1: _t->voidSlotInt((*reinterpret_cast< int(*)>(_a[1]))); break; + case 2: *reinterpret_cast(_a[0]) = _t->listInvokableQRealQString(*reinterpret_cast(_a[1]), *reinterpret_cast(_a[2])); break; + default: { + QMetaMethod method = _o->metaObject()->method(_o->metaObject()->methodOffset() + _id); + qFatal("You forgot to add a case for InvokeMetaMethod %s", method.signature()); + } + } + } else if (_c == QMetaObject::IndexOfMethod) { + // This code is currently unreachable because it's only used by the + // template-based versions of connect() and disconnect(), which don't + // work with dynamically generated meta-objects (see test). + int *result = reinterpret_cast(_a[0]); + void **func = reinterpret_cast(_a[1]); + { + typedef void (TestObject::*_t)(int ); + if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TestObject::intPropChanged)) { + *result = 0; + } + } + { + typedef void (TestObject::*_t)(int ); + if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TestObject::voidSlotInt)) { + *result = 1; + } + } + { + typedef QVariantList (TestObject::*_t)(qreal, const QString &); + if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TestObject::listInvokableQRealQString)) { + *result = 2; + } + } + qFatal("You forgot to add one or more IndexOfMethod cases"); + } +} + +const QMetaObject *TestObject::metaObject() const +{ + return m_metaObject; +} + +void *TestObject::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, "TestObject")) + return static_cast(const_cast< TestObject*>(this)); + return QObject::qt_metacast(_clname); +} + +int TestObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + int ownMethodCount = m_metaObject->methodCount() - m_metaObject->methodOffset(); + int ownPropertyCount = m_metaObject->propertyCount() - m_metaObject->propertyOffset(); + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < ownMethodCount) + qt_static_metacall(this, _c, _id, _a); + _id -= ownMethodCount; + } +#ifndef QT_NO_PROPERTIES + else if (_c == QMetaObject::ReadProperty) { + void *_v = _a[0]; + switch (_id) { + case 0: *reinterpret_cast< int*>(_v) = intProp(); break; + default: if (_id < ownPropertyCount) { + QMetaProperty prop = m_metaObject->property(m_metaObject->propertyOffset() + _id); + qFatal("You forgot to add a case for ReadProperty %s", prop.name()); + } + } + _id -= ownPropertyCount; + } else if (_c == QMetaObject::WriteProperty) { + void *_v = _a[0]; + switch (_id) { + case 0: setIntProp(*reinterpret_cast< int*>(_v)); break; + default: if (_id < ownPropertyCount) { + QMetaProperty prop = m_metaObject->property(m_metaObject->propertyOffset() + _id); + qFatal("You forgot to add a case for WriteProperty %s", prop.name()); + } + } + _id -= ownPropertyCount; + } else if (_c == QMetaObject::ResetProperty) { + _id -= ownPropertyCount; + } else if (_c == QMetaObject::QueryPropertyDesignable) { + _id -= ownPropertyCount; + } else if (_c == QMetaObject::QueryPropertyScriptable) { + _id -= ownPropertyCount; + } else if (_c == QMetaObject::QueryPropertyStored) { + _id -= ownPropertyCount; + } else if (_c == QMetaObject::QueryPropertyEditable) { + _id -= ownPropertyCount; + } else if (_c == QMetaObject::QueryPropertyUser) { + _id -= ownPropertyCount; + } +#endif // QT_NO_PROPERTIES + return _id; +} + +// SIGNAL 0 +void TestObject::intPropChanged(int _t1) +{ + void *_a[] = { 0, const_cast(reinterpret_cast(&_t1)) }; + QMetaObject::activate(this, m_metaObject, 0, _a); +} + + +void tst_QMetaObjectBuilder::usage_signal() +{ + QScopedPointer testObject(new TestObject); + + QSignalSpy propChangedSpy(testObject.data(), SIGNAL(intPropChanged(int))); + testObject->emitIntPropChanged(); + QCOMPARE(propChangedSpy.count(), 1); + QCOMPARE(propChangedSpy.at(0).count(), 1); + QCOMPARE(propChangedSpy.at(0).at(0).toInt(), testObject->intProp()); +} + +void tst_QMetaObjectBuilder::usage_property() +{ + QScopedPointer testObject(new TestObject); + + QVariant prop = testObject->property("intProp"); + QCOMPARE(prop.type(), QVariant::Int); + QCOMPARE(prop.toInt(), testObject->intProp()); + + QSignalSpy propChangedSpy(testObject.data(), SIGNAL(intPropChanged(int))); + QVERIFY(testObject->intProp() != 123); + testObject->setProperty("intProp", 123); + QCOMPARE(propChangedSpy.count(), 1); + prop = testObject->property("intProp"); + QCOMPARE(prop.type(), QVariant::Int); + QCOMPARE(prop.toInt(), 123); +} + +void tst_QMetaObjectBuilder::usage_slot() +{ + QScopedPointer testObject(new TestObject); + + int index = testObject->metaObject()->indexOfMethod("voidSlotInt(int)"); + QVERIFY(index != -1); + QMetaMethod voidSlotInt = testObject->metaObject()->method(index); + + QVERIFY(testObject->voidSlotIntArgument() == -1); + QVERIFY(voidSlotInt.invoke(testObject.data(), Q_ARG(int, 123))); + QCOMPARE(testObject->voidSlotIntArgument(), 123); +} + +void tst_QMetaObjectBuilder::usage_method() +{ + QScopedPointer testObject(new TestObject); + + int index = testObject->metaObject()->indexOfMethod("listInvokableQRealQString(qreal,QString)"); + QVERIFY(index != -1); + QMetaMethod listInvokableQRealQString = testObject->metaObject()->method(index); + QVariantList list; + QVERIFY(listInvokableQRealQString.invoke(testObject.data(), Q_RETURN_ARG(QVariantList, list), + Q_ARG(qreal, 123.0), Q_ARG(QString, "ciao"))); + QCOMPARE(list.size(), 2); + QCOMPARE(list.at(0).type(), QVariant::Type(QMetaType::QReal)); + QCOMPARE(list.at(0).toDouble(), double(123)); + QCOMPARE(list.at(1).type(), QVariant::String); + QCOMPARE(list.at(1).toString(), QString::fromLatin1("ciao")); +} + +void tst_QMetaObjectBuilder::usage_constructor() +{ + QScopedPointer testObject(new TestObject); + + QCOMPARE(testObject->metaObject()->constructorCount(), 2); + QScopedPointer testInstance(testObject->metaObject()->newInstance()); + QVERIFY(testInstance != 0); + QScopedPointer testInstance2(testObject->metaObject()->newInstance(Q_ARG(QObject*, testInstance.data()))); + QVERIFY(testInstance2 != 0); + QCOMPARE(testInstance2->parent(), testInstance.data()); +} + +void tst_QMetaObjectBuilder::usage_connect() +{ + QScopedPointer testObject(new TestObject); + + QVERIFY(QObject::connect(testObject.data(), SIGNAL(intPropChanged(int)), + testObject.data(), SLOT(voidSlotInt(int)))); + + QVERIFY(testObject->voidSlotIntArgument() == -1); + testObject->setProperty("intProp", 123); + QCOMPARE(testObject->voidSlotIntArgument(), 123); + + QVERIFY(QObject::disconnect(testObject.data(), SIGNAL(intPropChanged(int)), + testObject.data(), SLOT(voidSlotInt(int)))); +} + +void tst_QMetaObjectBuilder::usage_templateConnect() +{ + QScopedPointer testObject(new TestObject); + + QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in QObject"); + QMetaObject::Connection con = QObject::connect(testObject.data(), &TestObject::intPropChanged, + testObject.data(), &TestObject::voidSlotInt); + QEXPECT_FAIL("", "template-based connect() fails because meta-object is deduced at compile-time", Abort); + QVERIFY(con); +} + QTEST_MAIN(tst_QMetaObjectBuilder) #include "tst_qmetaobjectbuilder.moc" -- cgit v1.2.3