diff options
author | Alex <qt-info@nokia.com> | 2010-01-28 17:47:47 +1000 |
---|---|---|
committer | Alex <qt-info@nokia.com> | 2010-01-28 17:47:47 +1000 |
commit | aa016515052e8e793fa227d24fa3f73ac61ae7e9 (patch) | |
tree | 06b3e6c1bb05ed577fe53e44a4742f140632ff9f | |
parent | 4c8246f393f60e04432b460f528671be7786cbb8 (diff) |
meta method invocation w/o return type
-rw-r--r-- | examples/sfwipcclient/main.cpp | 40 | ||||
-rw-r--r-- | examples/sfwipcservice/main.cpp | 33 | ||||
-rw-r--r-- | src/serviceframework/ipc/objectendpoint.cpp | 101 | ||||
-rw-r--r-- | src/serviceframework/ipc/objectendpoint_p.h | 2 | ||||
-rw-r--r-- | src/serviceframework/ipc/proxyobject.cpp | 76 | ||||
-rw-r--r-- | src/serviceframework/ipc/proxyobject_p.h | 3 | ||||
-rw-r--r-- | src/serviceframework/ipc/qmetaobjectbuilder_p.h | 8 | ||||
-rw-r--r-- | src/serviceframework/ipc/qservicepackage.cpp | 16 | ||||
-rw-r--r-- | src/serviceframework/ipc/qservicepackage_p.h | 12 | ||||
-rw-r--r-- | src/serviceframework/qservicemanager.cpp | 5 | ||||
-rw-r--r-- | tests/auto/auto.pro | 3 | ||||
-rw-r--r-- | tests/auto/qmetaobjectbuilder/qmetaobjectbuilder.pro | 37 | ||||
-rw-r--r-- | tests/auto/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp | 1286 |
13 files changed, 1563 insertions, 59 deletions
diff --git a/examples/sfwipcclient/main.cpp b/examples/sfwipcclient/main.cpp index 3dfc6d417d..23dd285b23 100644 --- a/examples/sfwipcclient/main.cpp +++ b/examples/sfwipcclient/main.cpp @@ -1,6 +1,8 @@ #include <QApplication> #include "qservicemanager.h" #include <QTimer> +#include <QMetaObject> +#include <QMetaMethod> QTM_USE_NAMESPACE @@ -16,11 +18,47 @@ public: qWarning() << "Cannot find service. Error:" << manager.error(); QTimer::singleShot(1000, this, SLOT(killProcess())); } + checkServiceObject(); + } + +public slots: + void checkServiceObject() + { + const QMetaObject* mo = service->metaObject(); + qDebug() << "ServiceObject class: " << mo->className() << mo->superClass() << mo->superClass()->className(); + qDebug() << "Methods:" << mo->methodCount()- mo->methodOffset() << "(" << mo->methodCount() << ")"; + for (int i=0; i< mo->methodCount(); i++) { + QMetaMethod method = mo->method(i); + QString type; + switch(method.methodType()) { + case QMetaMethod::Signal: + type = "signal"; break; + case QMetaMethod::Slot: + type = "slot"; break; + case QMetaMethod::Constructor: + type = "constrcutor"; break; + case QMetaMethod::Method: + type = "method"; break; + } + qDebug() << " " << i << "." << method.signature() << type; + + } + + QTimer::singleShot(1000, this, SLOT(useService())); + } + + void useService() + { + qDebug() << "Invoking testSlot()"; + QMetaObject::invokeMethod( service, "testSlot" ); + + qDebug() << "Invoking testSlotWithArgs(QByteArray, int, QVariant)"; + QMetaObject::invokeMethod( service, "testSlotWithArgs", + Q_ARG(QByteArray, "array"), Q_ARG(int, 5), Q_ARG(QVariant, "dddd")); QTimer::singleShot(1000, this, SLOT(killService())); } -public slots: void killService() { delete service; diff --git a/examples/sfwipcservice/main.cpp b/examples/sfwipcservice/main.cpp index 4129b89fb6..d7c65cf507 100644 --- a/examples/sfwipcservice/main.cpp +++ b/examples/sfwipcservice/main.cpp @@ -13,11 +13,26 @@ class TestService : public QObject Q_SERVICE(TestService, "com.nokia.qt.interface", "3.4") public: - Q_INVOKABLE TestService(QObject* parent = 0) + TestService(QObject* parent = 0) : QObject(parent) { } +Q_SIGNALS: + void testSignal(); +public slots: + void testSlot() + { + qDebug() << "TestService::testSlot() called"; + } + + void testSlotWithArgs(const QByteArray& d, int a, const QVariant& variant) + { + QString output("%1, %2, %3"); + output = output.arg(d.constData()).arg(a).arg(variant.toString()); + qDebug() << "TestService::testSlotWithArgs(" << output << ") called"; + } + }; class TestService2 : public QObject { @@ -25,10 +40,24 @@ class TestService2 : public QObject Q_SERVICE(TestService2, "com.nokia.qt.interface", "3.5") public: - TestService2(QObject* parent = 0) + /*Q_INVOKABLE*/ TestService2(QObject* parent = 0) : QObject(parent) { } +Q_SIGNALS: + void testSignal(); + +public slots: + void testSlot() { + qDebug() << "TestService2::testSlot() called"; + } + + void testSlotWithArgs(const QByteArray& d, int a, const QVariant& variant) + { + QString output("%1, %2, %3"); + output = output.arg(d.constData()).arg(a).arg(variant.toString()); + qDebug() << "TestService2::testSlotWithArgs(" << output << ") called"; + } }; void unregisterExampleService() diff --git a/src/serviceframework/ipc/objectendpoint.cpp b/src/serviceframework/ipc/objectendpoint.cpp index e3fcaf1238..925b9adb5a 100644 --- a/src/serviceframework/ipc/objectendpoint.cpp +++ b/src/serviceframework/ipc/objectendpoint.cpp @@ -103,16 +103,15 @@ QObject* ObjectEndPoint::constructProxy(const QServiceTypeIdent& ident) //return meta object QServicePackage p; p.d = new QServicePackagePrivate(); - p.d->type = QServicePackage::ObjectCreation; - p.d->id = QUuid::createUuid(); + p.d->messageId = QUuid::createUuid(); p.d->typeId = ident; Response* response = new Response(); - openRequests()->insert(p.d->id, response); + openRequests()->insert(p.d->messageId, response); dispatch->writePackage(p); - waitForResponse(p.d->id); + waitForResponse(p.d->messageId); if (response->isFinished) { if (response->result == 0) @@ -123,7 +122,7 @@ QObject* ObjectEndPoint::constructProxy(const QServiceTypeIdent& ident) qDebug() << "response passed but not finished"; } - openRequests()->take(p.d->id); + openRequests()->take(p.d->messageId); delete response; return service; @@ -137,7 +136,7 @@ void ObjectEndPoint::newPackageReady() if (!p.isValid()) continue; - switch(p.d->type) { + switch(p.d->packageType) { case QServicePackage::ObjectCreation: objectRequest(p); break; @@ -155,21 +154,17 @@ void ObjectEndPoint::objectRequest(const QServicePackage& p) { if (p.d->responseType != QServicePackage::NotAResponse ) { qDebug() << p; - Response* response = openRequests()->value(p.d->id); + Response* response = openRequests()->value(p.d->messageId); if (p.d->responseType == QServicePackage::Failed) { response->result = 0; response->isFinished = true; + QTimer::singleShot(0, this, SIGNAL(pendingRequestFinished())); qWarning() << "Service instanciation failed"; return; } - //deserialize meta object - QByteArray payload = p.d->payload.toByteArray(); - QMetaObject mo; - QMetaObjectBuilder::fromRelocatableData(&mo, 0, payload); - qDebug() << mo.className() << payload.size() << sizeof(mo); - + //deserialize meta object and //create proxy object - QServiceProxy* proxy = new QServiceProxy(payload); + QServiceProxy* proxy = new QServiceProxy(p.d->payload.toByteArray(), this); response->result = reinterpret_cast<void *>(proxy); response->isFinished = true; @@ -190,16 +185,11 @@ void ObjectEndPoint::objectRequest(const QServicePackage& p) } //serialize meta object - bool ok = false; + QByteArray data; + QDataStream stream( &data, QIODevice::WriteOnly | QIODevice::Append ); QMetaObjectBuilder builder(meta); + builder.serialize(stream); - const QByteArray serializedMetaObject = builder.toRelocatableData(&ok); - if (!ok) { - qWarning() << "Cannot serialize QMetaObject"; - dispatch->writePackage(response); - return; - } - //instanciate service object from type register service = m->createObjectInstance(p.d->typeId, serviceInstanceId); if (!service) { @@ -212,23 +202,82 @@ void ObjectEndPoint::objectRequest(const QServicePackage& p) typeIdent = p.d->typeId; response.d->typeId = p.d->typeId; response.d->responseType = QServicePackage::Success; - response.d->payload = QVariant(serializedMetaObject); + response.d->payload = QVariant(data); dispatch->writePackage(response); } } - + +#define QVARIANT_ARG(arg) (arg.isValid? void ObjectEndPoint::methodCall(const QServicePackage& p) { - if (p.d->responseType != QServicePackage::NotAResponse ) { + if (p.d->responseType == QServicePackage::NotAResponse ) { qDebug() << p; - //TODO + QByteArray data = p.d->payload.toByteArray(); + QDataStream stream(&data, QIODevice::ReadOnly); + int metaIndex = -1; + QVariantList args; + stream >> metaIndex; + stream >> args; + + QMetaMethod method = service->metaObject()->method(metaIndex); + const int returnType = QMetaType::type(method.typeName()); + + const char* typenames[] = {0,0,0,0,0,0,0,0,0,0}; + const void* param[] = {0,0,0,0,0,0,0,0,0,0}; + + for(int i=0; i<args.size(); i++) { + typenames[i] = args[i].typeName(); + param[i] = args[i].constData(); + } + + if (returnType == QMetaType::Void) { + + bool result = method.invoke(service, + QGenericArgument(typenames[0], param[0]), + QGenericArgument(typenames[1], param[1]), + QGenericArgument(typenames[2], param[2]), + QGenericArgument(typenames[3], param[3]), + QGenericArgument(typenames[4], param[4]), + QGenericArgument(typenames[5], param[5]), + QGenericArgument(typenames[6], param[6]), + QGenericArgument(typenames[7], param[7]), + QGenericArgument(typenames[8], param[8]), + QGenericArgument(typenames[9], param[9])); + if (!result) + qWarning( "%s::%s cannot be called.", service->metaObject()->className(), method.signature()); + } else { + //TODO + } } else { qDebug() << p; //TODO } } +/*! + Will block if return value expected +*/ +QVariant ObjectEndPoint::invokeRemote(int metaIndex, QVariantList args, int returnType) +{ + QServicePackage p; + p.d = new QServicePackagePrivate(); + p.d->packageType = QServicePackage::MethodCall; + p.d->messageId = QUuid::createUuid(); + + if (returnType == QMetaType::Void) { + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly|QIODevice::Append); + stream << metaIndex << args; + p.d->payload = data; + dispatch->writePackage(p); + } else { + //create response and block for answer + } + + return QVariant(); +} + void ObjectEndPoint::waitForResponse(const QUuid& requestId) { if (openRequests()->contains(requestId) ) { diff --git a/src/serviceframework/ipc/objectendpoint_p.h b/src/serviceframework/ipc/objectendpoint_p.h index 30e999dde0..703a61436e 100644 --- a/src/serviceframework/ipc/objectendpoint_p.h +++ b/src/serviceframework/ipc/objectendpoint_p.h @@ -66,6 +66,8 @@ public: void objectRequest(const QServicePackage& p); void methodCall(const QServicePackage& p); + QVariant invokeRemote(int metaIndex, QVariantList args, int returnType); + Q_SIGNALS: void pendingRequestFinished(); diff --git a/src/serviceframework/ipc/proxyobject.cpp b/src/serviceframework/ipc/proxyobject.cpp index a62d5b5713..156e8668ce 100644 --- a/src/serviceframework/ipc/proxyobject.cpp +++ b/src/serviceframework/ipc/proxyobject.cpp @@ -51,21 +51,35 @@ class QServiceProxyPrivate public: QByteArray metadata; QMetaObject* meta; + ObjectEndPoint* endPoint; }; -QServiceProxy::QServiceProxy(const QByteArray& metadata, QObject* parent) +QServiceProxy::QServiceProxy(const QByteArray& metadata, ObjectEndPoint* endPoint, QObject* parent) : QObject(parent) { + Q_ASSERT(endPoint); d = new QServiceProxyPrivate(); d->metadata = metadata; - d->meta = new QMetaObject(); + d->meta = 0; + d->endPoint = endPoint; - QMetaObjectBuilder::fromRelocatableData(d->meta, 0, d->metadata); + QDataStream stream(d->metadata); + QMetaObjectBuilder builder; + QMap<QByteArray, const QMetaObject*> refs; + + builder.deserialize(stream, refs); + if (stream.status() != QDataStream::Ok) { + qWarning() << "Invalid metaObject for service received"; + } else { + d->meta = builder.toMetaObject(); + qWarning() << "Proxy object for" << d->meta->className() << "created."; + } } QServiceProxy::~QServiceProxy() { - delete d->meta; + if (d->meta) + delete d->meta; delete d; } @@ -78,16 +92,64 @@ const QMetaObject* QServiceProxy::metaObject() const int QServiceProxy::qt_metacall(QMetaObject::Call c, int id, void **a) { id = QObject::qt_metacall(c, id, a); - if (id < 0) + if (id < 0 || !d->meta) return id; - //TODO catch everything bound for remote service object + if (c == QMetaObject::InvokeMetaMethod) { + const int mcount = d->meta->methodCount() - d->meta->methodOffset(); + const int metaIndex = id + d->meta->methodOffset(); + + QMetaMethod method = d->meta->method(metaIndex); + const int returnType = QMetaType::type(method.typeName()); + + //process arguments + const QList<QByteArray> pTypes = method.parameterTypes(); + const int pTypesCount = pTypes.count(); + QVariantList args ; + if (pTypesCount > 10) { + qWarning() << "Cannot call" << method.signature() << ". More than 10 parameter."; + return id; + } + for (int i=0; i < pTypesCount; i++) { + const QByteArray& t = pTypes[i]; + + int variantType = QVariant::nameToType(t); + if (variantType == QVariant::UserType) + variantType = QMetaType::type(t); + + if (variantType == QVariant::Invalid && t == "QVariant") { + args << *reinterpret_cast<const QVariant(*)>(a[i+1]); + } else if ( variantType == 0 ){ + qWarning("Argument %s has unknown type", t.data()); + return id; + } else { + args << QVariant(variantType, a[i+1]); + } + } + + if (returnType == QMetaType::Void) { + //assume we don't have parameter //TODO + d->endPoint->invokeRemote(metaIndex, args, returnType); + } else { + //TODO + qWarning() << "Cannot handle functions with return type yet."; + qWarning() << method.signature(); + } + + + id-=mcount; + } else { + //TODO + qWarning() << "MetaCall type" << c << "not yet handled"; + } return id; } void *QServiceProxy::qt_metacast(const char* className) { - return 0; + if (!className) return 0; + //this object should not be castable to anything but QObject + return QObject::qt_metacast(className); } QTM_END_NAMESPACE diff --git a/src/serviceframework/ipc/proxyobject_p.h b/src/serviceframework/ipc/proxyobject_p.h index 646e2cc5df..d7d9156b47 100644 --- a/src/serviceframework/ipc/proxyobject_p.h +++ b/src/serviceframework/ipc/proxyobject_p.h @@ -43,6 +43,7 @@ #define PROXY_OBJECT_H #include "qmobilityglobal.h" +#include "objectendpoint_p.h" #include <QObject> QTM_BEGIN_NAMESPACE @@ -52,7 +53,7 @@ class QServiceProxy : public QObject { //Note: Do not put Q_OBJECT here public: - QServiceProxy(const QByteArray& metadata, QObject* parent = 0); + QServiceProxy(const QByteArray& metadata, ObjectEndPoint* endpoint, QObject* parent = 0); virtual ~QServiceProxy(); //provide custom Q_OBJECT implementation diff --git a/src/serviceframework/ipc/qmetaobjectbuilder_p.h b/src/serviceframework/ipc/qmetaobjectbuilder_p.h index 22176b42f9..042eec7c4c 100644 --- a/src/serviceframework/ipc/qmetaobjectbuilder_p.h +++ b/src/serviceframework/ipc/qmetaobjectbuilder_p.h @@ -75,7 +75,7 @@ class QMetaPropertyBuilderPrivate; class QMetaEnumBuilder; class QMetaEnumBuilderPrivate; -class QMetaObjectBuilder +class Q_AUTOTEST_EXPORT QMetaObjectBuilder { public: enum AddMember @@ -200,7 +200,7 @@ private: friend class QMetaEnumBuilder; }; -class QMetaMethodBuilder +class Q_AUTOTEST_EXPORT QMetaMethodBuilder { public: QMetaMethodBuilder() : _mobj(0), _index(0) {} @@ -238,7 +238,7 @@ private: QMetaMethodBuilderPrivate *d_func() const; }; -class QMetaPropertyBuilder +class Q_AUTOTEST_EXPORT QMetaPropertyBuilder { public: QMetaPropertyBuilder() : _mobj(0), _index(0) {} @@ -289,7 +289,7 @@ private: QMetaPropertyBuilderPrivate *d_func() const; }; -class QMetaEnumBuilder +class Q_AUTOTEST_EXPORT QMetaEnumBuilder { public: QMetaEnumBuilder() : _mobj(0), _index(0) {} diff --git a/src/serviceframework/ipc/qservicepackage.cpp b/src/serviceframework/ipc/qservicepackage.cpp index ebf426aa0b..45f67a159e 100644 --- a/src/serviceframework/ipc/qservicepackage.cpp +++ b/src/serviceframework/ipc/qservicepackage.cpp @@ -75,8 +75,8 @@ QServicePackage QServicePackage::createResponse() const Q_ASSERT(d->responseType == QServicePackage::NotAResponse); QServicePackage response; response.d = new QServicePackagePrivate(); - response.d->type = d->type; - response.d->id = d->id; + response.d->packageType = d->packageType; + response.d->messageId = d->messageId; response.d->responseType = QServicePackage::Failed; return response; @@ -92,9 +92,9 @@ QDataStream &operator<<(QDataStream &out, const QServicePackage& package) const qint8 valid = package.d ? 1 : 0; out << (qint8) valid; if (valid) { - out << (qint8) package.d->type; + out << (qint8) package.d->packageType; out << (qint8) package.d->responseType; - out << package.d->id; + out << package.d->messageId; out << package.d->typeId; out << package.d->payload; } @@ -126,10 +126,10 @@ QDataStream &operator>>(QDataStream &in, QServicePackage& package) } qint8 data; in >> data; - package.d->type = (QServicePackage::Type) data; + package.d->packageType = (QServicePackage::Type) data; in >> data; package.d->responseType = (QServicePackage::ResponseType) data; - in >> package.d->id; + in >> package.d->messageId; in >> package.d->typeId; in >> package.d->payload; } else { @@ -147,7 +147,7 @@ QDebug operator<<(QDebug dbg, const QServicePackage& p) { if (p.isValid()) { QString type; - switch(p.d->type) { + switch(p.d->packageType) { case QServicePackage::SignalEmission: type = QString("SignalEmission"); break; @@ -162,7 +162,7 @@ QDebug operator<<(QDebug dbg, const QServicePackage& p) } dbg.nospace() << "QServicePackage "; dbg.nospace() << type << " " << p.d->responseType ; dbg.space(); - dbg.nospace() << p.d->id; dbg.space(); + dbg.nospace() << p.d->messageId; dbg.space(); dbg.nospace() << p.d->typeId;dbg.space(); } else { dbg.nospace() << "QServicePackage(invalid)"; diff --git a/src/serviceframework/ipc/qservicepackage_p.h b/src/serviceframework/ipc/qservicepackage_p.h index 65f6df824f..9a9165528c 100644 --- a/src/serviceframework/ipc/qservicepackage_p.h +++ b/src/serviceframework/ipc/qservicepackage_p.h @@ -102,22 +102,22 @@ class QServicePackagePrivate : public QSharedData { public: QServicePackagePrivate() - : type(QServicePackage::ObjectCreation), + : packageType(QServicePackage::ObjectCreation), typeId(QServiceTypeIdent()), payload(QVariant()), - id(QUuid()), responseType(QServicePackage::NotAResponse) + messageId(QUuid()), responseType(QServicePackage::NotAResponse) { } - QServicePackage::Type type; + QServicePackage::Type packageType; QServiceTypeIdent typeId; QVariant payload; - QUuid id; + QUuid messageId; QServicePackage::ResponseType responseType; virtual void clean() { - type = QServicePackage::ObjectCreation; - id = QUuid(); + packageType = QServicePackage::ObjectCreation; + messageId = QUuid(); payload = QVariant(); typeId = QServiceTypeIdent(); responseType = QServicePackage::NotAResponse; diff --git a/src/serviceframework/qservicemanager.cpp b/src/serviceframework/qservicemanager.cpp index 1d6b3f3659..96609cdbf3 100644 --- a/src/serviceframework/qservicemanager.cpp +++ b/src/serviceframework/qservicemanager.cpp @@ -411,10 +411,9 @@ QObject* QServiceManager::loadInterface(const QServiceInterfaceDescriptor& descr .arg(descriptor.minorVersion()).toLatin1(); const QServiceTypeIdent ident(descriptor.interfaceName().toLatin1(), version); QObject* service = QServiceControlPrivate::proxyForService(ident, location); - if (!service) { - qDebug() << "Cannot load remote IPC service"; + if (!service) d->setError(InvalidServiceLocation); - } + //client owns proxy object return service; } diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 0d38ee31d4..62d0867414 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -9,7 +9,8 @@ contains(mobility_modules,serviceframework) { qservicefilter \ qservicemanager \ qabstractsecuritysession \ - qservicecontext + qservicecontext \ + qmetaobjectbuilder # servicedatabase is not compiled into the serviceframework library on symbian, # special handling is needed diff --git a/tests/auto/qmetaobjectbuilder/qmetaobjectbuilder.pro b/tests/auto/qmetaobjectbuilder/qmetaobjectbuilder.pro new file mode 100644 index 0000000000..4cbc03a49e --- /dev/null +++ b/tests/auto/qmetaobjectbuilder/qmetaobjectbuilder.pro @@ -0,0 +1,37 @@ +TARGET = tst_qmetaobjectbuilder +INCLUDEPATH += ../../../src/serviceframework +INCLUDEPATH += ../../../src/serviceframework/ipc + +CONFIG+=testcase + +QT = core + +include(../../../common.pri) + +# Input +SOURCES += tst_qmetaobjectbuilder.cpp + +CONFIG += mobility +MOBILITY = serviceframework + +symbian|wince* { + symbian { + TARGET.CAPABILITY = ALL -TCB + LIBS += -lefsrv + } + wince* { + SFWTEST_PLUGIN_DEPLOY.sources = \ + $$OUTPUT_DIR/build/tests/bin/plugins/tst_sfw_sampleserviceplugin.dll \ + $$OUTPUT_DIR/build/tests/bin/plugins/tst_sfw_sampleserviceplugin2.dll \ + $$OUTPUT_DIR/build/tests/bin/plugins/tst_sfw_testservice2plugin.dll + SFWTEST_PLUGIN_DEPLOY.path = plugins + DEPLOYMENT += SFWTEST_PLUGIN_DEPLOY + DEPLOYMENT_PLUGIN += qsqlite + } + + addFiles.sources = ../../testservice2/xml/testserviceplugin.xml \ + ../../sampleserviceplugin/xml/sampleservice.xml \ + ../../sampleserviceplugin2/xml/sampleservice2.xml + addFiles.path = plugins/xmldata + DEPLOYMENT += addFiles +} diff --git a/tests/auto/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp new file mode 100644 index 0000000000..3ecb890da9 --- /dev/null +++ b/tests/auto/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -0,0 +1,1286 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/qlocale.h> +#include <qmetaobjectbuilder_p.h> + + +QTM_USE_NAMESPACE + +class tst_QMetaObjectBuilder : public QObject +{ + Q_OBJECT +public: + tst_QMetaObjectBuilder() {} + ~tst_QMetaObjectBuilder() {} + +private slots: + void mocVersionCheck(); + void create(); + void className(); + void superClass(); + void flags(); + void method(); + void slot(); + void signal(); + void constructor(); + void property(); + void notifySignal(); + void enumerator(); + void classInfo(); + void relatedMetaObject(); + void staticMetacall(); + void copyMetaObject(); + void serialize(); + void removeNotifySignal(); + +private: + static bool checkForSideEffects + (const QMetaObjectBuilder& builder, + QMetaObjectBuilder::AddMembers members); + static bool sameMetaObject + (const QMetaObject *meta1, const QMetaObject *meta2); +}; + +// Dummy class that has something of every type of thing moc can generate. +class SomethingOfEverything : public QObject +{ + Q_OBJECT + Q_CLASSINFO("ci_foo", "ABC") + Q_CLASSINFO("ci_bar", "DEF") + Q_PROPERTY(QString prop READ prop WRITE setProp NOTIFY propChanged) + Q_PROPERTY(QString prop2 READ prop WRITE setProp) + Q_PROPERTY(SomethingEnum eprop READ eprop) + Q_PROPERTY(SomethingFlagEnum fprop READ fprop) + Q_PROPERTY(QLocale::Language language READ language) + Q_ENUMS(SomethingEnum) + Q_FLAGS(SomethingFlagEnum) +public: + Q_INVOKABLE SomethingOfEverything() {} + ~SomethingOfEverything() {} + + enum SomethingEnum + { + GHI, + JKL = 10 + }; + + enum SomethingFlagEnum + { + XYZ = 1, + UVW = 8 + }; + + Q_INVOKABLE Q_SCRIPTABLE void method1() {} + + QString prop() const { return QString(); } + void setProp(const QString& v) { Q_UNUSED(v); } + + SomethingOfEverything::SomethingEnum eprop() const { return GHI; } + SomethingOfEverything::SomethingFlagEnum fprop() const { return XYZ; } + QLocale::Language language() const { return QLocale::English; } + +public slots: + void slot1(const QString&) {} + void slot2(int, const QString&) {} + +private slots: + void slot3() {} + +protected slots: + Q_SCRIPTABLE void slot4(int) {} + void slot5(int a, const QString& b) { Q_UNUSED(a); Q_UNUSED(b); } + +signals: + void sig1(); + void sig2(int x, const QString& y); + 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. + QCOMPARE(int(QObject::staticMetaObject.d.data[0]), 4); + QCOMPARE(int(staticMetaObject.d.data[0]), 4); +} + +void tst_QMetaObjectBuilder::create() +{ + QMetaObjectBuilder builder; + QVERIFY(builder.className().isEmpty()); + QVERIFY(builder.superClass() == &QObject::staticMetaObject); + QCOMPARE(builder.methodCount(), 0); + QCOMPARE(builder.constructorCount(), 0); + QCOMPARE(builder.propertyCount(), 0); + QCOMPARE(builder.enumeratorCount(), 0); + QCOMPARE(builder.classInfoCount(), 0); + QCOMPARE(builder.relatedMetaObjectCount(), 0); + QVERIFY(builder.staticMetacallFunction() == 0); +} + +void tst_QMetaObjectBuilder::className() +{ + QMetaObjectBuilder builder; + + // Change the class name. + builder.setClassName("Foo"); + QCOMPARE(builder.className(), QByteArray("Foo")); + + // Change it again. + builder.setClassName("Bar"); + QCOMPARE(builder.className(), QByteArray("Bar")); + + // Clone the class name off a static QMetaObject. + builder.addMetaObject(&QObject::staticMetaObject, QMetaObjectBuilder::ClassName); + QCOMPARE(builder.className(), QByteArray("QObject")); + + // Check that nothing else changed. + QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::ClassName)); +} + +void tst_QMetaObjectBuilder::superClass() +{ + QMetaObjectBuilder builder; + + // Change the super class. + builder.setSuperClass(&QObject::staticMetaObject); + QVERIFY(builder.superClass() == &QObject::staticMetaObject); + + // Change it again. + builder.setSuperClass(&staticMetaObject); + QVERIFY(builder.superClass() == &staticMetaObject); + + // Clone the super class off a static QMetaObject. + builder.addMetaObject(&QObject::staticMetaObject, QMetaObjectBuilder::SuperClass); + QVERIFY(builder.superClass() == 0); + builder.addMetaObject(&staticMetaObject, QMetaObjectBuilder::SuperClass); + QVERIFY(builder.superClass() == staticMetaObject.superClass()); + + // Check that nothing else changed. + QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::SuperClass)); +} + +void tst_QMetaObjectBuilder::flags() +{ + QMetaObjectBuilder builder; + + // Check default + QVERIFY(builder.flags() == 0); + + // Set flags + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + QVERIFY(builder.flags() == QMetaObjectBuilder::DynamicMetaObject); +} + +void tst_QMetaObjectBuilder::method() +{ + QMetaObjectBuilder builder; + + // Check null method + QMetaMethodBuilder nullMethod; + QCOMPARE(nullMethod.signature(), QByteArray()); + QVERIFY(nullMethod.methodType() == QMetaMethod::Method); + QVERIFY(nullMethod.returnType().isEmpty()); + QVERIFY(nullMethod.parameterNames().isEmpty()); + QVERIFY(nullMethod.tag().isEmpty()); + QVERIFY(nullMethod.access() == QMetaMethod::Public); + QCOMPARE(nullMethod.attributes(), 0); + QCOMPARE(nullMethod.index(), 0); + + // Add a method and check its attributes. + QMetaMethodBuilder method1 = builder.addMethod("foo(const QString&, int)"); + QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); + QVERIFY(method1.methodType() == QMetaMethod::Method); + QVERIFY(method1.returnType().isEmpty()); + QVERIFY(method1.parameterNames().isEmpty()); + QVERIFY(method1.tag().isEmpty()); + QVERIFY(method1.access() == QMetaMethod::Public); + QCOMPARE(method1.attributes(), 0); + QCOMPARE(method1.index(), 0); + QCOMPARE(builder.methodCount(), 1); + + // Add another method and check again. + QMetaMethodBuilder method2 = builder.addMethod("bar(QString)", "int"); + QCOMPARE(method2.signature(), QByteArray("bar(QString)")); + QVERIFY(method2.methodType() == QMetaMethod::Method); + QCOMPARE(method2.returnType(), QByteArray("int")); + QVERIFY(method2.parameterNames().isEmpty()); + QVERIFY(method2.tag().isEmpty()); + QVERIFY(method2.access() == QMetaMethod::Public); + QCOMPARE(method2.attributes(), 0); + QCOMPARE(method2.index(), 1); + QCOMPARE(builder.methodCount(), 2); + + // Perform index-based lookup. + QCOMPARE(builder.indexOfMethod("foo(const QString&, int)"), 0); + QCOMPARE(builder.indexOfMethod("bar(QString)"), 1); + QCOMPARE(builder.indexOfMethod("baz()"), -1); + + // Modify the attributes on method1. + method1.setReturnType("int"); + method1.setParameterNames(QList<QByteArray>() << "a" << "b"); + method1.setTag("tag"); + method1.setAccess(QMetaMethod::Private); + method1.setAttributes(42); + + // Check that method1 is changed, but method2 is not. + QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); + QVERIFY(method1.methodType() == QMetaMethod::Method); + QCOMPARE(method1.returnType(), QByteArray("int")); + QCOMPARE(method1.parameterNames(), QList<QByteArray>() << "a" << "b"); + QCOMPARE(method1.tag(), QByteArray("tag")); + QVERIFY(method1.access() == QMetaMethod::Private); + QCOMPARE(method1.attributes(), 42); + QCOMPARE(method1.index(), 0); + QCOMPARE(method2.signature(), QByteArray("bar(QString)")); + QVERIFY(method2.methodType() == QMetaMethod::Method); + QCOMPARE(method2.returnType(), QByteArray("int")); + QVERIFY(method2.parameterNames().isEmpty()); + QVERIFY(method2.tag().isEmpty()); + QVERIFY(method2.access() == QMetaMethod::Public); + QCOMPARE(method2.attributes(), 0); + QCOMPARE(method2.index(), 1); + QCOMPARE(builder.methodCount(), 2); + + // Modify the attributes on method2. + method2.setReturnType("QString"); + method2.setParameterNames(QList<QByteArray>() << "c"); + method2.setTag("Q_FOO"); + method2.setAccess(QMetaMethod::Protected); + method2.setAttributes(24); + + // This time check that only method2 changed. + QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); + QVERIFY(method1.methodType() == QMetaMethod::Method); + QCOMPARE(method1.returnType(), QByteArray("int")); + QCOMPARE(method1.parameterNames(), QList<QByteArray>() << "a" << "b"); + QCOMPARE(method1.tag(), QByteArray("tag")); + QVERIFY(method1.access() == QMetaMethod::Private); + QCOMPARE(method1.attributes(), 42); + QCOMPARE(method1.index(), 0); + QCOMPARE(method2.signature(), QByteArray("bar(QString)")); + QVERIFY(method2.methodType() == QMetaMethod::Method); + QCOMPARE(method2.returnType(), QByteArray("QString")); + QCOMPARE(method2.parameterNames(), QList<QByteArray>() << "c"); + QCOMPARE(method2.tag(), QByteArray("Q_FOO")); + QVERIFY(method2.access() == QMetaMethod::Protected); + QCOMPARE(method2.attributes(), 24); + QCOMPARE(method2.index(), 1); + QCOMPARE(builder.methodCount(), 2); + + // Remove method1 and check that method2 becomes index 0. + builder.removeMethod(0); + QCOMPARE(builder.methodCount(), 1); + method2 = builder.method(0); + QCOMPARE(method2.signature(), QByteArray("bar(QString)")); + QVERIFY(method2.methodType() == QMetaMethod::Method); + QCOMPARE(method2.returnType(), QByteArray("QString")); + QCOMPARE(method2.parameterNames(), QList<QByteArray>() << "c"); + QCOMPARE(method2.tag(), QByteArray("Q_FOO")); + QVERIFY(method2.access() == QMetaMethod::Protected); + QCOMPARE(method2.attributes(), 24); + QCOMPARE(method2.index(), 0); + + // Perform index-based lookup again. + QCOMPARE(builder.indexOfMethod("foo(const QString&, int)"), -1); + QCOMPARE(builder.indexOfMethod("bar(QString)"), 0); + QCOMPARE(builder.indexOfMethod("baz()"), -1); + QCOMPARE(builder.method(0).signature(), QByteArray("bar(QString)")); + QCOMPARE(builder.method(9).signature(), QByteArray()); + + // Check that nothing else changed. + QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Methods)); +} + +void tst_QMetaObjectBuilder::slot() +{ + QMetaObjectBuilder builder; + + // Add a slot and check its attributes. + QMetaMethodBuilder method1 = builder.addSlot("foo(const QString&, int)"); + QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); + QVERIFY(method1.methodType() == QMetaMethod::Slot); + QVERIFY(method1.returnType().isEmpty()); + QVERIFY(method1.parameterNames().isEmpty()); + QVERIFY(method1.tag().isEmpty()); + QVERIFY(method1.access() == QMetaMethod::Public); + QCOMPARE(method1.attributes(), 0); + QCOMPARE(method1.index(), 0); + QCOMPARE(builder.methodCount(), 1); + + // Add another slot and check again. + QMetaMethodBuilder method2 = builder.addSlot("bar(QString)"); + QCOMPARE(method2.signature(), QByteArray("bar(QString)")); + QVERIFY(method2.methodType() == QMetaMethod::Slot); + QVERIFY(method2.returnType().isEmpty()); + QVERIFY(method2.parameterNames().isEmpty()); + QVERIFY(method2.tag().isEmpty()); + QVERIFY(method2.access() == QMetaMethod::Public); + QCOMPARE(method2.attributes(), 0); + QCOMPARE(method2.index(), 1); + QCOMPARE(builder.methodCount(), 2); + + // Perform index-based lookup + QCOMPARE(builder.indexOfSlot("foo(const QString &, int)"), 0); + QCOMPARE(builder.indexOfSlot("bar(QString)"), 1); + QCOMPARE(builder.indexOfSlot("baz()"), -1); + + // Check that nothing else changed. + QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Methods)); +} + +void tst_QMetaObjectBuilder::signal() +{ + QMetaObjectBuilder builder; + + // Add a signal and check its attributes. + QMetaMethodBuilder method1 = builder.addSignal("foo(const QString&, int)"); + QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); + QVERIFY(method1.methodType() == QMetaMethod::Signal); + QVERIFY(method1.returnType().isEmpty()); + QVERIFY(method1.parameterNames().isEmpty()); + QVERIFY(method1.tag().isEmpty()); + QVERIFY(method1.access() == QMetaMethod::Protected); + QCOMPARE(method1.attributes(), 0); + QCOMPARE(method1.index(), 0); + QCOMPARE(builder.methodCount(), 1); + + // Add another signal and check again. + QMetaMethodBuilder method2 = builder.addSignal("bar(QString)"); + QCOMPARE(method2.signature(), QByteArray("bar(QString)")); + QVERIFY(method2.methodType() == QMetaMethod::Signal); + QVERIFY(method2.returnType().isEmpty()); + QVERIFY(method2.parameterNames().isEmpty()); + QVERIFY(method2.tag().isEmpty()); + QVERIFY(method2.access() == QMetaMethod::Protected); + QCOMPARE(method2.attributes(), 0); + QCOMPARE(method2.index(), 1); + QCOMPARE(builder.methodCount(), 2); + + // Perform index-based lookup + QCOMPARE(builder.indexOfSignal("foo(const QString &, int)"), 0); + QCOMPARE(builder.indexOfSignal("bar(QString)"), 1); + QCOMPARE(builder.indexOfSignal("baz()"), -1); + + // Check that nothing else changed. + QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Methods)); +} + +void tst_QMetaObjectBuilder::constructor() +{ + QMetaObjectBuilder builder; + + // Add a constructor and check its attributes. + QMetaMethodBuilder ctor1 = builder.addConstructor("foo(const QString&, int)"); + QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)")); + QVERIFY(ctor1.methodType() == QMetaMethod::Constructor); + QVERIFY(ctor1.returnType().isEmpty()); + QVERIFY(ctor1.parameterNames().isEmpty()); + QVERIFY(ctor1.tag().isEmpty()); + QVERIFY(ctor1.access() == QMetaMethod::Public); + QCOMPARE(ctor1.attributes(), 0); + QCOMPARE(ctor1.index(), 0); + QCOMPARE(builder.constructorCount(), 1); + + // Add another constructor and check again. + QMetaMethodBuilder ctor2 = builder.addConstructor("bar(QString)"); + QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); + QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); + QVERIFY(ctor2.returnType().isEmpty()); + QVERIFY(ctor2.parameterNames().isEmpty()); + QVERIFY(ctor2.tag().isEmpty()); + QVERIFY(ctor2.access() == QMetaMethod::Public); + QCOMPARE(ctor2.attributes(), 0); + QCOMPARE(ctor2.index(), 1); + QCOMPARE(builder.constructorCount(), 2); + + // Perform index-based lookup. + QCOMPARE(builder.indexOfConstructor("foo(const QString&, int)"), 0); + QCOMPARE(builder.indexOfConstructor("bar(QString)"), 1); + QCOMPARE(builder.indexOfConstructor("baz()"), -1); + QCOMPARE(builder.constructor(1).signature(), QByteArray("bar(QString)")); + QCOMPARE(builder.constructor(9).signature(), QByteArray()); + + // Modify the attributes on ctor1. + ctor1.setReturnType("int"); + ctor1.setParameterNames(QList<QByteArray>() << "a" << "b"); + ctor1.setTag("tag"); + ctor1.setAccess(QMetaMethod::Private); + ctor1.setAttributes(42); + + // Check that ctor1 is changed, but ctor2 is not. + QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)")); + QVERIFY(ctor1.methodType() == QMetaMethod::Constructor); + QCOMPARE(ctor1.returnType(), QByteArray("int")); + QCOMPARE(ctor1.parameterNames(), QList<QByteArray>() << "a" << "b"); + QCOMPARE(ctor1.tag(), QByteArray("tag")); + QVERIFY(ctor1.access() == QMetaMethod::Private); + QCOMPARE(ctor1.attributes(), 42); + QCOMPARE(ctor1.index(), 0); + QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); + QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); + QVERIFY(ctor2.returnType().isEmpty()); + QVERIFY(ctor2.parameterNames().isEmpty()); + QVERIFY(ctor2.tag().isEmpty()); + QVERIFY(ctor2.access() == QMetaMethod::Public); + QCOMPARE(ctor2.attributes(), 0); + QCOMPARE(ctor2.index(), 1); + QCOMPARE(builder.constructorCount(), 2); + + // Modify the attributes on ctor2. + ctor2.setReturnType("QString"); + ctor2.setParameterNames(QList<QByteArray>() << "c"); + ctor2.setTag("Q_FOO"); + ctor2.setAccess(QMetaMethod::Protected); + ctor2.setAttributes(24); + + // This time check that only ctor2 changed. + QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)")); + QVERIFY(ctor1.methodType() == QMetaMethod::Constructor); + QCOMPARE(ctor1.returnType(), QByteArray("int")); + QCOMPARE(ctor1.parameterNames(), QList<QByteArray>() << "a" << "b"); + QCOMPARE(ctor1.tag(), QByteArray("tag")); + QVERIFY(ctor1.access() == QMetaMethod::Private); + QCOMPARE(ctor1.attributes(), 42); + QCOMPARE(ctor1.index(), 0); + QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); + QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); + QCOMPARE(ctor2.returnType(), QByteArray("QString")); + QCOMPARE(ctor2.parameterNames(), QList<QByteArray>() << "c"); + QCOMPARE(ctor2.tag(), QByteArray("Q_FOO")); + QVERIFY(ctor2.access() == QMetaMethod::Protected); + QCOMPARE(ctor2.attributes(), 24); + QCOMPARE(ctor2.index(), 1); + QCOMPARE(builder.constructorCount(), 2); + + // Remove ctor1 and check that ctor2 becomes index 0. + builder.removeConstructor(0); + QCOMPARE(builder.constructorCount(), 1); + ctor2 = builder.constructor(0); + QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); + QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); + QCOMPARE(ctor2.returnType(), QByteArray("QString")); + QCOMPARE(ctor2.parameterNames(), QList<QByteArray>() << "c"); + QCOMPARE(ctor2.tag(), QByteArray("Q_FOO")); + QVERIFY(ctor2.access() == QMetaMethod::Protected); + QCOMPARE(ctor2.attributes(), 24); + QCOMPARE(ctor2.index(), 0); + + // Perform index-based lookup again. + QCOMPARE(builder.indexOfConstructor("foo(const QString&, int)"), -1); + QCOMPARE(builder.indexOfConstructor("bar(QString)"), 0); + QCOMPARE(builder.indexOfConstructor("baz()"), -1); + + // Add constructor from prototype + QMetaMethod prototype = SomethingOfEverything::staticMetaObject.constructor(0); + QMetaMethodBuilder prototypeConstructor = builder.addMethod(prototype); + QCOMPARE(builder.constructorCount(), 2); + + QCOMPARE(prototypeConstructor.signature(), QByteArray("SomethingOfEverything()")); + QVERIFY(prototypeConstructor.methodType() == QMetaMethod::Constructor); + QCOMPARE(prototypeConstructor.returnType(), QByteArray()); + QVERIFY(prototypeConstructor.access() == QMetaMethod::Public); + QCOMPARE(prototypeConstructor.index(), 1); + + // Check that nothing else changed. + QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Constructors)); +} + +void tst_QMetaObjectBuilder::property() +{ + QMetaObjectBuilder builder; + + // Null property builder + QMetaPropertyBuilder nullProp; + QCOMPARE(nullProp.name(), QByteArray()); + QCOMPARE(nullProp.type(), QByteArray()); + QVERIFY(!nullProp.hasNotifySignal()); + QVERIFY(!nullProp.isReadable()); + QVERIFY(!nullProp.isWritable()); + QVERIFY(!nullProp.isResettable()); + QVERIFY(!nullProp.isDesignable()); + QVERIFY(!nullProp.isScriptable()); + QVERIFY(!nullProp.isStored()); + QVERIFY(!nullProp.isEditable()); + QVERIFY(!nullProp.isUser()); + QVERIFY(!nullProp.hasStdCppSet()); + QVERIFY(!nullProp.isEnumOrFlag()); + QVERIFY(!nullProp.isDynamic()); + QCOMPARE(nullProp.index(), 0); + + // Add a property and check its attributes. + QMetaPropertyBuilder prop1 = builder.addProperty("foo", "const QString &"); + QCOMPARE(prop1.name(), QByteArray("foo")); + QCOMPARE(prop1.type(), QByteArray("QString")); + QVERIFY(!prop1.hasNotifySignal()); + QVERIFY(prop1.isReadable()); + QVERIFY(prop1.isWritable()); + QVERIFY(!prop1.isResettable()); + QVERIFY(!prop1.isDesignable()); + QVERIFY(!prop1.isScriptable()); + QVERIFY(!prop1.isStored()); + QVERIFY(!prop1.isEditable()); + QVERIFY(!prop1.isUser()); + QVERIFY(!prop1.hasStdCppSet()); + QVERIFY(!prop1.isEnumOrFlag()); + QVERIFY(!prop1.isDynamic()); + QCOMPARE(prop1.index(), 0); + QCOMPARE(builder.propertyCount(), 1); + + // Add another property and check again. + QMetaPropertyBuilder prop2 = builder.addProperty("bar", "int"); + QCOMPARE(prop2.name(), QByteArray("bar")); + QCOMPARE(prop2.type(), QByteArray("int")); + QVERIFY(!prop2.hasNotifySignal()); + QVERIFY(prop2.isReadable()); + QVERIFY(prop2.isWritable()); + QVERIFY(!prop2.isResettable()); + QVERIFY(!prop2.isDesignable()); + QVERIFY(!prop2.isScriptable()); + QVERIFY(!prop2.isStored()); + QVERIFY(!prop2.isEditable()); + QVERIFY(!prop2.isUser()); + QVERIFY(!prop2.hasStdCppSet()); + QVERIFY(!prop2.isEnumOrFlag()); + QVERIFY(!prop2.isDynamic()); + QCOMPARE(prop2.index(), 1); + QCOMPARE(builder.propertyCount(), 2); + + // Perform index-based lookup. + QCOMPARE(builder.indexOfProperty("foo"), 0); + QCOMPARE(builder.indexOfProperty("bar"), 1); + QCOMPARE(builder.indexOfProperty("baz"), -1); + QCOMPARE(builder.property(1).name(), QByteArray("bar")); + QCOMPARE(builder.property(9).name(), QByteArray()); + + // Modify the attributes on prop1. + prop1.setReadable(false); + prop1.setWritable(false); + prop1.setResettable(true); + prop1.setDesignable(true); + prop1.setScriptable(true); + prop1.setStored(true); + prop1.setEditable(true); + prop1.setUser(true); + prop1.setStdCppSet(true); + prop1.setEnumOrFlag(true); + prop1.setDynamic(true); + + // Check that prop1 is changed, but prop2 is not. + QCOMPARE(prop1.name(), QByteArray("foo")); + QCOMPARE(prop1.type(), QByteArray("QString")); + QVERIFY(!prop1.isReadable()); + QVERIFY(!prop1.isWritable()); + QVERIFY(prop1.isResettable()); + QVERIFY(prop1.isDesignable()); + QVERIFY(prop1.isScriptable()); + QVERIFY(prop1.isStored()); + QVERIFY(prop1.isEditable()); + QVERIFY(prop1.isUser()); + QVERIFY(prop1.hasStdCppSet()); + QVERIFY(prop1.isEnumOrFlag()); + QVERIFY(prop1.isDynamic()); + QVERIFY(prop2.isReadable()); + QVERIFY(prop2.isWritable()); + QCOMPARE(prop2.name(), QByteArray("bar")); + QCOMPARE(prop2.type(), QByteArray("int")); + QVERIFY(!prop2.isResettable()); + QVERIFY(!prop2.isDesignable()); + QVERIFY(!prop2.isScriptable()); + QVERIFY(!prop2.isStored()); + QVERIFY(!prop2.isEditable()); + QVERIFY(!prop2.isUser()); + QVERIFY(!prop2.hasStdCppSet()); + QVERIFY(!prop2.isEnumOrFlag()); + QVERIFY(!prop2.isDynamic()); + + // Remove prop1 and check that prop2 becomes index 0. + builder.removeProperty(0); + QCOMPARE(builder.propertyCount(), 1); + prop2 = builder.property(0); + QCOMPARE(prop2.name(), QByteArray("bar")); + QCOMPARE(prop2.type(), QByteArray("int")); + QVERIFY(!prop2.isResettable()); + QVERIFY(!prop2.isDesignable()); + QVERIFY(!prop2.isScriptable()); + QVERIFY(!prop2.isStored()); + QVERIFY(!prop2.isEditable()); + QVERIFY(!prop2.isUser()); + QVERIFY(!prop2.hasStdCppSet()); + QVERIFY(!prop2.isEnumOrFlag()); + QVERIFY(!prop2.isDynamic()); + QCOMPARE(prop2.index(), 0); + + // Perform index-based lookup again. + QCOMPARE(builder.indexOfProperty("foo"), -1); + QCOMPARE(builder.indexOfProperty("bar"), 0); + QCOMPARE(builder.indexOfProperty("baz"), -1); + + // Check for side-effects between the flags on prop2. + // Setting a flag to true shouldn't set any of the others to true. + // This checks for cut-and-paste bugs in the implementation where + // the flag code was pasted but the flag name was not changed. +#define CLEAR_FLAGS() \ + do { \ + prop2.setReadable(false); \ + prop2.setWritable(false); \ + prop2.setResettable(false); \ + prop2.setDesignable(false); \ + prop2.setScriptable(false); \ + prop2.setStored(false); \ + prop2.setEditable(false); \ + prop2.setUser(false); \ + prop2.setStdCppSet(false); \ + prop2.setEnumOrFlag(false); \ + prop2.setDynamic(false); \ + } while (0) +#define COUNT_FLAGS() \ + ((prop2.isReadable() ? 1 : 0) + \ + (prop2.isWritable() ? 1 : 0) + \ + (prop2.isResettable() ? 1 : 0) + \ + (prop2.isDesignable() ? 1 : 0) + \ + (prop2.isScriptable() ? 1 : 0) + \ + (prop2.isStored() ? 1 : 0) + \ + (prop2.isEditable() ? 1 : 0) + \ + (prop2.isUser() ? 1 : 0) + \ + (prop2.hasStdCppSet() ? 1 : 0) + \ + (prop2.isEnumOrFlag() ? 1 : 0) + \ + (prop2.isDynamic() ? 1 : 0)) +#define CHECK_FLAG(setFunc,isFunc) \ + do { \ + CLEAR_FLAGS(); \ + QCOMPARE(COUNT_FLAGS(), 0); \ + prop2.setFunc(true); \ + QVERIFY(prop2.isFunc()); \ + QCOMPARE(COUNT_FLAGS(), 1); \ + } while (0) + CHECK_FLAG(setReadable, isReadable); + CHECK_FLAG(setWritable, isWritable); + CHECK_FLAG(setResettable, isResettable); + CHECK_FLAG(setDesignable, isDesignable); + CHECK_FLAG(setScriptable, isScriptable); + CHECK_FLAG(setStored, isStored); + CHECK_FLAG(setEditable, isEditable); + CHECK_FLAG(setUser, isUser); + CHECK_FLAG(setStdCppSet, hasStdCppSet); + CHECK_FLAG(setEnumOrFlag, isEnumOrFlag); + CHECK_FLAG(setDynamic, isDynamic); + + // Check that nothing else changed. + QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Properties)); + + // Add property from prototype + QMetaProperty prototype = SomethingOfEverything::staticMetaObject.property(1); + QVERIFY(prototype.name() == QByteArray("prop")); + QMetaPropertyBuilder prototypeProp = builder.addProperty(prototype); + QCOMPARE(prototypeProp.name(), QByteArray("prop")); + QVERIFY(prototypeProp.hasNotifySignal()); + QCOMPARE(prototypeProp.notifySignal().signature(), QByteArray("propChanged(QString)")); + QCOMPARE(builder.methodCount(), 1); + QCOMPARE(builder.method(0).signature(), QByteArray("propChanged(QString)")); +} + +void tst_QMetaObjectBuilder::notifySignal() +{ + QMetaObjectBuilder builder; + + QMetaPropertyBuilder prop = builder.addProperty("foo", "const QString &"); + builder.addSlot("setFoo(QString)"); + QMetaMethodBuilder notify = builder.addSignal("fooChanged(QString)"); + + QVERIFY(!prop.hasNotifySignal()); + QCOMPARE(prop.notifySignal().index(), 0); + + prop.setNotifySignal(notify); + QVERIFY(prop.hasNotifySignal()); + QCOMPARE(prop.notifySignal().index(), 1); + + prop.setNotifySignal(QMetaMethodBuilder()); + QVERIFY(!prop.hasNotifySignal()); + QCOMPARE(prop.notifySignal().index(), 0); + + prop.setNotifySignal(notify); + prop.removeNotifySignal(); + QVERIFY(!prop.hasNotifySignal()); + QCOMPARE(prop.notifySignal().index(), 0); + + QCOMPARE(builder.methodCount(), 2); + QCOMPARE(builder.propertyCount(), 1); + + // Check that nothing else changed except methods and properties. + QVERIFY(checkForSideEffects + (builder, QMetaObjectBuilder::Methods | QMetaObjectBuilder::Properties)); +} + +void tst_QMetaObjectBuilder::enumerator() +{ + QMetaObjectBuilder builder; + + // Add an enumerator and check its attributes. + QMetaEnumBuilder enum1 = builder.addEnumerator("foo"); + QCOMPARE(enum1.name(), QByteArray("foo")); + QVERIFY(!enum1.isFlag()); + QCOMPARE(enum1.keyCount(), 0); + QCOMPARE(enum1.index(), 0); + QCOMPARE(builder.enumeratorCount(), 1); + + // Add another enumerator and check again. + QMetaEnumBuilder enum2 = builder.addEnumerator("bar"); + QCOMPARE(enum2.name(), QByteArray("bar")); + QVERIFY(!enum2.isFlag()); + QCOMPARE(enum2.keyCount(), 0); + QCOMPARE(enum2.index(), 1); + QCOMPARE(builder.enumeratorCount(), 2); + + // Perform index-based lookup. + QCOMPARE(builder.indexOfEnumerator("foo"), 0); + QCOMPARE(builder.indexOfEnumerator("bar"), 1); + QCOMPARE(builder.indexOfEnumerator("baz"), -1); + QCOMPARE(builder.enumerator(1).name(), QByteArray("bar")); + QCOMPARE(builder.enumerator(9).name(), QByteArray()); + + // Modify the attributes on enum1. + enum1.setIsFlag(true); + QCOMPARE(enum1.addKey("ABC", 0), 0); + QCOMPARE(enum1.addKey("DEF", 1), 1); + QCOMPARE(enum1.addKey("GHI", -1), 2); + + // Check that enum1 is changed, but enum2 is not. + QCOMPARE(enum1.name(), QByteArray("foo")); + QVERIFY(enum1.isFlag()); + QCOMPARE(enum1.keyCount(), 3); + QCOMPARE(enum1.index(), 0); + QCOMPARE(enum1.key(0), QByteArray("ABC")); + QCOMPARE(enum1.key(1), QByteArray("DEF")); + QCOMPARE(enum1.key(2), QByteArray("GHI")); + QCOMPARE(enum1.key(3), QByteArray()); + QCOMPARE(enum1.value(0), 0); + QCOMPARE(enum1.value(1), 1); + QCOMPARE(enum1.value(2), -1); + QCOMPARE(enum2.name(), QByteArray("bar")); + QVERIFY(!enum2.isFlag()); + QCOMPARE(enum2.keyCount(), 0); + QCOMPARE(enum2.index(), 1); + + // Modify the attributes on enum2. + enum2.setIsFlag(true); + QCOMPARE(enum2.addKey("XYZ", 10), 0); + QCOMPARE(enum2.addKey("UVW", 19), 1); + + // This time check that only method2 changed. + QCOMPARE(enum1.name(), QByteArray("foo")); + QVERIFY(enum1.isFlag()); + QCOMPARE(enum1.keyCount(), 3); + QCOMPARE(enum1.index(), 0); + QCOMPARE(enum1.key(0), QByteArray("ABC")); + QCOMPARE(enum1.key(1), QByteArray("DEF")); + QCOMPARE(enum1.key(2), QByteArray("GHI")); + QCOMPARE(enum1.key(3), QByteArray()); + QCOMPARE(enum1.value(0), 0); + QCOMPARE(enum1.value(1), 1); + QCOMPARE(enum1.value(2), -1); + QCOMPARE(enum2.name(), QByteArray("bar")); + QVERIFY(enum2.isFlag()); + QCOMPARE(enum2.keyCount(), 2); + QCOMPARE(enum2.index(), 1); + QCOMPARE(enum2.key(0), QByteArray("XYZ")); + QCOMPARE(enum2.key(1), QByteArray("UVW")); + QCOMPARE(enum2.key(2), QByteArray()); + QCOMPARE(enum2.value(0), 10); + QCOMPARE(enum2.value(1), 19); + + // Remove enum1 key + enum1.removeKey(2); + QCOMPARE(enum1.name(), QByteArray("foo")); + QVERIFY(enum1.isFlag()); + QCOMPARE(enum1.keyCount(), 2); + QCOMPARE(enum1.index(), 0); + QCOMPARE(enum1.key(0), QByteArray("ABC")); + QCOMPARE(enum1.key(1), QByteArray("DEF")); + QCOMPARE(enum1.key(2), QByteArray()); + QCOMPARE(enum1.value(0), 0); + QCOMPARE(enum1.value(1), 1); + QCOMPARE(enum1.value(2), -1); + QCOMPARE(enum2.name(), QByteArray("bar")); + QVERIFY(enum2.isFlag()); + QCOMPARE(enum2.keyCount(), 2); + QCOMPARE(enum2.index(), 1); + QCOMPARE(enum2.key(0), QByteArray("XYZ")); + QCOMPARE(enum2.key(1), QByteArray("UVW")); + QCOMPARE(enum2.key(2), QByteArray()); + QCOMPARE(enum2.value(0), 10); + QCOMPARE(enum2.value(1), 19); + + // Remove enum1 and check that enum2 becomes index 0. + builder.removeEnumerator(0); + QCOMPARE(builder.enumeratorCount(), 1); + enum2 = builder.enumerator(0); + QCOMPARE(enum2.name(), QByteArray("bar")); + QVERIFY(enum2.isFlag()); + QCOMPARE(enum2.keyCount(), 2); + QCOMPARE(enum2.index(), 0); + QCOMPARE(enum2.key(0), QByteArray("XYZ")); + QCOMPARE(enum2.key(1), QByteArray("UVW")); + QCOMPARE(enum2.key(2), QByteArray()); + QCOMPARE(enum2.value(0), 10); + QCOMPARE(enum2.value(1), 19); + + // Perform index-based lookup again. + QCOMPARE(builder.indexOfEnumerator("foo"), -1); + QCOMPARE(builder.indexOfEnumerator("bar"), 0); + QCOMPARE(builder.indexOfEnumerator("baz"), -1); + + // Check that nothing else changed. + QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Enumerators)); +} + +void tst_QMetaObjectBuilder::classInfo() +{ + QMetaObjectBuilder builder; + + // Add two items of class information and check their attributes. + QCOMPARE(builder.addClassInfo("foo", "value1"), 0); + QCOMPARE(builder.addClassInfo("bar", "value2"), 1); + QCOMPARE(builder.classInfoName(0), QByteArray("foo")); + QCOMPARE(builder.classInfoValue(0), QByteArray("value1")); + QCOMPARE(builder.classInfoName(1), QByteArray("bar")); + QCOMPARE(builder.classInfoValue(1), QByteArray("value2")); + QCOMPARE(builder.classInfoName(9), QByteArray()); + QCOMPARE(builder.classInfoValue(9), QByteArray()); + QCOMPARE(builder.classInfoCount(), 2); + + // Perform index-based lookup. + QCOMPARE(builder.indexOfClassInfo("foo"), 0); + QCOMPARE(builder.indexOfClassInfo("bar"), 1); + QCOMPARE(builder.indexOfClassInfo("baz"), -1); + + // Remove the first one and check again. + builder.removeClassInfo(0); + QCOMPARE(builder.classInfoName(0), QByteArray("bar")); + QCOMPARE(builder.classInfoValue(0), QByteArray("value2")); + QCOMPARE(builder.classInfoCount(), 1); + + // Perform index-based lookup again. + QCOMPARE(builder.indexOfClassInfo("foo"), -1); + QCOMPARE(builder.indexOfClassInfo("bar"), 0); + QCOMPARE(builder.indexOfClassInfo("baz"), -1); + + // Check that nothing else changed. + QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::ClassInfos)); +} + +void tst_QMetaObjectBuilder::relatedMetaObject() +{ + QMetaObjectBuilder builder; + + // Add two related meta objects and check their attributes. + QCOMPARE(builder.addRelatedMetaObject(&QObject::staticMetaObject), 0); + QCOMPARE(builder.addRelatedMetaObject(&staticMetaObject), 1); + QVERIFY(builder.relatedMetaObject(0) == &QObject::staticMetaObject); + QVERIFY(builder.relatedMetaObject(1) == &staticMetaObject); + QCOMPARE(builder.relatedMetaObjectCount(), 2); + + // Remove the first one and check again. + builder.removeRelatedMetaObject(0); + QVERIFY(builder.relatedMetaObject(0) == &staticMetaObject); + QCOMPARE(builder.relatedMetaObjectCount(), 1); + + // Check that nothing else changed. + QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::RelatedMetaObjects)); +} + +static int smetacall(QMetaObject::Call, int, void **) +{ + return 0; +} + +void tst_QMetaObjectBuilder::staticMetacall() +{ + QMetaObjectBuilder builder; + QVERIFY(!builder.staticMetacallFunction()); + builder.setStaticMetacallFunction(smetacall); + QVERIFY(builder.staticMetacallFunction() == smetacall); + QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::StaticMetacall)); +} + +// Copy the entire contents of a static QMetaObject and then check +// that QMetaObjectBuilder will produce an exact copy as output. +void tst_QMetaObjectBuilder::copyMetaObject() +{ + QMetaObjectBuilder builder(&QObject::staticMetaObject); + QMetaObject *meta = builder.toMetaObject(); + QVERIFY(sameMetaObject(meta, &QObject::staticMetaObject)); + qFree(meta); + + QMetaObjectBuilder builder2(&staticMetaObject); + meta = builder2.toMetaObject(); + QVERIFY(sameMetaObject(meta, &staticMetaObject)); + qFree(meta); + + QMetaObjectBuilder builder3(&SomethingOfEverything::staticMetaObject); + meta = builder3.toMetaObject(); + QVERIFY(sameMetaObject(meta, &SomethingOfEverything::staticMetaObject)); + qFree(meta); + + + //copy via stream + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly | QIODevice::Append); + builder3.serialize(stream); + + QMetaObjectBuilder builder4; + QDataStream stream2(data); + QMap<QByteArray, const QMetaObject *> references; + references.insert(QByteArray("QLocale"), &QLocale::staticMetaObject); + builder4.deserialize(stream2, references); + builder4.setStaticMetacallFunction(builder.staticMetacallFunction()); + QMetaObject *streamedMeta = builder4.toMetaObject(); + + QVERIFY(sameMetaObject(streamedMeta, &SomethingOfEverything::staticMetaObject)); + + qFree(streamedMeta); + + bool ok = false; + QByteArray array = builder3.toRelocatableData(&ok); + QVERIFY(ok); + QMetaObject* meta1 = new QMetaObject(); + QMetaObjectBuilder::fromRelocatableData(meta1, 0, array); + QVERIFY(sameMetaObject(meta1, &SomethingOfEverything::staticMetaObject)); + + delete meta1; +} + +// Serialize and deserialize a meta object and check that +// it round-trips to the exact same value. +void tst_QMetaObjectBuilder::serialize() +{ + // Full QMetaObjectBuilder + { + QMetaObjectBuilder builder(&SomethingOfEverything::staticMetaObject); + QMetaObject *meta = builder.toMetaObject(); + + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly | QIODevice::Append); + builder.serialize(stream); + + QMetaObjectBuilder builder2; + QDataStream stream2(data); + QMap<QByteArray, const QMetaObject *> references; + references.insert(QByteArray("QLocale"), &QLocale::staticMetaObject); + builder2.deserialize(stream2, references); + builder2.setStaticMetacallFunction(builder.staticMetacallFunction()); + QMetaObject *meta2 = builder2.toMetaObject(); + + QVERIFY(sameMetaObject(meta, meta2)); + qFree(meta); + qFree(meta2); + } + + // Partial QMetaObjectBuilder + { + QMetaObjectBuilder builder; + builder.setClassName("Test"); + builder.addProperty("foo", "int"); + + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly | QIODevice::Append); + builder.serialize(stream); + + QMetaObjectBuilder builder2; + QDataStream stream2(data); + builder2.deserialize(stream2, QMap<QByteArray, const QMetaObject *>()); + + QCOMPARE(builder.superClass(), builder2.superClass()); + QCOMPARE(builder.className(), builder2.className()); + QCOMPARE(builder.propertyCount(), builder2.propertyCount()); + QCOMPARE(builder.property(0).name(), builder2.property(0).name()); + QCOMPARE(builder.property(0).type(), builder2.property(0).type()); + } +} + +// Check that removing a method updates notify signals appropriately +void tst_QMetaObjectBuilder::removeNotifySignal() +{ + QMetaObjectBuilder builder; + + QMetaMethodBuilder method1 = builder.addSignal("foo(const QString&, int)"); + QMetaMethodBuilder method2 = builder.addSignal("bar(QString)"); + + // Setup property + QMetaPropertyBuilder prop = builder.addProperty("prop", "const QString &"); + prop.setNotifySignal(method2); + QVERIFY(prop.hasNotifySignal()); + QCOMPARE(prop.notifySignal().index(), 1); + + // Remove non-notify signal + builder.removeMethod(0); + QVERIFY(prop.hasNotifySignal()); + QCOMPARE(prop.notifySignal().index(), 0); + + // Remove notify signal + builder.removeMethod(0); + QVERIFY(!prop.hasNotifySignal()); +} + +// Check that the only changes to a "builder" relative to the default +// state is specified by "members". +bool tst_QMetaObjectBuilder::checkForSideEffects + (const QMetaObjectBuilder& builder, + QMetaObjectBuilder::AddMembers members) +{ + if ((members & QMetaObjectBuilder::ClassName) == 0) { + if (!builder.className().isEmpty()) + return false; + } + + if ((members & QMetaObjectBuilder::SuperClass) == 0) { + if (builder.superClass() != &QObject::staticMetaObject) + return false; + } + + if ((members & QMetaObjectBuilder::Methods) == 0) { + if (builder.methodCount() != 0) + return false; + } + + if ((members & QMetaObjectBuilder::Constructors) == 0) { + if (builder.constructorCount() != 0) + return false; + } + + if ((members & QMetaObjectBuilder::Properties) == 0) { + if (builder.propertyCount() != 0) + return false; + } + + if ((members & QMetaObjectBuilder::Enumerators) == 0) { + if (builder.enumeratorCount() != 0) + return false; + } + + if ((members & QMetaObjectBuilder::ClassInfos) == 0) { + if (builder.classInfoCount() != 0) + return false; + } + + if ((members & QMetaObjectBuilder::RelatedMetaObjects) == 0) { + if (builder.relatedMetaObjectCount() != 0) + return false; + } + + if ((members & QMetaObjectBuilder::StaticMetacall) == 0) { + if (builder.staticMetacallFunction() != 0) + return false; + } + + return true; +} + +static bool sameMethod(const QMetaMethod& method1, const QMetaMethod& method2) +{ + if (QByteArray(method1.signature()) != QByteArray(method2.signature())) + return false; + + if (QByteArray(method1.typeName()) != QByteArray(method2.typeName())) + return false; + + if (method1.parameterNames() != method2.parameterNames()) + return false; + + if (QByteArray(method1.tag()) != QByteArray(method2.tag())) + return false; + + if (method1.access() != method2.access()) + return false; + + if (method1.methodType() != method2.methodType()) + return false; + + if (method1.attributes() != method2.attributes()) + return false; + + return true; +} + +static bool sameProperty(const QMetaProperty& prop1, const QMetaProperty& prop2) +{ + if (QByteArray(prop1.name()) != QByteArray(prop2.name())) + return false; + + if (QByteArray(prop1.typeName()) != QByteArray(prop2.typeName())) + return false; + + if (prop1.isReadable() != prop2.isReadable() || + prop1.isWritable() != prop2.isWritable() || + prop1.isResettable() != prop2.isResettable() || + prop1.isDesignable() != prop2.isDesignable() || + prop1.isScriptable() != prop2.isScriptable() || + prop1.isStored() != prop2.isStored() || + prop1.isEditable() != prop2.isEditable() || + prop1.isUser() != prop2.isUser() || + prop1.isFlagType() != prop2.isFlagType() || + prop1.isEnumType() != prop2.isEnumType() || + prop1.hasNotifySignal() != prop2.hasNotifySignal() || + prop1.hasStdCppSet() != prop2.hasStdCppSet()) + return false; + + if (prop1.hasNotifySignal()) { + if (prop1.notifySignalIndex() != prop2.notifySignalIndex()) + return false; + } + + return true; +} + +static bool sameEnumerator(const QMetaEnum& enum1, const QMetaEnum& enum2) +{ + if (QByteArray(enum1.name()) != QByteArray(enum2.name())) + return false; + + if (enum1.isFlag() != enum2.isFlag()) + return false; + + if (enum1.keyCount() != enum2.keyCount()) + return false; + + for (int index = 0; index < enum1.keyCount(); ++index) { + if (QByteArray(enum1.key(index)) != QByteArray(enum2.key(index))) + return false; + if (enum1.value(index) != enum2.value(index)) + return false; + } + + if (QByteArray(enum1.scope()) != QByteArray(enum2.scope())) + return false; + + return true; +} + +// Determine if two meta objects are identical. +bool tst_QMetaObjectBuilder::sameMetaObject + (const QMetaObject *meta1, const QMetaObject *meta2) +{ + int index; + + if (strcmp(meta1->className(), meta2->className()) != 0) + return false; + + if (meta1->superClass() != meta2->superClass()) + return false; + + if (meta1->constructorCount() != meta2->constructorCount() || + meta1->methodCount() != meta2->methodCount() || + meta1->enumeratorCount() != meta2->enumeratorCount() || + meta1->propertyCount() != meta2->propertyCount() || + meta1->classInfoCount() != meta2->classInfoCount()) + return false; + + for (index = 0; index < meta1->constructorCount(); ++index) { + if (!sameMethod(meta1->constructor(index), meta2->constructor(index))) + return false; + } + + for (index = 0; index < meta1->methodCount(); ++index) { + if (!sameMethod(meta1->method(index), meta2->method(index))) + return false; + } + + for (index = 0; index < meta1->propertyCount(); ++index) { + if (!sameProperty(meta1->property(index), meta2->property(index))) + return false; + } + + for (index = 0; index < meta1->enumeratorCount(); ++index) { + if (!sameEnumerator(meta1->enumerator(index), meta2->enumerator(index))) + return false; + } + + for (index = 0; index < meta1->classInfoCount(); ++index) { + if (QByteArray(meta1->classInfo(index).name()) != + QByteArray(meta2->classInfo(index).name())) + return false; + if (QByteArray(meta1->classInfo(index).value()) != + QByteArray(meta2->classInfo(index).value())) + return false; + } + + const QMetaObject **objects1 = 0; + const QMetaObject **objects2 = 0; + if (meta1->d.data[0] == meta2->d.data[0] && meta1->d.data[0] >= 2) { + QMetaObjectExtraData *extra1 = (QMetaObjectExtraData *)(meta1->d.extradata); + QMetaObjectExtraData *extra2 = (QMetaObjectExtraData *)(meta2->d.extradata); + if (extra1 && !extra2) + return false; + if (extra2 && !extra1) + return false; + if (extra1 && extra2) { + if (extra1->static_metacall != extra2->static_metacall) + return false; + objects1 = extra1->objects; + objects2 = extra1->objects; + } + } else if (meta1->d.data[0] == meta2->d.data[0] && meta1->d.data[0] == 1) { + objects1 = (const QMetaObject **)(meta1->d.extradata); + objects2 = (const QMetaObject **)(meta2->d.extradata); + } + if (objects1 && !objects2) + return false; + if (objects2 && !objects1) + return false; + if (objects1 && objects2) { + while (*objects1 != 0 && *objects2 != 0) { + if (*objects1 != *objects2) + return false; + ++objects1; + ++objects2; + } + } + + return true; +} + +QTEST_MAIN(tst_QMetaObjectBuilder) + +#include "tst_qmetaobjectbuilder.moc" |