Revision control

Copy as Markdown

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QSignalSpy>
#include <QtCore/qlocale.h>
#include <private/qmetaobjectbuilder_p.h>
class tst_QMetaObjectBuilder : public QObject
{
Q_OBJECT
private slots:
void create();
void className();
void superClass();
void flags();
void method();
void slot();
void signal();
void signalAdditionOrderDoesNotMatter();
void constructor();
void property();
void variantProperty();
void notifySignal();
void enumerator();
void enumProperty();
void classInfo();
void relatedMetaObject();
void staticMetacall();
void copyMetaObject();
void copyMetaObject_metaProperty();
void removeNotifySignal();
void usage_signal();
void usage_property();
void usage_slot();
void usage_method();
void usage_constructor();
void usage_connect();
void usage_templateConnect();
void classNameFirstInStringData();
void propertyMetaType();
void enumCloning();
void ownMetaTypeNoProperties();
// void tooLongParameterNamesList(); // QTBUG-139845
private:
static bool checkForSideEffects
(const QMetaObjectBuilder& builder,
QMetaObjectBuilder::AddMembers members);
};
struct MetaObjectDeleter
{
void operator()(QMetaObject *p) { free(p); }
};
using MetaObjectPtr = std::unique_ptr<QMetaObject, MetaObjectDeleter>;
struct MetaObjectComparison {
bool isSame = false;
QString details;
operator bool() {return isSame;}
static inline auto Ok() {return MetaObjectComparison{true, QString()}; }
static inline auto Failed(QStringView message) {return MetaObjectComparison{false, message.toString()}; }
};
MetaObjectComparison sameMetaObject(const QMetaObject *meta1, const QMetaObject *meta2);
static MetaObjectComparison sameProperty(const QMetaProperty& prop1, const QMetaProperty& prop2);
// 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(QString revisionProp READ prop WRITE setProp REVISION 42)
Q_PROPERTY(SomethingEnum eprop READ eprop)
Q_PROPERTY(SomethingFlagEnum fprop READ fprop)
Q_PROPERTY(QLocale::Language language READ language)
Q_PROPERTY(QString virtualP READ prop VIRTUAL)
// Doesn't override anything, used only to verify MOC handling of OVERRIDE keyword
Q_PROPERTY(QString overrideP READ prop OVERRIDE)
public:
Q_INVOKABLE SomethingOfEverything() {}
~SomethingOfEverything() {}
enum SomethingEnum
{
GHI,
JKL = 10
};
Q_ENUM(SomethingEnum)
enum SomethingEnum64 : qint64
{
MNO = -1,
PQR = 0x1'2345'5678,
};
Q_ENUM(SomethingEnum64)
enum SomethingFlagEnum
{
XYZ = 1,
UVW = 8
};
Q_DECLARE_FLAGS(SomethingFlag, SomethingFlagEnum)
Q_FLAG(SomethingFlag)
enum SomethingFlagEnum64 : quint64
{
RST = Q_UINT64_C(1) << 31,
OPQ = Q_UINT64_C(1) << 63,
};
Q_DECLARE_FLAGS(SomethingFlag64, SomethingFlagEnum64)
Q_FLAG(SomethingFlag64)
Q_INVOKABLE Q_SCRIPTABLE void method1() const {}
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&) {}
Q_REVISION(24) void revisionSlot() {}
private slots:
void slot3() {}
protected slots:
Q_SCRIPTABLE void slot4(int) {}
void slot5(int, const QString &) {}
signals:
void sig1();
void sig2(int x, const QString& y);
void propChanged(const QString&);
};
void tst_QMetaObjectBuilder::create()
{
QMetaObjectBuilder builder;
QVERIFY(builder.className().isEmpty());
QCOMPARE(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());
}
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);
QCOMPARE(builder.superClass(), &QObject::staticMetaObject);
// Change it again.
builder.setSuperClass(&staticMetaObject);
QCOMPARE(builder.superClass(), &staticMetaObject);
// Clone the super class off a static QMetaObject.
builder.addMetaObject(&QObject::staticMetaObject, QMetaObjectBuilder::SuperClass);
QVERIFY(!builder.superClass());
builder.addMetaObject(&staticMetaObject, QMetaObjectBuilder::SuperClass);
QCOMPARE(builder.superClass(), staticMetaObject.superClass());
// Check that nothing else changed.
QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::SuperClass));
}
void tst_QMetaObjectBuilder::flags()
{
QMetaObjectBuilder builder;
// Check default
QCOMPARE(builder.flags(), 0);
// Set flags
builder.setFlags(DynamicMetaObject);
QCOMPARE(builder.flags(), DynamicMetaObject);
}
void tst_QMetaObjectBuilder::method()
{
QMetaObjectBuilder builder;
// Check null method
QMetaMethodBuilder nullMethod;
QCOMPARE(nullMethod.signature(), QByteArray());
QCOMPARE(nullMethod.methodType(), QMetaMethod::Method);
QVERIFY(nullMethod.returnType().isEmpty());
QVERIFY(nullMethod.parameterTypes().isEmpty());
QVERIFY(nullMethod.parameterNames().isEmpty());
QVERIFY(nullMethod.tag().isEmpty());
QCOMPARE(nullMethod.access(), QMetaMethod::Public);
QCOMPARE(nullMethod.attributes(), 0);
QCOMPARE(nullMethod.revision(), 0);
QCOMPARE(nullMethod.index(), 0);
QCOMPARE(nullMethod.isConst(),0);
// Add a method and check its attributes.
QMetaMethodBuilder method1 = builder.addMethod("foo(const QString&, int)");
QCOMPARE(method1.signature(), QByteArray("foo(QString,int)"));
QCOMPARE(method1.methodType(), QMetaMethod::Method);
QCOMPARE(method1.returnType(), QByteArray("void"));
QCOMPARE(method1.parameterTypes(), QList<QByteArrayView>() << "QString" << "int");
QVERIFY(method1.parameterNames().isEmpty());
QVERIFY(method1.tag().isEmpty());
QCOMPARE(method1.access(), QMetaMethod::Public);
QCOMPARE(method1.attributes(), 0);
QCOMPARE(method1.revision(), 0);
QCOMPARE(method1.index(), 0);
QCOMPARE(method1.isConst(),0);
QCOMPARE(builder.methodCount(), 1);
// Add another method and check again.
QMetaMethodBuilder method2 = builder.addMethod("bar(QString)", "int");
QCOMPARE(method2.signature(), QByteArray("bar(QString)"));
QCOMPARE(method2.methodType(), QMetaMethod::Method);
QCOMPARE(method2.returnType(), QByteArray("int"));
QCOMPARE(method2.parameterTypes(), QList<QByteArrayView>() << "QString");
QVERIFY(method2.parameterNames().isEmpty());
QVERIFY(method2.tag().isEmpty());
QCOMPARE(method2.access(), QMetaMethod::Public);
QCOMPARE(method2.attributes(), 0);
QCOMPARE(method2.revision(), 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(QMetaMethod::Cloned);
method1.setRevision(123);
method1.setConst(true);
// Check that method1 is changed, but method2 is not.
QCOMPARE(method1.signature(), QByteArray("foo(QString,int)"));
QCOMPARE(method1.methodType(), QMetaMethod::Method);
QCOMPARE(method1.returnType(), QByteArray("int"));
QCOMPARE(method1.parameterTypes(), QList<QByteArrayView>() << "QString" << "int");
QCOMPARE(method1.parameterNames(), QList<QByteArray>() << "a" << "b");
QCOMPARE(method1.tag(), QByteArray("tag"));
QCOMPARE(method1.access(), QMetaMethod::Private);
QCOMPARE(method1.attributes(), QMetaMethod::Cloned);
QCOMPARE(method1.revision(), 123);
QCOMPARE(method1.index(), 0);
QCOMPARE(method1.isConst(),true);
QCOMPARE(method2.signature(), QByteArray("bar(QString)"));
QCOMPARE(method2.methodType(), QMetaMethod::Method);
QCOMPARE(method2.returnType(), QByteArray("int"));
QCOMPARE(method2.parameterTypes(), QList<QByteArrayView>() << "QString");
QVERIFY(method2.parameterNames().isEmpty());
QVERIFY(method2.tag().isEmpty());
QCOMPARE(method2.access(), QMetaMethod::Public);
QCOMPARE(method2.attributes(), 0);
QCOMPARE(method2.revision(), 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(QMetaMethod::Scriptable);
method2.setRevision(321);
// This time check that only method2 changed.
QCOMPARE(method1.signature(), QByteArray("foo(QString,int)"));
QCOMPARE(method1.methodType(), QMetaMethod::Method);
QCOMPARE(method1.returnType(), QByteArray("int"));
QCOMPARE(method1.parameterTypes(), QList<QByteArrayView>() << "QString" << "int");
QCOMPARE(method1.parameterNames(), QList<QByteArray>() << "a" << "b");
QCOMPARE(method1.tag(), QByteArray("tag"));
QCOMPARE(method1.access(), QMetaMethod::Private);
QCOMPARE(method1.attributes(), QMetaMethod::Cloned);
QCOMPARE(method1.revision(), 123);
QCOMPARE(method1.index(), 0);
QCOMPARE(method1.isConst(),true);
QCOMPARE(method2.signature(), QByteArray("bar(QString)"));
QCOMPARE(method2.methodType(), QMetaMethod::Method);
QCOMPARE(method2.returnType(), QByteArray("QString"));
QCOMPARE(method2.parameterTypes(), QList<QByteArrayView>() << "QString");
QCOMPARE(method2.parameterNames(), QList<QByteArray>() << "c");
QCOMPARE(method2.tag(), QByteArray("Q_FOO"));
QCOMPARE(method2.access(), QMetaMethod::Protected);
QCOMPARE(method2.attributes(), QMetaMethod::Scriptable);
QCOMPARE(method2.revision(), 321);
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)"));
QCOMPARE(method2.methodType(), QMetaMethod::Method);
QCOMPARE(method2.returnType(), QByteArray("QString"));
QCOMPARE(method2.parameterTypes(), QList<QByteArrayView>() << "QString");
QCOMPARE(method2.parameterNames(), QList<QByteArray>() << "c");
QCOMPARE(method2.tag(), QByteArray("Q_FOO"));
QCOMPARE(method2.access(), QMetaMethod::Protected);
QCOMPARE(method2.attributes(), QMetaMethod::Scriptable);
QCOMPARE(method2.revision(), 321);
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)"));
QCOMPARE(method1.methodType(), QMetaMethod::Slot);
QCOMPARE(method1.returnType(), QByteArray("void"));
QCOMPARE(method1.parameterTypes(), QList<QByteArrayView>() << "QString" << "int");
QVERIFY(method1.parameterNames().isEmpty());
QVERIFY(method1.tag().isEmpty());
QCOMPARE(method1.access(), QMetaMethod::Public);
QCOMPARE(method1.attributes(), 0);
QCOMPARE(method1.index(), 0);
QCOMPARE(method1.isConst(),0);
QCOMPARE(builder.methodCount(), 1);
// Add another slot and check again.
QMetaMethodBuilder method2 = builder.addSlot("bar(QString)");
QCOMPARE(method2.signature(), QByteArray("bar(QString)"));
QCOMPARE(method2.methodType(), QMetaMethod::Slot);
QCOMPARE(method2.returnType(), QByteArray("void"));
QCOMPARE(method2.parameterTypes(), QList<QByteArrayView>() << "QString");
QVERIFY(method2.parameterNames().isEmpty());
QVERIFY(method2.tag().isEmpty());
QCOMPARE(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)"));
QCOMPARE(method1.methodType(), QMetaMethod::Signal);
QCOMPARE(method1.returnType(), QByteArray("void"));
QCOMPARE(method1.parameterTypes(), QList<QByteArrayView>() << "QString" << "int");
QVERIFY(method1.parameterNames().isEmpty());
QVERIFY(method1.tag().isEmpty());
QCOMPARE(method1.access(), QMetaMethod::Public);
QCOMPARE(method1.attributes(), 0);
QCOMPARE(method1.index(), 0);
QCOMPARE(method1.isConst(),0);
QCOMPARE(builder.methodCount(), 1);
// Add another signal and check again.
QMetaMethodBuilder method2 = builder.addSignal("bar(QString)");
QCOMPARE(method2.signature(), QByteArray("bar(QString)"));
QCOMPARE(method2.methodType(), QMetaMethod::Signal);
QCOMPARE(method2.returnType(), QByteArray("void"));
QCOMPARE(method2.parameterTypes(), QList<QByteArrayView>() << "QString");
QVERIFY(method2.parameterNames().isEmpty());
QVERIFY(method2.tag().isEmpty());
QCOMPARE(method2.access(), QMetaMethod::Public);
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::signalAdditionOrderDoesNotMatter()
{
QMetaObjectBuilder builder;
auto property1 = builder.addProperty("prop1", "int", QMetaType::fromType<int>());
auto property2 = builder.addProperty("prop2", "int", QMetaType::fromType<int>());
auto signal1 = builder.addSignal("sig1()");
builder.addMethod("m(const QString&, int)");
auto signal2 = builder.addSignal("sig2()");
property1.setNotifySignal(signal1);
property2.setNotifySignal(signal2);
MetaObjectPtr meta{builder.toMetaObject()};
int idx1 = meta->indexOfSignal("sig1()");
QCOMPARE_NE(idx1, -1);
QMetaProperty metaProp1 = meta->property(meta->indexOfProperty("prop1"));
QCOMPARE(idx1, metaProp1.notifySignalIndex());
int idx2 = meta->indexOfSignal("sig2()");
QCOMPARE_NE(idx2, -1);
QMetaProperty metaProp2 = meta->property(meta->indexOfProperty("prop2"));
QCOMPARE(idx2, metaProp2.notifySignalIndex());
builder.removeSignal(signal1.index());
meta = MetaObjectPtr{builder.toMetaObject()};
QCOMPARE(meta->indexOfSignal("sig1()"), -1);
metaProp1 = meta->property(meta->indexOfProperty("prop1"));
QCOMPARE(metaProp1.notifySignalIndex(), -1);
metaProp2 = meta->property(meta->indexOfProperty("prop2"));
QCOMPARE(metaProp2.notifySignalIndex(), idx2 - 1);
auto signal3 = builder.addSignal("sig3()");
property1.setNotifySignal(signal3);
meta = MetaObjectPtr{builder.toMetaObject()};
metaProp1 = meta->property(meta->indexOfProperty("prop1"));
metaProp2 = meta->property(meta->indexOfProperty("prop2"));
// Signals are always grouped together near the beginning of the meta-object
QCOMPARE(metaProp1.notifySignalIndex(), metaProp2.notifySignalIndex() + 1);
}
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)"));
QCOMPARE(ctor1.methodType(), QMetaMethod::Constructor);
QVERIFY(ctor1.returnType().isEmpty());
QCOMPARE(ctor1.parameterTypes(), QList<QByteArrayView>() << "QString" << "int");
QVERIFY(ctor1.parameterNames().isEmpty());
QVERIFY(ctor1.tag().isEmpty());
QCOMPARE(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)"));
QCOMPARE(ctor2.methodType(), QMetaMethod::Constructor);
QVERIFY(ctor2.returnType().isEmpty());
QCOMPARE(ctor2.parameterTypes(), QList<QByteArrayView>() << "QString");
QVERIFY(ctor2.parameterNames().isEmpty());
QVERIFY(ctor2.tag().isEmpty());
QCOMPARE(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(QMetaMethod::Scriptable);
// Check that ctor1 is changed, but ctor2 is not.
QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)"));
QCOMPARE(ctor1.methodType(), QMetaMethod::Constructor);
QCOMPARE(ctor1.returnType(), QByteArray("int"));
QCOMPARE(ctor1.parameterTypes(), QList<QByteArrayView>() << "QString" << "int");
QCOMPARE(ctor1.parameterNames(), QList<QByteArray>() << "a" << "b");
QCOMPARE(ctor1.tag(), QByteArray("tag"));
QCOMPARE(ctor1.access(), QMetaMethod::Private);
QCOMPARE(ctor1.attributes(), QMetaMethod::Scriptable);
QCOMPARE(ctor1.index(), 0);
QCOMPARE(ctor2.signature(), QByteArray("bar(QString)"));
QCOMPARE(ctor2.methodType(), QMetaMethod::Constructor);
QVERIFY(ctor2.returnType().isEmpty());
QCOMPARE(ctor2.parameterTypes(), QList<QByteArrayView>() << "QString");
QVERIFY(ctor2.parameterNames().isEmpty());
QVERIFY(ctor2.tag().isEmpty());
QCOMPARE(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(QMetaMethod::Compatibility);
// This time check that only ctor2 changed.
QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)"));
QCOMPARE(ctor1.methodType(), QMetaMethod::Constructor);
QCOMPARE(ctor1.returnType(), QByteArray("int"));
QCOMPARE(ctor1.parameterTypes(), QList<QByteArrayView>() << "QString" << "int");
QCOMPARE(ctor1.parameterNames(), QList<QByteArray>() << "a" << "b");
QCOMPARE(ctor1.tag(), QByteArray("tag"));
QCOMPARE(ctor1.access(), QMetaMethod::Private);
QCOMPARE(ctor1.attributes(), QMetaMethod::Scriptable);
QCOMPARE(ctor1.index(), 0);
QCOMPARE(ctor2.signature(), QByteArray("bar(QString)"));
QCOMPARE(ctor2.methodType(), QMetaMethod::Constructor);
QCOMPARE(ctor2.returnType(), QByteArray("QString"));
QCOMPARE(ctor2.parameterTypes(), QList<QByteArrayView>() << "QString");
QCOMPARE(ctor2.parameterNames(), QList<QByteArray>() << "c");
QCOMPARE(ctor2.tag(), QByteArray("Q_FOO"));
QCOMPARE(ctor2.access(), QMetaMethod::Protected);
QCOMPARE(ctor2.attributes(), QMetaMethod::Compatibility);
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)"));
QCOMPARE(ctor2.methodType(), QMetaMethod::Constructor);
QCOMPARE(ctor2.returnType(), QByteArray("QString"));
QCOMPARE(ctor2.parameterTypes(), QList<QByteArrayView>() << "QString");
QCOMPARE(ctor2.parameterNames(), QList<QByteArray>() << "c");
QCOMPARE(ctor2.tag(), QByteArray("Q_FOO"));
QCOMPARE(ctor2.access(), QMetaMethod::Protected);
QCOMPARE(ctor2.attributes(), QMetaMethod::Compatibility);
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()"));
QCOMPARE(prototypeConstructor.methodType(), QMetaMethod::Constructor);
QCOMPARE(prototypeConstructor.returnType(), QByteArray());
QVERIFY(prototypeConstructor.parameterTypes().isEmpty());
QCOMPARE(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.isUser());
QVERIFY(!nullProp.hasStdCppSet());
QVERIFY(!nullProp.isEnumOrFlag());
QVERIFY(!nullProp.isConstant());
QVERIFY(!nullProp.isFinal());
QVERIFY(!nullProp.isVirtual());
QVERIFY(!nullProp.isOverride());
QCOMPARE(nullProp.index(), 0);
QCOMPARE(nullProp.revision(), 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.isUser());
QVERIFY(!prop1.hasStdCppSet());
QVERIFY(!prop1.isEnumOrFlag());
QVERIFY(!prop1.isConstant());
QVERIFY(!prop1.isFinal());
QVERIFY(!prop1.isVirtual());
QVERIFY(!prop1.isOverride());
QCOMPARE(prop1.revision(), 0);
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.isUser());
QVERIFY(!prop2.hasStdCppSet());
QVERIFY(!prop2.isEnumOrFlag());
QVERIFY(!prop2.isConstant());
QVERIFY(!prop2.isFinal());
QVERIFY(!prop2.isVirtual());
QVERIFY(!prop2.isOverride());
QCOMPARE(prop2.revision(), 0);
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.setUser(true);
prop1.setStdCppSet(true);
prop1.setEnumOrFlag(true);
prop1.setConstant(true);
prop1.setFinal(true);
prop1.setRevision(123);
// 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.isUser());
QVERIFY(prop1.hasStdCppSet());
QVERIFY(prop1.isEnumOrFlag());
QVERIFY(prop1.isConstant());
QVERIFY(prop1.isFinal());
QCOMPARE(prop1.revision(), 123);
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.isUser());
QVERIFY(!prop2.hasStdCppSet());
QVERIFY(!prop2.isEnumOrFlag());
QVERIFY(!prop2.isConstant());
QVERIFY(!prop2.isFinal());
QVERIFY(!prop2.isVirtual());
QVERIFY(!prop2.isOverride());
QCOMPARE(prop2.revision(), 0);
// 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.isUser());
QVERIFY(!prop2.hasStdCppSet());
QVERIFY(!prop2.isEnumOrFlag());
QVERIFY(!prop2.isConstant());
QVERIFY(!prop2.isFinal());
QVERIFY(!prop2.isVirtual());
QVERIFY(!prop2.isOverride());
QCOMPARE(prop2.revision(), 0);
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.setUser(false); \
prop2.setStdCppSet(false); \
prop2.setEnumOrFlag(false); \
prop2.setConstant(false); \
prop2.setFinal(false); \
prop2.setVirtual(false); \
prop2.setOverride(false); \
prop2.setBindable(false); \
prop2.setRequired(false); \
} while (0)
#define SET_ALL_FLAGS() \
do { \
prop2.setReadable(true); \
prop2.setWritable(true); \
prop2.setResettable(true); \
prop2.setDesignable(true); \
prop2.setScriptable(true); \
prop2.setStored(true); \
prop2.setUser(true); \
prop2.setStdCppSet(true); \
prop2.setEnumOrFlag(true); \
prop2.setConstant(true); \
prop2.setFinal(true); \
prop2.setVirtual(true); \
prop2.setOverride(true); \
prop2.setBindable(true); \
prop2.setRequired(true); \
} 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.isUser() ? 1 : 0) + \
(prop2.hasStdCppSet() ? 1 : 0) + \
(prop2.isEnumOrFlag() ? 1 : 0) + \
(prop2.isConstant() ? 1 : 0) + \
(prop2.isFinal() ? 1 : 0) + \
(prop2.isVirtual() ? 1 : 0) + \
(prop2.isOverride() ? 1 : 0) + \
(prop2.isBindable() ? 1 : 0) + \
(prop2.isRequired() ? 1 : 0))
#define CHECK_FLAG(setFunc,isFunc) \
do { \
++flagCounter; \
CLEAR_FLAGS(); \
QCOMPARE(COUNT_FLAGS(), 0); \
prop2.setFunc(true); \
QVERIFY(prop2.isFunc()); \
QCOMPARE(COUNT_FLAGS(), 1); \
} while (0)
int flagCounter = 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(setUser, isUser);
CHECK_FLAG(setStdCppSet, hasStdCppSet);
CHECK_FLAG(setEnumOrFlag, isEnumOrFlag);
CHECK_FLAG(setConstant, isConstant);
CHECK_FLAG(setBindable, isBindable);
CHECK_FLAG(setFinal, isFinal);
CHECK_FLAG(setVirtual, isVirtual);
CHECK_FLAG(setOverride, isOverride);
CHECK_FLAG(setRequired, isRequired);
SET_ALL_FLAGS();
QCOMPARE(COUNT_FLAGS(), flagCounter);
// 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.signal(0).signature(), QByteArrayView{"propChanged(QString)"});
// virt specifiers
{ //Q_PROPERTY(int virtualP READ prop VIRTUAL)
QMetaProperty prototype = SomethingOfEverything::staticMetaObject.property(7);
QMetaPropertyBuilder prototypeProp = builder.addProperty(prototype);
QCOMPARE(prototypeProp.isVirtual(), true);
QCOMPARE(prototypeProp.isOverride(), false);
QCOMPARE(prototypeProp.isFinal(), false);
}
{ // Q_PROPERTY(int overrideP READ prop OVERRIDE)
QMetaProperty prototype = SomethingOfEverything::staticMetaObject.property(8);
QMetaPropertyBuilder prototypeProp = builder.addProperty(prototype);
QCOMPARE(prototypeProp.isVirtual(), false);
QCOMPARE(prototypeProp.isOverride(), true);
QCOMPARE(prototypeProp.isFinal(), false);
}
}
void tst_QMetaObjectBuilder::variantProperty()
{
QMetaObjectBuilder builder;
builder.addProperty("variant", "const QVariant &");
MetaObjectPtr meta{builder.toMetaObject()};
QMetaProperty prop = meta->property(meta->propertyOffset());
QCOMPARE(QMetaType::Type(prop.userType()), QMetaType::QVariant);
QCOMPARE(QMetaType::Type(prop.userType()), QMetaType::QVariant);
QCOMPARE(QByteArray(prop.typeName()), QByteArray("QVariant"));
}
void tst_QMetaObjectBuilder::notifySignal()
{
QMetaObjectBuilder builder;
QMetaPropertyBuilder prop = builder.addProperty("foo", "const QString &");
builder.addSlot("setFoo(QString)");
builder.addSignal("dummy()");
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(), 3);
QCOMPARE(builder.propertyCount(), 1);
// Check that nothing else changed except methods and properties.
QVERIFY(checkForSideEffects
(builder, QMetaObjectBuilder::Methods | QMetaObjectBuilder::Properties));
}
void tst_QMetaObjectBuilder::enumerator()
{
static const QtPrivate::QMetaTypeInterface fooFlagMetaType = {
0,
8,
8,
QMetaType::IsEnumeration | QMetaType::IsUnsignedEnumeration | QMetaType::RelocatableType,
{},
nullptr,
"fooFlag",
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
};
QMetaObjectBuilder builder;
// Add an enumerator and check its attributes.
QMetaEnumBuilder enum1 = builder.addEnumerator("foo");
QCOMPARE(enum1.name(), QByteArray("foo"));
QVERIFY(!enum1.isFlag());
QVERIFY(!enum1.isScoped());
QVERIFY(!enum1.is64Bit());
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());
QVERIFY(!enum2.isScoped());
QVERIFY(!enum2.is64Bit());
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);
enum1.setIsScoped(true);
enum1.setEnumName(QByteArrayLiteral("fooFlag"));
enum1.setMetaType(QMetaType(&fooFlagMetaType));
QVERIFY(enum1.is64Bit());
enum1.setIs64Bit(false);
QVERIFY(!enum1.is64Bit());
QCOMPARE(enum1.addKey("ABC", 0), 0);
QCOMPARE(enum1.addKey("DEF", 1), 1);
QCOMPARE(enum1.addKey("GHI", -2), 2);
// Check that enum1 is changed, but enum2 is not.
QCOMPARE(enum1.name(), QByteArray("foo"));
QVERIFY(enum1.isFlag());
QVERIFY(enum1.isScoped());
QCOMPARE(enum1.enumName(), QByteArray("fooFlag"));
QCOMPARE(enum1.metaType(), QMetaType(&fooFlagMetaType));
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), -2);
QCOMPARE(enum1.value64(0), 0);
QCOMPARE(enum1.value64(1), 1);
QCOMPARE(enum1.value64(2), uint(-2));
QCOMPARE(enum2.name(), QByteArray("bar"));
QVERIFY(!enum2.isFlag());
QVERIFY(!enum2.isScoped());
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", quint64(1) << 32), 1);
QVERIFY(enum2.is64Bit());
// This time check that only method2 changed.
QCOMPARE(enum1.name(), QByteArray("foo"));
QVERIFY(enum1.isFlag());
QVERIFY(enum1.isScoped());
QVERIFY(!enum1.is64Bit());
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), -2);
QCOMPARE(enum1.value64(0), 0);
QCOMPARE(enum1.value64(1), 1);
QCOMPARE(enum1.value64(2), uint(-2));
QCOMPARE(enum2.name(), QByteArray("bar"));
QVERIFY(enum2.isFlag());
QVERIFY(!enum2.isScoped());
QVERIFY(enum2.is64Bit());
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), 0); // truncated!
QCOMPARE(enum2.value64(0), 10);
QCOMPARE(enum2.value64(1), quint64(1) << 32);
// Reset enum2 to 32 bits
enum2.setIs64Bit(false);
QCOMPARE(enum2.value(0), 10);
QCOMPARE(enum2.value(1), 0);
QCOMPARE(enum2.value64(0), 10);
QCOMPARE(enum2.value64(1), 0);
// Reset back restores it
enum2.setIs64Bit(true);
QCOMPARE(enum2.value64(0), 10);
QCOMPARE(enum2.value64(1), quint64(1) << 32);
// Remove enum1 key
enum1.removeKey(2);
QCOMPARE(enum1.name(), QByteArray("foo"));
QVERIFY(enum1.isFlag());
QVERIFY(enum1.isScoped());
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(enum1.value64(0), 0);
QCOMPARE(enum1.value64(1), 1);
QCOMPARE(enum1.value64(2), std::nullopt);
QCOMPARE(enum2.name(), QByteArray("bar"));
QVERIFY(enum2.isFlag());
QVERIFY(!enum2.isScoped());
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), 0); // truncated!
QCOMPARE(enum2.value64(0), 10);
QCOMPARE(enum2.value64(1), quint64(1) << 32);
// 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());
QVERIFY(!enum2.isScoped());
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), 0); // truncated!
QCOMPARE(enum2.value64(0), 10);
QCOMPARE(enum2.value64(1), quint64(1) << 32);
// 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::enumProperty()
{
// When adding property with an enumeration type, QMetaProperty::isEnumType()
// should return true.
QMetaObjectBuilder builder;
builder.setSuperClass(QObject::metaObject());
auto enumMetaType = QMetaType::fromType<Qt::Orientation>();
QVERIFY(enumMetaType.isValid());
builder.addProperty("orientation", "Qt::Orientation", enumMetaType);
MetaObjectPtr mo{builder.toMetaObject()};
QVERIFY(mo != nullptr);
const int index = mo->indexOfProperty("orientation");
QVERIFY(index != -1);
QVERIFY(mo->property(index).isEnumType());
}
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);
QCOMPARE(builder.relatedMetaObject(0), &QObject::staticMetaObject);
QCOMPARE(builder.relatedMetaObject(1), &staticMetaObject);
QCOMPARE(builder.relatedMetaObjectCount(), 2);
// Remove the first one and check again.
builder.removeRelatedMetaObject(0);
QCOMPARE(builder.relatedMetaObject(0), &staticMetaObject);
QCOMPARE(builder.relatedMetaObjectCount(), 1);
// Check that nothing else changed.
QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::RelatedMetaObjects));
}
static void smetacall(QObject *, QMetaObject::Call, int, void **)
{
return;
}
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);
MetaObjectPtr meta{builder.toMetaObject()};
auto compared = sameMetaObject(meta.get(), &QObject::staticMetaObject);
QVERIFY2(compared, qPrintable(compared.details));
QMetaObjectBuilder builder2(&staticMetaObject);
meta.reset(builder2.toMetaObject());
compared = sameMetaObject(meta.get(), &staticMetaObject);
QVERIFY2(compared, qPrintable(compared.details));
QMetaObjectBuilder builder3(&SomethingOfEverything::staticMetaObject);
meta.reset(builder3.toMetaObject());
compared = sameMetaObject(meta.get(), &SomethingOfEverything::staticMetaObject);
QVERIFY2(compared, qPrintable(compared.details));
}
class TestMetaPropertyFlags : public QObject
{
Q_OBJECT
Q_PROPERTY(MyEnumFlag fprop READ fprop)
public:
enum MyEnum { A = 0x01, B = 0x02 };
Q_DECLARE_FLAGS(MyEnumFlag, MyEnum)
Q_FLAG(MyEnumFlag)
Q_ENUM(MyEnum)
MyEnumFlag fprop() const { return A; }
};
void tst_QMetaObjectBuilder::copyMetaObject_metaProperty()
{
// The TestMetaPropertyFlags class has both `Q_ENUM(MyEnum)` and
// `Q_FLAG(MyEnumFlag, MyEnum)`, which means two separate sets of enumerator data
// are generated for `MyEnum`, from the .moc file:
// `QtMocHelpers::EnumData<enum MyEnum>`
// `QtMocHelpers::EnumData<MyEnumFlag>`
const auto *mo = &TestMetaPropertyFlags::staticMetaObject;
auto index1 = mo->indexOfProperty("fprop");
// No problem with the original meta-object, the correct enumerator
// (name() == "MyEnumFlag") is found in the `QMetaProperty(const QMetaObject *, int)`
// constructor
QMetaProperty p1 = mo->property(index1);
QVERIFY(p1.isEnumType());
QVERIFY(p1.isFlagType()); // and it has `EnumIsFlag`.
// Clone the meta-object
QMetaObjectBuilder builder(&TestMetaPropertyFlags::staticMetaObject);
MetaObjectPtr meta{builder.toMetaObject()};
int index2 = meta->indexOfProperty("fprop");
QMetaProperty p2 = meta->property(index2);
QVERIFY(p2.isEnumType());
// When constructing `p2`, it doesn't find the "MyEnumFlag" enumerator data,
// instead it only finds "MyEnum".
QEXPECT_FAIL("", "isFlagType() returns false, QTBUG-101426", Continue);
QVERIFY(p2.isFlagType());
QEXPECT_FAIL("", "Meta properties differ, QTBUG-101426", Continue);
QVERIFY(sameProperty(p1, p2));
}
// Check that removing a method updates notify signals appropriately
void tst_QMetaObjectBuilder::removeNotifySignal()
{
QMetaObjectBuilder builder;
builder.addSignal("foo(const QString&, int)");
QMetaMethodBuilder method = builder.addSignal("bar(QString)");
// Setup property
QMetaPropertyBuilder prop = builder.addProperty("prop", "const QString &");
prop.setNotifySignal(method);
QVERIFY(prop.hasNotifySignal());
QCOMPARE(prop.notifySignal().index(), 1);
// Remove non-notify signal
builder.removeSignal(0);
QVERIFY(prop.hasNotifySignal());
QCOMPARE(prop.notifySignal().index(), 0);
// Remove notify signal
builder.removeSignal(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 (method1.methodSignature() != method2.methodSignature())
return false;
if (QByteArray(method1.typeName()) != QByteArray(method2.typeName()))
return false;
if (method1.parameterTypes() != method2.parameterTypes())
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;
if (method1.revision() != method2.revision())
return false;
if (method1.isConst() != method2.isConst())
return false;
return true;
}
static MetaObjectComparison sameProperty(const QMetaProperty& prop1, const QMetaProperty& prop2)
{
if (QByteArray(prop1.name()) != QByteArray(prop2.name()))
return MetaObjectComparison::Failed(QStringLiteral("Property names differ: %1 vs %2").arg(prop1.name(), prop2.name()));
if (QByteArray(prop1.typeName()) != QByteArray(prop2.typeName()))
return MetaObjectComparison::Failed(QStringLiteral("Property type names differ: %1 vs %2").arg(prop1.typeName(), prop2.typeName()));
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.isUser() != prop2.isUser() ||
prop1.isFlagType() != prop2.isFlagType() ||
prop1.isEnumType() != prop2.isEnumType() ||
prop1.hasNotifySignal() != prop2.hasNotifySignal() ||
prop1.hasStdCppSet() != prop2.hasStdCppSet())
return MetaObjectComparison::Failed(u"Flags differ");
if (prop1.hasNotifySignal()) {
if (prop1.notifySignalIndex() != prop2.notifySignalIndex())
return MetaObjectComparison::Failed(QStringLiteral("Notify signal index differ: %1 vs %2").arg(
QString::number(prop1.notifySignalIndex()),
QString::number(prop2.notifySignalIndex())));
}
const int revision1 = prop1.revision();
const int revision2 = prop2.revision();
if (revision1 != revision2)
return MetaObjectComparison::Failed(QStringLiteral("Revisions differ: %1 vs %2").arg(QString::number(revision1), QString::number(revision2)));
return MetaObjectComparison::Ok();
}
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.value64(index) != enum2.value64(index))
return false;
}
if (QByteArray(enum1.scope()) != QByteArray(enum2.scope()))
return false;
if (enum1.isScoped() != enum2.isScoped())
return false;
if (enum1.is64Bit() != enum2.is64Bit())
return false;
return true;
}
// Determine if two meta objects are identical.
MetaObjectComparison sameMetaObject(const QMetaObject *meta1, const QMetaObject *meta2)
{
int index;
if (strcmp(meta1->className(), meta2->className()) != 0) {
QString message = QLatin1String("Class names differ: %1 %2").arg(meta1->className(), meta2->className());
return MetaObjectComparison::Failed(message);
}
if (meta1->superClass() != meta2->superClass())
return MetaObjectComparison::Failed(QStringLiteral("Super classes differ"));
auto numCompare = [](int num1, int num2, const QString &message) {
if (num1 != num2)
return MetaObjectComparison::Failed(message.arg(QString::number(num1), QString::number(num2)));
return MetaObjectComparison::Ok();
};
auto compared = numCompare(meta1->constructorCount(), meta2->constructorCount(), QStringLiteral("Construct counts differ: %1 %2"));
if (!compared.isSame)
return compared;
compared = numCompare(meta1->methodCount(), meta2->methodCount(), QStringLiteral("Method counts differ: %1 %2"));
if (!compared.isSame)
return compared;
compared = numCompare(meta1->enumeratorCount(), meta2->enumeratorCount(), QStringLiteral("Enumerator counts differ: %1 %2"));
if (!compared.isSame)
return compared;
compared = numCompare(meta1->propertyCount(), meta2->propertyCount(), QStringLiteral("Property counts differ: %1 %2"));
if (!compared.isSame)
return compared;
compared = numCompare(meta1->classInfoCount(), meta2->classInfoCount(), QStringLiteral("ClassInfo counts differ: %1 %2"));
if (!compared.isSame)
return compared;
for (index = 0; index < meta1->constructorCount(); ++index) {
if (!sameMethod(meta1->constructor(index), meta2->constructor(index)))
return MetaObjectComparison::Failed(QStringLiteral("Constructors difer at index %1").arg(index));
}
for (index = 0; index < meta1->methodCount(); ++index) {
if (!sameMethod(meta1->method(index), meta2->method(index)))
return MetaObjectComparison::Failed(QStringLiteral("Methods difer at index %1").arg(index));
}
for (index = 0; index < meta1->propertyCount(); ++index) {
if (auto compared = sameProperty(meta1->property(index), meta2->property(index)); !compared) {
compared.details += QStringLiteral(" at index %1").arg(index);
return compared;
}
}
for (index = 0; index < meta1->enumeratorCount(); ++index) {
if (!sameEnumerator(meta1->enumerator(index), meta2->enumerator(index)))
return MetaObjectComparison::Failed(QStringLiteral("Enumerators difer at index %1").arg(index));
}
for (index = 0; index < meta1->classInfoCount(); ++index) {
const auto name1= QByteArray(meta1->classInfo(index).name());
const auto name2= QByteArray(meta1->classInfo(index).name());
if (name1 != name2)
return MetaObjectComparison::Failed(QStringLiteral("Class infos difer at index %1: %2 vs %3").arg(QString::number(index), name1, name2));
const auto value1 = QByteArray(meta1->classInfo(index).value());
const auto value2= QByteArray(meta2->classInfo(index).value());
if ( value1 != value2)
return MetaObjectComparison::Failed(QStringLiteral("Class infos difer at index %1: %2 vs %3").arg(QString::number(index), value1, value2));
}
const auto *objects1 = meta1->d.relatedMetaObjects;
const auto *objects2 = meta2->d.relatedMetaObjects;
if (objects1 && !objects2)
return MetaObjectComparison::Failed(u"Metaobject 1 had related metaobjects, but Metaoject 2 did not");
if (objects2 && !objects1)
return MetaObjectComparison::Failed(u"Metaobject 2 had related metaobjects, but Metaoject 1 did not");
if (objects1 && objects2) {
while (*objects1 != 0 && *objects2 != 0) {
if (*objects1 != *objects2)
return MetaObjectComparison::Failed(QStringLiteral("Related metaobjects differ at index %1").arg(index));
++objects1;
++objects2;
}
}
return MetaObjectComparison::Ok();
}
// This class is used to test that the meta-object generated by QMOB can be
// used by a real object.
// The class manually implements the functions normally generated by moc, and
// creates the corresponding meta-object using QMOB. The autotests check that
// this object can be used by QObject/QMetaObject functionality (property
// access, signals & slots, constructing instances, ...).
class TestObject : public QObject
{
// Manually expanded from Q_OBJECT macro
public:
static QMetaObject staticMetaObject;
virtual const QMetaObject *metaObject() const override;
virtual void *qt_metacast(const char *) override;
virtual int qt_metacall(QMetaObject::Call, int, void **) override;
private:
Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
//Q_PROPERTY(int intProp READ intProp WRITE setIntProp NOTIFY intPropChanged)
public:
TestObject(QObject *parent = nullptr); // Q_INVOKABLE
// Property accessors
int intProp() const;
void setIntProp(int v);
void emitIntPropChanged();
int voidSlotIntArgument() const;
// Q_INVOKABLE
QVariantList listInvokableQRealQString(qreal, const QString &);
//public Q_SLOTS:
void voidSlotInt(int);
//Q_SIGNALS:
void intPropChanged(int);
private:
static QMetaObject *buildMetaObject();
MetaObjectPtr m_metaObject;
int m_intProp;
int m_voidSlotIntArg;
};
QMetaObject TestObject::staticMetaObject = {
{ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}
};
TestObject::TestObject(QObject *parent)
: QObject(parent), m_metaObject(buildMetaObject()),
m_intProp(-1), m_voidSlotIntArg(-1)
{
staticMetaObject = *m_metaObject;
}
QMetaObject *TestObject::buildMetaObject()
{
QMetaObjectBuilder builder;
// NOTE: If you change the meta-object, remember to adapt qt_metacall and
// friends below accordingly.
builder.setClassName("TestObject");
builder.setStaticMetacallFunction(qt_static_metacall);
QMetaMethodBuilder intPropChanged = builder.addSignal("intPropChanged(int)");
intPropChanged.setParameterNames(QList<QByteArray>() << "newIntPropValue");
QMetaPropertyBuilder prop = builder.addProperty("intProp", "int");
prop.setNotifySignal(intPropChanged);
QMetaMethodBuilder voidSlotInt = builder.addSlot("voidSlotInt(int)");
voidSlotInt.setParameterNames(QList<QByteArray>() << "slotIntArg");
QMetaMethodBuilder listInvokableQRealQString = builder.addMethod("listInvokableQRealQString(qreal,QString)");
listInvokableQRealQString.setReturnType("QVariantList");
listInvokableQRealQString.setParameterNames(QList<QByteArray>() << "qrealArg" << "qstringArg");
builder.addConstructor("TestObject(QObject*)");
builder.addConstructor("TestObject()");
return builder.toMetaObject();
}
int TestObject::intProp() const
{
return m_intProp;
}
void TestObject::setIntProp(int value)
{
if (m_intProp != value) {
m_intProp = value;
emit intPropChanged(value);
}
}
void TestObject::emitIntPropChanged()
{
emit intPropChanged(m_intProp);
}
QVariantList TestObject::listInvokableQRealQString(qreal r, const QString &s)
{
return QVariantList() << r << s;
}
void TestObject::voidSlotInt(int value)
{
m_voidSlotIntArg = value;
}
int TestObject::voidSlotIntArgument() const
{
return m_voidSlotIntArg;
}
void TestObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::CreateInstance) {
switch (_id) {
case 0: { TestObject *_r = new TestObject((*reinterpret_cast< QObject*(*)>(_a[1])));
if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break;
case 1: { TestObject *_r = new TestObject();
if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break;
default: {
QMetaMethod ctor = _o->metaObject()->constructor(_id);
qFatal("You forgot to add a case for CreateInstance %s", ctor.methodSignature().constData());
}
}
} else if (_c == QMetaObject::InvokeMetaMethod) {
Q_ASSERT(_o->metaObject()->cast(_o));
TestObject *_t = static_cast<TestObject *>(_o);
switch (_id) {
case 0: _t->intPropChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
case 1: _t->voidSlotInt((*reinterpret_cast< int(*)>(_a[1]))); break;
case 2: *reinterpret_cast<QVariantList(*)>(_a[0]) = _t->listInvokableQRealQString(*reinterpret_cast<qreal(*)>(_a[1]), *reinterpret_cast<QString(*)>(_a[2])); break;
default: {
QMetaMethod method = _o->metaObject()->method(_o->metaObject()->methodOffset() + _id);
qFatal("You forgot to add a case for InvokeMetaMethod %s", method.methodSignature().constData());
}
}
} else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast<int *>(_a[0]);
void **func = reinterpret_cast<void **>(_a[1]);
{
typedef void (TestObject::*_t)(int );
if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TestObject::intPropChanged)) {
*result = 0;
}
}
{
typedef void (TestObject::*_t)(int );
if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TestObject::voidSlotInt)) {
*result = 1;
}
}
{
typedef QVariantList (TestObject::*_t)(qreal, const QString &);
if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TestObject::listInvokableQRealQString)) {
*result = 2;
}
}
}
}
const QMetaObject *TestObject::metaObject() const
{
return m_metaObject.get();
}
void *TestObject::qt_metacast(const char *_clname)
{
if (!_clname) return 0;
if (!strcmp(_clname, "TestObject"))
return static_cast<void*>(const_cast< TestObject*>(this));
return QObject::qt_metacast(_clname);
}
int TestObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QObject::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
int ownMethodCount = m_metaObject->methodCount() - m_metaObject->methodOffset();
int ownPropertyCount = m_metaObject->propertyCount() - m_metaObject->propertyOffset();
if (_c == QMetaObject::InvokeMetaMethod) {
if (_id < ownMethodCount)
qt_static_metacall(this, _c, _id, _a);
_id -= ownMethodCount;
} else if (_c == QMetaObject::ReadProperty) {
void *_v = _a[0];
switch (_id) {
case 0: *reinterpret_cast< int*>(_v) = intProp(); break;
default: if (_id < ownPropertyCount) {
QMetaProperty prop = m_metaObject->property(m_metaObject->propertyOffset() + _id);
qFatal("You forgot to add a case for ReadProperty %s", prop.name());
}
}
_id -= ownPropertyCount;
} else if (_c == QMetaObject::WriteProperty) {
void *_v = _a[0];
switch (_id) {
case 0: setIntProp(*reinterpret_cast< int*>(_v)); break;
default: if (_id < ownPropertyCount) {
QMetaProperty prop = m_metaObject->property(m_metaObject->propertyOffset() + _id);
qFatal("You forgot to add a case for WriteProperty %s", prop.name());
}
}
_id -= ownPropertyCount;
} else if (_c == QMetaObject::ResetProperty) {
_id -= ownPropertyCount;
}
return _id;
}
// SIGNAL 0
void TestObject::intPropChanged(int _t1)
{
void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, m_metaObject.get(), 0, _a);
}
void tst_QMetaObjectBuilder::usage_signal()
{
QScopedPointer<TestObject> testObject(new TestObject);
QSignalSpy propChangedSpy(testObject.data(), &TestObject::intPropChanged);
testObject->emitIntPropChanged();
QCOMPARE(propChangedSpy.size(), 1);
QCOMPARE(propChangedSpy.at(0).size(), 1);
QCOMPARE(propChangedSpy.at(0).at(0).toInt(), testObject->intProp());
}
void tst_QMetaObjectBuilder::usage_property()
{
QScopedPointer<TestObject> testObject(new TestObject);
QVariant prop = testObject->property("intProp");
QCOMPARE(prop.metaType(), QMetaType(QMetaType::Int));
QCOMPARE(prop.toInt(), testObject->intProp());
QSignalSpy propChangedSpy(testObject.data(), &TestObject::intPropChanged);
QVERIFY(testObject->intProp() != 123);
testObject->setProperty("intProp", 123);
QCOMPARE(propChangedSpy.size(), 1);
prop = testObject->property("intProp");
QCOMPARE(prop.metaType(), QMetaType(QMetaType::Int));
QCOMPARE(prop.toInt(), 123);
}
void tst_QMetaObjectBuilder::usage_slot()
{
QScopedPointer<TestObject> testObject(new TestObject);
int index = testObject->metaObject()->indexOfMethod("voidSlotInt(int)");
QVERIFY(index != -1);
QMetaMethod voidSlotInt = testObject->metaObject()->method(index);
QCOMPARE(testObject->voidSlotIntArgument(), -1);
QVERIFY(voidSlotInt.invoke(testObject.data(), Q_ARG(int, 123)));
QCOMPARE(testObject->voidSlotIntArgument(), 123);
}
void tst_QMetaObjectBuilder::usage_method()
{
QScopedPointer<TestObject> testObject(new TestObject);
int index = testObject->metaObject()->indexOfMethod("listInvokableQRealQString(qreal,QString)");
QVERIFY(index != -1);
QMetaMethod listInvokableQRealQString = testObject->metaObject()->method(index);
QVariantList list;
QVERIFY(listInvokableQRealQString.invoke(testObject.data(), Q_RETURN_ARG(QVariantList, list),
Q_ARG(qreal, 123.0), Q_ARG(QString, "ciao")));
QCOMPARE(list.size(), 2);
QCOMPARE(list.at(0).metaType(), QMetaType(QMetaType::QReal));
QCOMPARE(list.at(0).toDouble(), double(123));
QCOMPARE(list.at(1).metaType(), QMetaType(QMetaType::QString));
QCOMPARE(list.at(1).toString(), QString::fromLatin1("ciao"));
}
void tst_QMetaObjectBuilder::usage_constructor()
{
QScopedPointer<TestObject> testObject(new TestObject);
QCOMPARE(testObject->metaObject()->constructorCount(), 2);
QScopedPointer<QObject> testInstance(testObject->metaObject()->newInstance());
QVERIFY(testInstance != 0);
QScopedPointer<QObject> testInstance2(testObject->metaObject()->newInstance(Q_ARG(QObject*, testInstance.data())));
QVERIFY(testInstance2 != 0);
QCOMPARE(testInstance2->parent(), testInstance.data());
}
void tst_QMetaObjectBuilder::usage_connect()
{
QScopedPointer<TestObject> testObject(new TestObject);
QVERIFY(QObject::connect(testObject.data(), SIGNAL(intPropChanged(int)),
testObject.data(), SLOT(voidSlotInt(int))));
QCOMPARE(testObject->voidSlotIntArgument(), -1);
testObject->setProperty("intProp", 123);
QCOMPARE(testObject->voidSlotIntArgument(), 123);
QVERIFY(QObject::disconnect(testObject.data(), SIGNAL(intPropChanged(int)),
testObject.data(), SLOT(voidSlotInt(int))));
}
void tst_QMetaObjectBuilder::usage_templateConnect()
{
QScopedPointer<TestObject> testObject(new TestObject);
QMetaObject::Connection con = QObject::connect(testObject.data(), &TestObject::intPropChanged,
testObject.data(), &TestObject::voidSlotInt);
QVERIFY(con);
QCOMPARE(testObject->voidSlotIntArgument(), -1);
testObject->setProperty("intProp", 123);
QCOMPARE(testObject->voidSlotIntArgument(), 123);
QVERIFY(QObject::disconnect(testObject.data(), &TestObject::intPropChanged,
testObject.data(), &TestObject::voidSlotInt));
// Something that isn't a signal
QTest::ignoreMessage(QtWarningMsg, "QObject::connect(TestObject, TestObject): signal not found");
con = QObject::connect(testObject.data(), &TestObject::setIntProp,
testObject.data(), &TestObject::intPropChanged);
QVERIFY(!con);
}
void tst_QMetaObjectBuilder::classNameFirstInStringData()
{
QMetaObjectBuilder builder;
builder.addMetaObject(&SomethingOfEverything::staticMetaObject);
builder.setClassName(QByteArrayLiteral("TestClass"));
MetaObjectPtr mo{builder.toMetaObject()};
uint offset = mo->d.stringdata[0];
uint len = mo->d.stringdata[1];
QByteArray className(reinterpret_cast<const char *>(mo->d.stringdata) + offset, len);
QCOMPARE(className, QByteArrayLiteral("TestClass"));
}
struct MyFoo {};
struct myParameter {};
void tst_QMetaObjectBuilder::propertyMetaType()
{
qRegisterMetaType<myParameter>();
QMetaType meta = QMetaType::fromType<MyFoo>();
auto metaId = meta.id();
QMetaObjectBuilder builder;
builder.setClassName("Test");
builder.addProperty("myParameter", "MyFoo");
MetaObjectPtr mo{builder.toMetaObject()};
QMetaProperty metaProp = mo->property(mo->indexOfProperty("myParameter"));
QCOMPARE(metaProp.typeName(), meta.name());
QCOMPARE(metaProp.typeId(), metaId);
QCOMPARE(metaProp.metaType(), meta);
}
void tst_QMetaObjectBuilder::enumCloning()
{
QMetaObjectBuilder builder(&SomethingOfEverything::staticMetaObject);
auto smo = SomethingOfEverything::staticMetaObject;
auto mo = builder.toMetaObject();
auto cleanup = qScopeGuard([&]() { free(mo); });
QCOMPARE_EQ(mo->enumeratorCount(), smo.enumeratorCount());
for (int enumIndex = 0; enumIndex < smo.enumeratorCount(); ++enumIndex) {
QMetaEnum metaEnumFromBuilder = mo->enumerator(enumIndex);
QMetaEnum originalMetaEnum = smo.enumerator(enumIndex);
QCOMPARE_EQ(metaEnumFromBuilder.metaType(), originalMetaEnum.metaType());
QCOMPARE_EQ(metaEnumFromBuilder.keyCount(), originalMetaEnum.keyCount());
for (int k = 0; k < originalMetaEnum.keyCount(); ++k) {
QCOMPARE_EQ(QByteArrayView(metaEnumFromBuilder.key(k)), QByteArrayView(originalMetaEnum.key(k)));
QCOMPARE_EQ(metaEnumFromBuilder.value(k), originalMetaEnum.value(k));
QCOMPARE_EQ(metaEnumFromBuilder.value64(k), originalMetaEnum.value64(k));
}
}
}
// Can't use this unittest on the CI because it hits an assert
// void tst_QMetaObjectBuilder::tooLongParameterNamesList()
// {
// // QTBUG-139845
// QMetaObjectBuilder builder;
//
// builder.setSuperClass(&QObject::staticMetaObject);
//
// QMetaMethodBuilder methodBuilder = builder.addSignal("iChanged(int)");
// methodBuilder.setParameterNames({"i"});
// int icIdx = methodBuilder.index();
//
// methodBuilder = builder.addSignal("fChanged(float)");
// methodBuilder.setParameterNames({"f"});
// int fcIdx = methodBuilder.index();
//
// methodBuilder = builder.addSlot("pushI(int)");
// methodBuilder.setParameterNames({"i"});
// builder.addSlot("reset()");
// methodBuilder = builder.addSlot("add(int)");
// methodBuilder.setParameterNames({"", "i"});
//
// methodBuilder.setReturnType("int");
//
// builder.addProperty("i", "int", icIdx);
// builder.addProperty("f", "float", fcIdx);
//
// builder.toMetaObject();
// }
void tst_QMetaObjectBuilder::ownMetaTypeNoProperties()
{
QMetaObjectBuilder builder;
builder.setClassName("NoProperties");
auto mo = builder.toMetaObject();
auto cleanup = qScopeGuard([&](){ free(mo); });
// own metatype should be invalid, as the dynamic metaobject has not been registered
QVERIFY(!mo->metaType().isValid());// should not crash
}
QTEST_MAIN(tst_QMetaObjectBuilder)
#include "tst_qmetaobjectbuilder.moc"