/**************************************************************************** ** ** Copyright (C) 2010 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 #include #include 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. int version = int(QObject::staticMetaObject.d.data[0]); QVERIFY(version == 4 || version == 5); version = int(staticMetaObject.d.data[0]); QVERIFY(version == 4 || version == 5); } 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() << "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() << "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() << "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() << "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() << "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() << "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() << "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() << "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() << "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() << "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() << "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() << "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(false); 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); } // 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 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()); 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"