| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. |
| 4 | ** Contact: https://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the test suite of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at https://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU |
| 19 | ** General Public License version 3 as published by the Free Software |
| 20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| 21 | ** included in the packaging of this file. Please review the following |
| 22 | ** information to ensure the GNU General Public License requirements will |
| 23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| 24 | ** |
| 25 | ** $QT_END_LICENSE$ |
| 26 | ** |
| 27 | ****************************************************************************/ |
| 28 | |
| 29 | #include <QtTest/QtTest> |
| 30 | #include <QtCore/qlocale.h> |
| 31 | #include <private/qmetaobjectbuilder_p.h> |
| 32 | |
| 33 | class tst_QMetaObjectBuilder : public QObject |
| 34 | { |
| 35 | Q_OBJECT |
| 36 | private slots: |
| 37 | void create(); |
| 38 | void className(); |
| 39 | void superClass(); |
| 40 | void flags(); |
| 41 | void method(); |
| 42 | void slot(); |
| 43 | void signal(); |
| 44 | void constructor(); |
| 45 | void property(); |
| 46 | void variantProperty(); |
| 47 | void notifySignal(); |
| 48 | void enumerator(); |
| 49 | void classInfo(); |
| 50 | void relatedMetaObject(); |
| 51 | void staticMetacall(); |
| 52 | void copyMetaObject(); |
| 53 | void serialize(); |
| 54 | void relocatableData(); |
| 55 | void removeNotifySignal(); |
| 56 | |
| 57 | void usage_signal(); |
| 58 | void usage_property(); |
| 59 | void usage_slot(); |
| 60 | void usage_method(); |
| 61 | void usage_constructor(); |
| 62 | void usage_connect(); |
| 63 | void usage_templateConnect(); |
| 64 | |
| 65 | void classNameFirstInStringData(); |
| 66 | |
| 67 | private: |
| 68 | static bool checkForSideEffects |
| 69 | (const QMetaObjectBuilder& builder, |
| 70 | QMetaObjectBuilder::AddMembers members); |
| 71 | static bool sameMetaObject |
| 72 | (const QMetaObject *meta1, const QMetaObject *meta2); |
| 73 | }; |
| 74 | |
| 75 | // Dummy class that has something of every type of thing moc can generate. |
| 76 | class SomethingOfEverything : public QObject |
| 77 | { |
| 78 | Q_OBJECT |
| 79 | Q_CLASSINFO("ci_foo" , "ABC" ) |
| 80 | Q_CLASSINFO("ci_bar" , "DEF" ) |
| 81 | Q_PROPERTY(QString prop READ prop WRITE setProp NOTIFY propChanged) |
| 82 | Q_PROPERTY(QString prop2 READ prop WRITE setProp) |
| 83 | Q_PROPERTY(QString revisionProp READ prop WRITE setProp REVISION 42) |
| 84 | Q_PROPERTY(SomethingEnum eprop READ eprop) |
| 85 | Q_PROPERTY(SomethingFlagEnum fprop READ fprop) |
| 86 | Q_PROPERTY(QLocale::Language language READ language) |
| 87 | Q_ENUMS(SomethingEnum) |
| 88 | Q_FLAGS(SomethingFlagEnum) |
| 89 | public: |
| 90 | Q_INVOKABLE SomethingOfEverything() {} |
| 91 | ~SomethingOfEverything() {} |
| 92 | |
| 93 | enum SomethingEnum |
| 94 | { |
| 95 | GHI, |
| 96 | JKL = 10 |
| 97 | }; |
| 98 | |
| 99 | enum SomethingFlagEnum |
| 100 | { |
| 101 | XYZ = 1, |
| 102 | UVW = 8 |
| 103 | }; |
| 104 | |
| 105 | Q_INVOKABLE Q_SCRIPTABLE void method1() {} |
| 106 | |
| 107 | QString prop() const { return QString(); } |
| 108 | void setProp(const QString& v) { Q_UNUSED(v); } |
| 109 | |
| 110 | SomethingOfEverything::SomethingEnum eprop() const { return GHI; } |
| 111 | SomethingOfEverything::SomethingFlagEnum fprop() const { return XYZ; } |
| 112 | QLocale::Language language() const { return QLocale::English; } |
| 113 | |
| 114 | public slots: |
| 115 | void slot1(const QString&) {} |
| 116 | void slot2(int, const QString&) {} |
| 117 | Q_REVISION(24) void revisionSlot() {} |
| 118 | |
| 119 | private slots: |
| 120 | void slot3() {} |
| 121 | |
| 122 | protected slots: |
| 123 | Q_SCRIPTABLE void slot4(int) {} |
| 124 | void slot5(int a, const QString& b) { Q_UNUSED(a); Q_UNUSED(b); } |
| 125 | |
| 126 | signals: |
| 127 | void sig1(); |
| 128 | void sig2(int x, const QString& y); |
| 129 | void propChanged(const QString&); |
| 130 | }; |
| 131 | |
| 132 | void tst_QMetaObjectBuilder::create() |
| 133 | { |
| 134 | QMetaObjectBuilder builder; |
| 135 | QVERIFY(builder.className().isEmpty()); |
| 136 | QCOMPARE(builder.superClass(), &QObject::staticMetaObject); |
| 137 | QCOMPARE(builder.methodCount(), 0); |
| 138 | QCOMPARE(builder.constructorCount(), 0); |
| 139 | QCOMPARE(builder.propertyCount(), 0); |
| 140 | QCOMPARE(builder.enumeratorCount(), 0); |
| 141 | QCOMPARE(builder.classInfoCount(), 0); |
| 142 | QCOMPARE(builder.relatedMetaObjectCount(), 0); |
| 143 | QVERIFY(!builder.staticMetacallFunction()); |
| 144 | } |
| 145 | |
| 146 | void tst_QMetaObjectBuilder::className() |
| 147 | { |
| 148 | QMetaObjectBuilder builder; |
| 149 | |
| 150 | // Change the class name. |
| 151 | builder.setClassName("Foo" ); |
| 152 | QCOMPARE(builder.className(), QByteArray("Foo" )); |
| 153 | |
| 154 | // Change it again. |
| 155 | builder.setClassName("Bar" ); |
| 156 | QCOMPARE(builder.className(), QByteArray("Bar" )); |
| 157 | |
| 158 | // Clone the class name off a static QMetaObject. |
| 159 | builder.addMetaObject(prototype: &QObject::staticMetaObject, members: QMetaObjectBuilder::ClassName); |
| 160 | QCOMPARE(builder.className(), QByteArray("QObject" )); |
| 161 | |
| 162 | // Check that nothing else changed. |
| 163 | QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::ClassName)); |
| 164 | } |
| 165 | |
| 166 | void tst_QMetaObjectBuilder::superClass() |
| 167 | { |
| 168 | QMetaObjectBuilder builder; |
| 169 | |
| 170 | // Change the super class. |
| 171 | builder.setSuperClass(&QObject::staticMetaObject); |
| 172 | QCOMPARE(builder.superClass(), &QObject::staticMetaObject); |
| 173 | |
| 174 | // Change it again. |
| 175 | builder.setSuperClass(&staticMetaObject); |
| 176 | QCOMPARE(builder.superClass(), &staticMetaObject); |
| 177 | |
| 178 | // Clone the super class off a static QMetaObject. |
| 179 | builder.addMetaObject(prototype: &QObject::staticMetaObject, members: QMetaObjectBuilder::SuperClass); |
| 180 | QVERIFY(!builder.superClass()); |
| 181 | builder.addMetaObject(prototype: &staticMetaObject, members: QMetaObjectBuilder::SuperClass); |
| 182 | QCOMPARE(builder.superClass(), staticMetaObject.superClass()); |
| 183 | |
| 184 | // Check that nothing else changed. |
| 185 | QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::SuperClass)); |
| 186 | } |
| 187 | |
| 188 | void tst_QMetaObjectBuilder::flags() |
| 189 | { |
| 190 | QMetaObjectBuilder builder; |
| 191 | |
| 192 | // Check default |
| 193 | QCOMPARE(builder.flags(), 0); |
| 194 | |
| 195 | // Set flags |
| 196 | builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); |
| 197 | QCOMPARE(builder.flags(), QMetaObjectBuilder::DynamicMetaObject); |
| 198 | } |
| 199 | |
| 200 | void tst_QMetaObjectBuilder::method() |
| 201 | { |
| 202 | QMetaObjectBuilder builder; |
| 203 | |
| 204 | // Check null method |
| 205 | QMetaMethodBuilder nullMethod; |
| 206 | QCOMPARE(nullMethod.signature(), QByteArray()); |
| 207 | QCOMPARE(nullMethod.methodType(), QMetaMethod::Method); |
| 208 | QVERIFY(nullMethod.returnType().isEmpty()); |
| 209 | QVERIFY(nullMethod.parameterTypes().isEmpty()); |
| 210 | QVERIFY(nullMethod.parameterNames().isEmpty()); |
| 211 | QVERIFY(nullMethod.tag().isEmpty()); |
| 212 | QCOMPARE(nullMethod.access(), QMetaMethod::Public); |
| 213 | QCOMPARE(nullMethod.attributes(), 0); |
| 214 | QCOMPARE(nullMethod.revision(), 0); |
| 215 | QCOMPARE(nullMethod.index(), 0); |
| 216 | |
| 217 | // Add a method and check its attributes. |
| 218 | QMetaMethodBuilder method1 = builder.addMethod(signature: "foo(const QString&, int)" ); |
| 219 | QCOMPARE(method1.signature(), QByteArray("foo(QString,int)" )); |
| 220 | QCOMPARE(method1.methodType(), QMetaMethod::Method); |
| 221 | QCOMPARE(method1.returnType(), QByteArray("void" )); |
| 222 | QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int" ); |
| 223 | QVERIFY(method1.parameterNames().isEmpty()); |
| 224 | QVERIFY(method1.tag().isEmpty()); |
| 225 | QCOMPARE(method1.access(), QMetaMethod::Public); |
| 226 | QCOMPARE(method1.attributes(), 0); |
| 227 | QCOMPARE(method1.revision(), 0); |
| 228 | QCOMPARE(method1.index(), 0); |
| 229 | QCOMPARE(builder.methodCount(), 1); |
| 230 | |
| 231 | // Add another method and check again. |
| 232 | QMetaMethodBuilder method2 = builder.addMethod(signature: "bar(QString)" , returnType: "int" ); |
| 233 | QCOMPARE(method2.signature(), QByteArray("bar(QString)" )); |
| 234 | QCOMPARE(method2.methodType(), QMetaMethod::Method); |
| 235 | QCOMPARE(method2.returnType(), QByteArray("int" )); |
| 236 | QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString" ); |
| 237 | QVERIFY(method2.parameterNames().isEmpty()); |
| 238 | QVERIFY(method2.tag().isEmpty()); |
| 239 | QCOMPARE(method2.access(), QMetaMethod::Public); |
| 240 | QCOMPARE(method2.attributes(), 0); |
| 241 | QCOMPARE(method2.revision(), 0); |
| 242 | QCOMPARE(method2.index(), 1); |
| 243 | QCOMPARE(builder.methodCount(), 2); |
| 244 | |
| 245 | // Perform index-based lookup. |
| 246 | QCOMPARE(builder.indexOfMethod("foo(const QString&, int)" ), 0); |
| 247 | QCOMPARE(builder.indexOfMethod("bar(QString)" ), 1); |
| 248 | QCOMPARE(builder.indexOfMethod("baz()" ), -1); |
| 249 | |
| 250 | // Modify the attributes on method1. |
| 251 | method1.setReturnType("int" ); |
| 252 | method1.setParameterNames(QList<QByteArray>() << "a" << "b" ); |
| 253 | method1.setTag("tag" ); |
| 254 | method1.setAccess(QMetaMethod::Private); |
| 255 | method1.setAttributes(42); |
| 256 | method1.setRevision(123); |
| 257 | |
| 258 | // Check that method1 is changed, but method2 is not. |
| 259 | QCOMPARE(method1.signature(), QByteArray("foo(QString,int)" )); |
| 260 | QCOMPARE(method1.methodType(), QMetaMethod::Method); |
| 261 | QCOMPARE(method1.returnType(), QByteArray("int" )); |
| 262 | QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int" ); |
| 263 | QCOMPARE(method1.parameterNames(), QList<QByteArray>() << "a" << "b" ); |
| 264 | QCOMPARE(method1.tag(), QByteArray("tag" )); |
| 265 | QCOMPARE(method1.access(), QMetaMethod::Private); |
| 266 | QCOMPARE(method1.attributes(), 42); |
| 267 | QCOMPARE(method1.revision(), 123); |
| 268 | QCOMPARE(method1.index(), 0); |
| 269 | QCOMPARE(method2.signature(), QByteArray("bar(QString)" )); |
| 270 | QCOMPARE(method2.methodType(), QMetaMethod::Method); |
| 271 | QCOMPARE(method2.returnType(), QByteArray("int" )); |
| 272 | QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString" ); |
| 273 | QVERIFY(method2.parameterNames().isEmpty()); |
| 274 | QVERIFY(method2.tag().isEmpty()); |
| 275 | QCOMPARE(method2.access(), QMetaMethod::Public); |
| 276 | QCOMPARE(method2.attributes(), 0); |
| 277 | QCOMPARE(method2.revision(), 0); |
| 278 | QCOMPARE(method2.index(), 1); |
| 279 | QCOMPARE(builder.methodCount(), 2); |
| 280 | |
| 281 | // Modify the attributes on method2. |
| 282 | method2.setReturnType("QString" ); |
| 283 | method2.setParameterNames(QList<QByteArray>() << "c" ); |
| 284 | method2.setTag("Q_FOO" ); |
| 285 | method2.setAccess(QMetaMethod::Protected); |
| 286 | method2.setAttributes(24); |
| 287 | method2.setRevision(321); |
| 288 | |
| 289 | // This time check that only method2 changed. |
| 290 | QCOMPARE(method1.signature(), QByteArray("foo(QString,int)" )); |
| 291 | QCOMPARE(method1.methodType(), QMetaMethod::Method); |
| 292 | QCOMPARE(method1.returnType(), QByteArray("int" )); |
| 293 | QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int" ); |
| 294 | QCOMPARE(method1.parameterNames(), QList<QByteArray>() << "a" << "b" ); |
| 295 | QCOMPARE(method1.tag(), QByteArray("tag" )); |
| 296 | QCOMPARE(method1.access(), QMetaMethod::Private); |
| 297 | QCOMPARE(method1.attributes(), 42); |
| 298 | QCOMPARE(method1.revision(), 123); |
| 299 | QCOMPARE(method1.index(), 0); |
| 300 | QCOMPARE(method2.signature(), QByteArray("bar(QString)" )); |
| 301 | QCOMPARE(method2.methodType(), QMetaMethod::Method); |
| 302 | QCOMPARE(method2.returnType(), QByteArray("QString" )); |
| 303 | QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString" ); |
| 304 | QCOMPARE(method2.parameterNames(), QList<QByteArray>() << "c" ); |
| 305 | QCOMPARE(method2.tag(), QByteArray("Q_FOO" )); |
| 306 | QCOMPARE(method2.access(), QMetaMethod::Protected); |
| 307 | QCOMPARE(method2.attributes(), 24); |
| 308 | QCOMPARE(method2.revision(), 321); |
| 309 | QCOMPARE(method2.index(), 1); |
| 310 | QCOMPARE(builder.methodCount(), 2); |
| 311 | |
| 312 | // Remove method1 and check that method2 becomes index 0. |
| 313 | builder.removeMethod(index: 0); |
| 314 | QCOMPARE(builder.methodCount(), 1); |
| 315 | method2 = builder.method(index: 0); |
| 316 | QCOMPARE(method2.signature(), QByteArray("bar(QString)" )); |
| 317 | QCOMPARE(method2.methodType(), QMetaMethod::Method); |
| 318 | QCOMPARE(method2.returnType(), QByteArray("QString" )); |
| 319 | QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString" ); |
| 320 | QCOMPARE(method2.parameterNames(), QList<QByteArray>() << "c" ); |
| 321 | QCOMPARE(method2.tag(), QByteArray("Q_FOO" )); |
| 322 | QCOMPARE(method2.access(), QMetaMethod::Protected); |
| 323 | QCOMPARE(method2.attributes(), 24); |
| 324 | QCOMPARE(method2.revision(), 321); |
| 325 | QCOMPARE(method2.index(), 0); |
| 326 | |
| 327 | // Perform index-based lookup again. |
| 328 | QCOMPARE(builder.indexOfMethod("foo(const QString&, int)" ), -1); |
| 329 | QCOMPARE(builder.indexOfMethod("bar(QString)" ), 0); |
| 330 | QCOMPARE(builder.indexOfMethod("baz()" ), -1); |
| 331 | QCOMPARE(builder.method(0).signature(), QByteArray("bar(QString)" )); |
| 332 | QCOMPARE(builder.method(9).signature(), QByteArray()); |
| 333 | |
| 334 | // Check that nothing else changed. |
| 335 | QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Methods)); |
| 336 | } |
| 337 | |
| 338 | void tst_QMetaObjectBuilder::slot() |
| 339 | { |
| 340 | QMetaObjectBuilder builder; |
| 341 | |
| 342 | // Add a slot and check its attributes. |
| 343 | QMetaMethodBuilder method1 = builder.addSlot(signature: "foo(const QString&, int)" ); |
| 344 | QCOMPARE(method1.signature(), QByteArray("foo(QString,int)" )); |
| 345 | QCOMPARE(method1.methodType(), QMetaMethod::Slot); |
| 346 | QCOMPARE(method1.returnType(), QByteArray("void" )); |
| 347 | QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int" ); |
| 348 | QVERIFY(method1.parameterNames().isEmpty()); |
| 349 | QVERIFY(method1.tag().isEmpty()); |
| 350 | QCOMPARE(method1.access(), QMetaMethod::Public); |
| 351 | QCOMPARE(method1.attributes(), 0); |
| 352 | QCOMPARE(method1.index(), 0); |
| 353 | QCOMPARE(builder.methodCount(), 1); |
| 354 | |
| 355 | // Add another slot and check again. |
| 356 | QMetaMethodBuilder method2 = builder.addSlot(signature: "bar(QString)" ); |
| 357 | QCOMPARE(method2.signature(), QByteArray("bar(QString)" )); |
| 358 | QCOMPARE(method2.methodType(), QMetaMethod::Slot); |
| 359 | QCOMPARE(method2.returnType(), QByteArray("void" )); |
| 360 | QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString" ); |
| 361 | QVERIFY(method2.parameterNames().isEmpty()); |
| 362 | QVERIFY(method2.tag().isEmpty()); |
| 363 | QCOMPARE(method2.access(), QMetaMethod::Public); |
| 364 | QCOMPARE(method2.attributes(), 0); |
| 365 | QCOMPARE(method2.index(), 1); |
| 366 | QCOMPARE(builder.methodCount(), 2); |
| 367 | |
| 368 | // Perform index-based lookup |
| 369 | QCOMPARE(builder.indexOfSlot("foo(const QString &, int)" ), 0); |
| 370 | QCOMPARE(builder.indexOfSlot("bar(QString)" ), 1); |
| 371 | QCOMPARE(builder.indexOfSlot("baz()" ), -1); |
| 372 | |
| 373 | // Check that nothing else changed. |
| 374 | QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Methods)); |
| 375 | } |
| 376 | |
| 377 | void tst_QMetaObjectBuilder::signal() |
| 378 | { |
| 379 | QMetaObjectBuilder builder; |
| 380 | |
| 381 | // Add a signal and check its attributes. |
| 382 | QMetaMethodBuilder method1 = builder.addSignal(signature: "foo(const QString&, int)" ); |
| 383 | QCOMPARE(method1.signature(), QByteArray("foo(QString,int)" )); |
| 384 | QCOMPARE(method1.methodType(), QMetaMethod::Signal); |
| 385 | QCOMPARE(method1.returnType(), QByteArray("void" )); |
| 386 | QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int" ); |
| 387 | QVERIFY(method1.parameterNames().isEmpty()); |
| 388 | QVERIFY(method1.tag().isEmpty()); |
| 389 | QCOMPARE(method1.access(), QMetaMethod::Public); |
| 390 | QCOMPARE(method1.attributes(), 0); |
| 391 | QCOMPARE(method1.index(), 0); |
| 392 | QCOMPARE(builder.methodCount(), 1); |
| 393 | |
| 394 | // Add another signal and check again. |
| 395 | QMetaMethodBuilder method2 = builder.addSignal(signature: "bar(QString)" ); |
| 396 | QCOMPARE(method2.signature(), QByteArray("bar(QString)" )); |
| 397 | QCOMPARE(method2.methodType(), QMetaMethod::Signal); |
| 398 | QCOMPARE(method2.returnType(), QByteArray("void" )); |
| 399 | QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString" ); |
| 400 | QVERIFY(method2.parameterNames().isEmpty()); |
| 401 | QVERIFY(method2.tag().isEmpty()); |
| 402 | QCOMPARE(method2.access(), QMetaMethod::Public); |
| 403 | QCOMPARE(method2.attributes(), 0); |
| 404 | QCOMPARE(method2.index(), 1); |
| 405 | QCOMPARE(builder.methodCount(), 2); |
| 406 | |
| 407 | // Perform index-based lookup |
| 408 | QCOMPARE(builder.indexOfSignal("foo(const QString &, int)" ), 0); |
| 409 | QCOMPARE(builder.indexOfSignal("bar(QString)" ), 1); |
| 410 | QCOMPARE(builder.indexOfSignal("baz()" ), -1); |
| 411 | |
| 412 | // Check that nothing else changed. |
| 413 | QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Methods)); |
| 414 | } |
| 415 | |
| 416 | void tst_QMetaObjectBuilder::constructor() |
| 417 | { |
| 418 | QMetaObjectBuilder builder; |
| 419 | |
| 420 | // Add a constructor and check its attributes. |
| 421 | QMetaMethodBuilder ctor1 = builder.addConstructor(signature: "foo(const QString&, int)" ); |
| 422 | QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)" )); |
| 423 | QCOMPARE(ctor1.methodType(), QMetaMethod::Constructor); |
| 424 | QVERIFY(ctor1.returnType().isEmpty()); |
| 425 | QCOMPARE(ctor1.parameterTypes(), QList<QByteArray>() << "QString" << "int" ); |
| 426 | QVERIFY(ctor1.parameterNames().isEmpty()); |
| 427 | QVERIFY(ctor1.tag().isEmpty()); |
| 428 | QCOMPARE(ctor1.access(), QMetaMethod::Public); |
| 429 | QCOMPARE(ctor1.attributes(), 0); |
| 430 | QCOMPARE(ctor1.index(), 0); |
| 431 | QCOMPARE(builder.constructorCount(), 1); |
| 432 | |
| 433 | // Add another constructor and check again. |
| 434 | QMetaMethodBuilder ctor2 = builder.addConstructor(signature: "bar(QString)" ); |
| 435 | QCOMPARE(ctor2.signature(), QByteArray("bar(QString)" )); |
| 436 | QCOMPARE(ctor2.methodType(), QMetaMethod::Constructor); |
| 437 | QVERIFY(ctor2.returnType().isEmpty()); |
| 438 | QCOMPARE(ctor2.parameterTypes(), QList<QByteArray>() << "QString" ); |
| 439 | QVERIFY(ctor2.parameterNames().isEmpty()); |
| 440 | QVERIFY(ctor2.tag().isEmpty()); |
| 441 | QCOMPARE(ctor2.access(), QMetaMethod::Public); |
| 442 | QCOMPARE(ctor2.attributes(), 0); |
| 443 | QCOMPARE(ctor2.index(), 1); |
| 444 | QCOMPARE(builder.constructorCount(), 2); |
| 445 | |
| 446 | // Perform index-based lookup. |
| 447 | QCOMPARE(builder.indexOfConstructor("foo(const QString&, int)" ), 0); |
| 448 | QCOMPARE(builder.indexOfConstructor("bar(QString)" ), 1); |
| 449 | QCOMPARE(builder.indexOfConstructor("baz()" ), -1); |
| 450 | QCOMPARE(builder.constructor(1).signature(), QByteArray("bar(QString)" )); |
| 451 | QCOMPARE(builder.constructor(9).signature(), QByteArray()); |
| 452 | |
| 453 | // Modify the attributes on ctor1. |
| 454 | ctor1.setReturnType("int" ); |
| 455 | ctor1.setParameterNames(QList<QByteArray>() << "a" << "b" ); |
| 456 | ctor1.setTag("tag" ); |
| 457 | ctor1.setAccess(QMetaMethod::Private); |
| 458 | ctor1.setAttributes(42); |
| 459 | |
| 460 | // Check that ctor1 is changed, but ctor2 is not. |
| 461 | QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)" )); |
| 462 | QCOMPARE(ctor1.methodType(), QMetaMethod::Constructor); |
| 463 | QCOMPARE(ctor1.returnType(), QByteArray("int" )); |
| 464 | QCOMPARE(ctor1.parameterTypes(), QList<QByteArray>() << "QString" << "int" ); |
| 465 | QCOMPARE(ctor1.parameterNames(), QList<QByteArray>() << "a" << "b" ); |
| 466 | QCOMPARE(ctor1.tag(), QByteArray("tag" )); |
| 467 | QCOMPARE(ctor1.access(), QMetaMethod::Private); |
| 468 | QCOMPARE(ctor1.attributes(), 42); |
| 469 | QCOMPARE(ctor1.index(), 0); |
| 470 | QCOMPARE(ctor2.signature(), QByteArray("bar(QString)" )); |
| 471 | QCOMPARE(ctor2.methodType(), QMetaMethod::Constructor); |
| 472 | QVERIFY(ctor2.returnType().isEmpty()); |
| 473 | QCOMPARE(ctor2.parameterTypes(), QList<QByteArray>() << "QString" ); |
| 474 | QVERIFY(ctor2.parameterNames().isEmpty()); |
| 475 | QVERIFY(ctor2.tag().isEmpty()); |
| 476 | QCOMPARE(ctor2.access(), QMetaMethod::Public); |
| 477 | QCOMPARE(ctor2.attributes(), 0); |
| 478 | QCOMPARE(ctor2.index(), 1); |
| 479 | QCOMPARE(builder.constructorCount(), 2); |
| 480 | |
| 481 | // Modify the attributes on ctor2. |
| 482 | ctor2.setReturnType("QString" ); |
| 483 | ctor2.setParameterNames(QList<QByteArray>() << "c" ); |
| 484 | ctor2.setTag("Q_FOO" ); |
| 485 | ctor2.setAccess(QMetaMethod::Protected); |
| 486 | ctor2.setAttributes(24); |
| 487 | |
| 488 | // This time check that only ctor2 changed. |
| 489 | QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)" )); |
| 490 | QCOMPARE(ctor1.methodType(), QMetaMethod::Constructor); |
| 491 | QCOMPARE(ctor1.returnType(), QByteArray("int" )); |
| 492 | QCOMPARE(ctor1.parameterTypes(), QList<QByteArray>() << "QString" << "int" ); |
| 493 | QCOMPARE(ctor1.parameterNames(), QList<QByteArray>() << "a" << "b" ); |
| 494 | QCOMPARE(ctor1.tag(), QByteArray("tag" )); |
| 495 | QCOMPARE(ctor1.access(), QMetaMethod::Private); |
| 496 | QCOMPARE(ctor1.attributes(), 42); |
| 497 | QCOMPARE(ctor1.index(), 0); |
| 498 | QCOMPARE(ctor2.signature(), QByteArray("bar(QString)" )); |
| 499 | QCOMPARE(ctor2.methodType(), QMetaMethod::Constructor); |
| 500 | QCOMPARE(ctor2.returnType(), QByteArray("QString" )); |
| 501 | QCOMPARE(ctor2.parameterTypes(), QList<QByteArray>() << "QString" ); |
| 502 | QCOMPARE(ctor2.parameterNames(), QList<QByteArray>() << "c" ); |
| 503 | QCOMPARE(ctor2.tag(), QByteArray("Q_FOO" )); |
| 504 | QCOMPARE(ctor2.access(), QMetaMethod::Protected); |
| 505 | QCOMPARE(ctor2.attributes(), 24); |
| 506 | QCOMPARE(ctor2.index(), 1); |
| 507 | QCOMPARE(builder.constructorCount(), 2); |
| 508 | |
| 509 | // Remove ctor1 and check that ctor2 becomes index 0. |
| 510 | builder.removeConstructor(index: 0); |
| 511 | QCOMPARE(builder.constructorCount(), 1); |
| 512 | ctor2 = builder.constructor(index: 0); |
| 513 | QCOMPARE(ctor2.signature(), QByteArray("bar(QString)" )); |
| 514 | QCOMPARE(ctor2.methodType(), QMetaMethod::Constructor); |
| 515 | QCOMPARE(ctor2.returnType(), QByteArray("QString" )); |
| 516 | QCOMPARE(ctor2.parameterTypes(), QList<QByteArray>() << "QString" ); |
| 517 | QCOMPARE(ctor2.parameterNames(), QList<QByteArray>() << "c" ); |
| 518 | QCOMPARE(ctor2.tag(), QByteArray("Q_FOO" )); |
| 519 | QCOMPARE(ctor2.access(), QMetaMethod::Protected); |
| 520 | QCOMPARE(ctor2.attributes(), 24); |
| 521 | QCOMPARE(ctor2.index(), 0); |
| 522 | |
| 523 | // Perform index-based lookup again. |
| 524 | QCOMPARE(builder.indexOfConstructor("foo(const QString&, int)" ), -1); |
| 525 | QCOMPARE(builder.indexOfConstructor("bar(QString)" ), 0); |
| 526 | QCOMPARE(builder.indexOfConstructor("baz()" ), -1); |
| 527 | |
| 528 | // Add constructor from prototype |
| 529 | QMetaMethod prototype = SomethingOfEverything::staticMetaObject.constructor(index: 0); |
| 530 | QMetaMethodBuilder prototypeConstructor = builder.addMethod(prototype); |
| 531 | QCOMPARE(builder.constructorCount(), 2); |
| 532 | |
| 533 | QCOMPARE(prototypeConstructor.signature(), QByteArray("SomethingOfEverything()" )); |
| 534 | QCOMPARE(prototypeConstructor.methodType(), QMetaMethod::Constructor); |
| 535 | QCOMPARE(prototypeConstructor.returnType(), QByteArray()); |
| 536 | QVERIFY(prototypeConstructor.parameterTypes().isEmpty()); |
| 537 | QCOMPARE(prototypeConstructor.access(), QMetaMethod::Public); |
| 538 | QCOMPARE(prototypeConstructor.index(), 1); |
| 539 | |
| 540 | // Check that nothing else changed. |
| 541 | QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Constructors)); |
| 542 | } |
| 543 | |
| 544 | void tst_QMetaObjectBuilder::property() |
| 545 | { |
| 546 | QMetaObjectBuilder builder; |
| 547 | |
| 548 | // Null property builder |
| 549 | QMetaPropertyBuilder nullProp; |
| 550 | QCOMPARE(nullProp.name(), QByteArray()); |
| 551 | QCOMPARE(nullProp.type(), QByteArray()); |
| 552 | QVERIFY(!nullProp.hasNotifySignal()); |
| 553 | QVERIFY(!nullProp.isReadable()); |
| 554 | QVERIFY(!nullProp.isWritable()); |
| 555 | QVERIFY(!nullProp.isResettable()); |
| 556 | QVERIFY(!nullProp.isDesignable()); |
| 557 | QVERIFY(!nullProp.isScriptable()); |
| 558 | QVERIFY(!nullProp.isStored()); |
| 559 | QVERIFY(!nullProp.isEditable()); |
| 560 | QVERIFY(!nullProp.isUser()); |
| 561 | QVERIFY(!nullProp.hasStdCppSet()); |
| 562 | QVERIFY(!nullProp.isEnumOrFlag()); |
| 563 | QVERIFY(!nullProp.isConstant()); |
| 564 | QVERIFY(!nullProp.isFinal()); |
| 565 | QCOMPARE(nullProp.index(), 0); |
| 566 | QCOMPARE(nullProp.revision(), 0); |
| 567 | |
| 568 | // Add a property and check its attributes. |
| 569 | QMetaPropertyBuilder prop1 = builder.addProperty(name: "foo" , type: "const QString &" ); |
| 570 | QCOMPARE(prop1.name(), QByteArray("foo" )); |
| 571 | QCOMPARE(prop1.type(), QByteArray("QString" )); |
| 572 | QVERIFY(!prop1.hasNotifySignal()); |
| 573 | QVERIFY(prop1.isReadable()); |
| 574 | QVERIFY(prop1.isWritable()); |
| 575 | QVERIFY(!prop1.isResettable()); |
| 576 | QVERIFY(!prop1.isDesignable()); |
| 577 | QVERIFY(prop1.isScriptable()); |
| 578 | QVERIFY(!prop1.isStored()); |
| 579 | QVERIFY(!prop1.isEditable()); |
| 580 | QVERIFY(!prop1.isUser()); |
| 581 | QVERIFY(!prop1.hasStdCppSet()); |
| 582 | QVERIFY(!prop1.isEnumOrFlag()); |
| 583 | QVERIFY(!prop1.isConstant()); |
| 584 | QVERIFY(!prop1.isFinal()); |
| 585 | QCOMPARE(prop1.revision(), 0); |
| 586 | QCOMPARE(prop1.index(), 0); |
| 587 | QCOMPARE(builder.propertyCount(), 1); |
| 588 | |
| 589 | // Add another property and check again. |
| 590 | QMetaPropertyBuilder prop2 = builder.addProperty(name: "bar" , type: "int" ); |
| 591 | QCOMPARE(prop2.name(), QByteArray("bar" )); |
| 592 | QCOMPARE(prop2.type(), QByteArray("int" )); |
| 593 | QVERIFY(!prop2.hasNotifySignal()); |
| 594 | QVERIFY(prop2.isReadable()); |
| 595 | QVERIFY(prop2.isWritable()); |
| 596 | QVERIFY(!prop2.isResettable()); |
| 597 | QVERIFY(!prop2.isDesignable()); |
| 598 | QVERIFY(prop2.isScriptable()); |
| 599 | QVERIFY(!prop2.isStored()); |
| 600 | QVERIFY(!prop2.isEditable()); |
| 601 | QVERIFY(!prop2.isUser()); |
| 602 | QVERIFY(!prop2.hasStdCppSet()); |
| 603 | QVERIFY(!prop2.isEnumOrFlag()); |
| 604 | QVERIFY(!prop2.isConstant()); |
| 605 | QVERIFY(!prop2.isFinal()); |
| 606 | QCOMPARE(prop2.revision(), 0); |
| 607 | QCOMPARE(prop2.index(), 1); |
| 608 | QCOMPARE(builder.propertyCount(), 2); |
| 609 | |
| 610 | // Perform index-based lookup. |
| 611 | QCOMPARE(builder.indexOfProperty("foo" ), 0); |
| 612 | QCOMPARE(builder.indexOfProperty("bar" ), 1); |
| 613 | QCOMPARE(builder.indexOfProperty("baz" ), -1); |
| 614 | QCOMPARE(builder.property(1).name(), QByteArray("bar" )); |
| 615 | QCOMPARE(builder.property(9).name(), QByteArray()); |
| 616 | |
| 617 | // Modify the attributes on prop1. |
| 618 | prop1.setReadable(false); |
| 619 | prop1.setWritable(false); |
| 620 | prop1.setResettable(true); |
| 621 | prop1.setDesignable(true); |
| 622 | prop1.setScriptable(false); |
| 623 | prop1.setStored(true); |
| 624 | prop1.setEditable(true); |
| 625 | prop1.setUser(true); |
| 626 | prop1.setStdCppSet(true); |
| 627 | prop1.setEnumOrFlag(true); |
| 628 | prop1.setConstant(true); |
| 629 | prop1.setFinal(true); |
| 630 | prop1.setRevision(123); |
| 631 | |
| 632 | // Check that prop1 is changed, but prop2 is not. |
| 633 | QCOMPARE(prop1.name(), QByteArray("foo" )); |
| 634 | QCOMPARE(prop1.type(), QByteArray("QString" )); |
| 635 | QVERIFY(!prop1.isReadable()); |
| 636 | QVERIFY(!prop1.isWritable()); |
| 637 | QVERIFY(prop1.isResettable()); |
| 638 | QVERIFY(prop1.isDesignable()); |
| 639 | QVERIFY(!prop1.isScriptable()); |
| 640 | QVERIFY(prop1.isStored()); |
| 641 | QVERIFY(prop1.isEditable()); |
| 642 | QVERIFY(prop1.isUser()); |
| 643 | QVERIFY(prop1.hasStdCppSet()); |
| 644 | QVERIFY(prop1.isEnumOrFlag()); |
| 645 | QVERIFY(prop1.isConstant()); |
| 646 | QVERIFY(prop1.isFinal()); |
| 647 | QCOMPARE(prop1.revision(), 123); |
| 648 | QVERIFY(prop2.isReadable()); |
| 649 | QVERIFY(prop2.isWritable()); |
| 650 | QCOMPARE(prop2.name(), QByteArray("bar" )); |
| 651 | QCOMPARE(prop2.type(), QByteArray("int" )); |
| 652 | QVERIFY(!prop2.isResettable()); |
| 653 | QVERIFY(!prop2.isDesignable()); |
| 654 | QVERIFY(prop2.isScriptable()); |
| 655 | QVERIFY(!prop2.isStored()); |
| 656 | QVERIFY(!prop2.isEditable()); |
| 657 | QVERIFY(!prop2.isUser()); |
| 658 | QVERIFY(!prop2.hasStdCppSet()); |
| 659 | QVERIFY(!prop2.isEnumOrFlag()); |
| 660 | QVERIFY(!prop2.isConstant()); |
| 661 | QVERIFY(!prop2.isFinal()); |
| 662 | QCOMPARE(prop2.revision(), 0); |
| 663 | |
| 664 | // Remove prop1 and check that prop2 becomes index 0. |
| 665 | builder.removeProperty(index: 0); |
| 666 | QCOMPARE(builder.propertyCount(), 1); |
| 667 | prop2 = builder.property(index: 0); |
| 668 | QCOMPARE(prop2.name(), QByteArray("bar" )); |
| 669 | QCOMPARE(prop2.type(), QByteArray("int" )); |
| 670 | QVERIFY(!prop2.isResettable()); |
| 671 | QVERIFY(!prop2.isDesignable()); |
| 672 | QVERIFY(prop2.isScriptable()); |
| 673 | QVERIFY(!prop2.isStored()); |
| 674 | QVERIFY(!prop2.isEditable()); |
| 675 | QVERIFY(!prop2.isUser()); |
| 676 | QVERIFY(!prop2.hasStdCppSet()); |
| 677 | QVERIFY(!prop2.isEnumOrFlag()); |
| 678 | QVERIFY(!prop2.isConstant()); |
| 679 | QVERIFY(!prop2.isFinal()); |
| 680 | QCOMPARE(prop2.revision(), 0); |
| 681 | QCOMPARE(prop2.index(), 0); |
| 682 | |
| 683 | // Perform index-based lookup again. |
| 684 | QCOMPARE(builder.indexOfProperty("foo" ), -1); |
| 685 | QCOMPARE(builder.indexOfProperty("bar" ), 0); |
| 686 | QCOMPARE(builder.indexOfProperty("baz" ), -1); |
| 687 | |
| 688 | // Check for side-effects between the flags on prop2. |
| 689 | // Setting a flag to true shouldn't set any of the others to true. |
| 690 | // This checks for cut-and-paste bugs in the implementation where |
| 691 | // the flag code was pasted but the flag name was not changed. |
| 692 | #define CLEAR_FLAGS() \ |
| 693 | do { \ |
| 694 | prop2.setReadable(false); \ |
| 695 | prop2.setWritable(false); \ |
| 696 | prop2.setResettable(false); \ |
| 697 | prop2.setDesignable(false); \ |
| 698 | prop2.setScriptable(false); \ |
| 699 | prop2.setStored(false); \ |
| 700 | prop2.setEditable(false); \ |
| 701 | prop2.setUser(false); \ |
| 702 | prop2.setStdCppSet(false); \ |
| 703 | prop2.setEnumOrFlag(false); \ |
| 704 | prop2.setConstant(false); \ |
| 705 | prop2.setFinal(false); \ |
| 706 | } while (0) |
| 707 | #define COUNT_FLAGS() \ |
| 708 | ((prop2.isReadable() ? 1 : 0) + \ |
| 709 | (prop2.isWritable() ? 1 : 0) + \ |
| 710 | (prop2.isResettable() ? 1 : 0) + \ |
| 711 | (prop2.isDesignable() ? 1 : 0) + \ |
| 712 | (prop2.isScriptable() ? 1 : 0) + \ |
| 713 | (prop2.isStored() ? 1 : 0) + \ |
| 714 | (prop2.isEditable() ? 1 : 0) + \ |
| 715 | (prop2.isUser() ? 1 : 0) + \ |
| 716 | (prop2.hasStdCppSet() ? 1 : 0) + \ |
| 717 | (prop2.isEnumOrFlag() ? 1 : 0) + \ |
| 718 | (prop2.isConstant() ? 1 : 0) + \ |
| 719 | (prop2.isFinal() ? 1 : 0)) |
| 720 | #define CHECK_FLAG(setFunc,isFunc) \ |
| 721 | do { \ |
| 722 | CLEAR_FLAGS(); \ |
| 723 | QCOMPARE(COUNT_FLAGS(), 0); \ |
| 724 | prop2.setFunc(true); \ |
| 725 | QVERIFY(prop2.isFunc()); \ |
| 726 | QCOMPARE(COUNT_FLAGS(), 1); \ |
| 727 | } while (0) |
| 728 | CHECK_FLAG(setReadable, isReadable); |
| 729 | CHECK_FLAG(setWritable, isWritable); |
| 730 | CHECK_FLAG(setResettable, isResettable); |
| 731 | CHECK_FLAG(setDesignable, isDesignable); |
| 732 | CHECK_FLAG(setScriptable, isScriptable); |
| 733 | CHECK_FLAG(setStored, isStored); |
| 734 | CHECK_FLAG(setEditable, isEditable); |
| 735 | CHECK_FLAG(setUser, isUser); |
| 736 | CHECK_FLAG(setStdCppSet, hasStdCppSet); |
| 737 | CHECK_FLAG(setEnumOrFlag, isEnumOrFlag); |
| 738 | CHECK_FLAG(setConstant, isConstant); |
| 739 | CHECK_FLAG(setFinal, isFinal); |
| 740 | |
| 741 | // Check that nothing else changed. |
| 742 | QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Properties)); |
| 743 | |
| 744 | // Add property from prototype |
| 745 | QMetaProperty prototype = SomethingOfEverything::staticMetaObject.property(index: 1); |
| 746 | QVERIFY(prototype.name() == QByteArray("prop" )); |
| 747 | QMetaPropertyBuilder prototypeProp = builder.addProperty(prototype); |
| 748 | QCOMPARE(prototypeProp.name(), QByteArray("prop" )); |
| 749 | QVERIFY(prototypeProp.hasNotifySignal()); |
| 750 | QCOMPARE(prototypeProp.notifySignal().signature(), QByteArray("propChanged(QString)" )); |
| 751 | QCOMPARE(builder.methodCount(), 1); |
| 752 | QCOMPARE(builder.method(0).signature(), QByteArray("propChanged(QString)" )); |
| 753 | } |
| 754 | |
| 755 | void tst_QMetaObjectBuilder::variantProperty() |
| 756 | { |
| 757 | QMetaObjectBuilder builder; |
| 758 | builder.addProperty(name: "variant" , type: "const QVariant &" ); |
| 759 | QMetaObject *meta = builder.toMetaObject(); |
| 760 | |
| 761 | QMetaProperty prop = meta->property(index: meta->propertyOffset()); |
| 762 | QCOMPARE(QMetaType::Type(prop.type()), QMetaType::QVariant); |
| 763 | QCOMPARE(QMetaType::Type(prop.userType()), QMetaType::QVariant); |
| 764 | QCOMPARE(QByteArray(prop.typeName()), QByteArray("QVariant" )); |
| 765 | |
| 766 | free(ptr: meta); |
| 767 | } |
| 768 | |
| 769 | void tst_QMetaObjectBuilder::notifySignal() |
| 770 | { |
| 771 | QMetaObjectBuilder builder; |
| 772 | |
| 773 | QMetaPropertyBuilder prop = builder.addProperty(name: "foo" , type: "const QString &" ); |
| 774 | builder.addSlot(signature: "setFoo(QString)" ); |
| 775 | QMetaMethodBuilder notify = builder.addSignal(signature: "fooChanged(QString)" ); |
| 776 | |
| 777 | QVERIFY(!prop.hasNotifySignal()); |
| 778 | QCOMPARE(prop.notifySignal().index(), 0); |
| 779 | |
| 780 | prop.setNotifySignal(notify); |
| 781 | QVERIFY(prop.hasNotifySignal()); |
| 782 | QCOMPARE(prop.notifySignal().index(), 1); |
| 783 | |
| 784 | prop.setNotifySignal(QMetaMethodBuilder()); |
| 785 | QVERIFY(!prop.hasNotifySignal()); |
| 786 | QCOMPARE(prop.notifySignal().index(), 0); |
| 787 | |
| 788 | prop.setNotifySignal(notify); |
| 789 | prop.removeNotifySignal(); |
| 790 | QVERIFY(!prop.hasNotifySignal()); |
| 791 | QCOMPARE(prop.notifySignal().index(), 0); |
| 792 | |
| 793 | QCOMPARE(builder.methodCount(), 2); |
| 794 | QCOMPARE(builder.propertyCount(), 1); |
| 795 | |
| 796 | // Check that nothing else changed except methods and properties. |
| 797 | QVERIFY(checkForSideEffects |
| 798 | (builder, QMetaObjectBuilder::Methods | QMetaObjectBuilder::Properties)); |
| 799 | } |
| 800 | |
| 801 | void tst_QMetaObjectBuilder::enumerator() |
| 802 | { |
| 803 | QMetaObjectBuilder builder; |
| 804 | |
| 805 | // Add an enumerator and check its attributes. |
| 806 | QMetaEnumBuilder enum1 = builder.addEnumerator(name: "foo" ); |
| 807 | QCOMPARE(enum1.name(), QByteArray("foo" )); |
| 808 | QVERIFY(!enum1.isFlag()); |
| 809 | QVERIFY(!enum1.isScoped()); |
| 810 | QCOMPARE(enum1.keyCount(), 0); |
| 811 | QCOMPARE(enum1.index(), 0); |
| 812 | QCOMPARE(builder.enumeratorCount(), 1); |
| 813 | |
| 814 | // Add another enumerator and check again. |
| 815 | QMetaEnumBuilder enum2 = builder.addEnumerator(name: "bar" ); |
| 816 | QCOMPARE(enum2.name(), QByteArray("bar" )); |
| 817 | QVERIFY(!enum2.isFlag()); |
| 818 | QVERIFY(!enum2.isScoped()); |
| 819 | QCOMPARE(enum2.keyCount(), 0); |
| 820 | QCOMPARE(enum2.index(), 1); |
| 821 | QCOMPARE(builder.enumeratorCount(), 2); |
| 822 | |
| 823 | // Perform index-based lookup. |
| 824 | QCOMPARE(builder.indexOfEnumerator("foo" ), 0); |
| 825 | QCOMPARE(builder.indexOfEnumerator("bar" ), 1); |
| 826 | QCOMPARE(builder.indexOfEnumerator("baz" ), -1); |
| 827 | QCOMPARE(builder.enumerator(1).name(), QByteArray("bar" )); |
| 828 | QCOMPARE(builder.enumerator(9).name(), QByteArray()); |
| 829 | |
| 830 | // Modify the attributes on enum1. |
| 831 | enum1.setIsFlag(true); |
| 832 | enum1.setIsScoped(true); |
| 833 | enum1.setEnumName(QByteArrayLiteral("fooFlag" )); |
| 834 | QCOMPARE(enum1.addKey("ABC" , 0), 0); |
| 835 | QCOMPARE(enum1.addKey("DEF" , 1), 1); |
| 836 | QCOMPARE(enum1.addKey("GHI" , -1), 2); |
| 837 | |
| 838 | // Check that enum1 is changed, but enum2 is not. |
| 839 | QCOMPARE(enum1.name(), QByteArray("foo" )); |
| 840 | QVERIFY(enum1.isFlag()); |
| 841 | QVERIFY(enum1.isScoped()); |
| 842 | QCOMPARE(enum1.enumName(), QByteArray("fooFlag" )); |
| 843 | QCOMPARE(enum1.keyCount(), 3); |
| 844 | QCOMPARE(enum1.index(), 0); |
| 845 | QCOMPARE(enum1.key(0), QByteArray("ABC" )); |
| 846 | QCOMPARE(enum1.key(1), QByteArray("DEF" )); |
| 847 | QCOMPARE(enum1.key(2), QByteArray("GHI" )); |
| 848 | QCOMPARE(enum1.key(3), QByteArray()); |
| 849 | QCOMPARE(enum1.value(0), 0); |
| 850 | QCOMPARE(enum1.value(1), 1); |
| 851 | QCOMPARE(enum1.value(2), -1); |
| 852 | QCOMPARE(enum2.name(), QByteArray("bar" )); |
| 853 | QVERIFY(!enum2.isFlag()); |
| 854 | QVERIFY(!enum2.isScoped()); |
| 855 | QCOMPARE(enum2.keyCount(), 0); |
| 856 | QCOMPARE(enum2.index(), 1); |
| 857 | |
| 858 | // Modify the attributes on enum2. |
| 859 | enum2.setIsFlag(true); |
| 860 | QCOMPARE(enum2.addKey("XYZ" , 10), 0); |
| 861 | QCOMPARE(enum2.addKey("UVW" , 19), 1); |
| 862 | |
| 863 | // This time check that only method2 changed. |
| 864 | QCOMPARE(enum1.name(), QByteArray("foo" )); |
| 865 | QVERIFY(enum1.isFlag()); |
| 866 | QVERIFY(enum1.isScoped()); |
| 867 | QCOMPARE(enum1.keyCount(), 3); |
| 868 | QCOMPARE(enum1.index(), 0); |
| 869 | QCOMPARE(enum1.key(0), QByteArray("ABC" )); |
| 870 | QCOMPARE(enum1.key(1), QByteArray("DEF" )); |
| 871 | QCOMPARE(enum1.key(2), QByteArray("GHI" )); |
| 872 | QCOMPARE(enum1.key(3), QByteArray()); |
| 873 | QCOMPARE(enum1.value(0), 0); |
| 874 | QCOMPARE(enum1.value(1), 1); |
| 875 | QCOMPARE(enum1.value(2), -1); |
| 876 | QCOMPARE(enum2.name(), QByteArray("bar" )); |
| 877 | QVERIFY(enum2.isFlag()); |
| 878 | QVERIFY(!enum2.isScoped()); |
| 879 | QCOMPARE(enum2.keyCount(), 2); |
| 880 | QCOMPARE(enum2.index(), 1); |
| 881 | QCOMPARE(enum2.key(0), QByteArray("XYZ" )); |
| 882 | QCOMPARE(enum2.key(1), QByteArray("UVW" )); |
| 883 | QCOMPARE(enum2.key(2), QByteArray()); |
| 884 | QCOMPARE(enum2.value(0), 10); |
| 885 | QCOMPARE(enum2.value(1), 19); |
| 886 | |
| 887 | // Remove enum1 key |
| 888 | enum1.removeKey(index: 2); |
| 889 | QCOMPARE(enum1.name(), QByteArray("foo" )); |
| 890 | QVERIFY(enum1.isFlag()); |
| 891 | QVERIFY(enum1.isScoped()); |
| 892 | QCOMPARE(enum1.keyCount(), 2); |
| 893 | QCOMPARE(enum1.index(), 0); |
| 894 | QCOMPARE(enum1.key(0), QByteArray("ABC" )); |
| 895 | QCOMPARE(enum1.key(1), QByteArray("DEF" )); |
| 896 | QCOMPARE(enum1.key(2), QByteArray()); |
| 897 | QCOMPARE(enum1.value(0), 0); |
| 898 | QCOMPARE(enum1.value(1), 1); |
| 899 | QCOMPARE(enum1.value(2), -1); |
| 900 | QCOMPARE(enum2.name(), QByteArray("bar" )); |
| 901 | QVERIFY(enum2.isFlag()); |
| 902 | QVERIFY(!enum2.isScoped()); |
| 903 | QCOMPARE(enum2.keyCount(), 2); |
| 904 | QCOMPARE(enum2.index(), 1); |
| 905 | QCOMPARE(enum2.key(0), QByteArray("XYZ" )); |
| 906 | QCOMPARE(enum2.key(1), QByteArray("UVW" )); |
| 907 | QCOMPARE(enum2.key(2), QByteArray()); |
| 908 | QCOMPARE(enum2.value(0), 10); |
| 909 | QCOMPARE(enum2.value(1), 19); |
| 910 | |
| 911 | // Remove enum1 and check that enum2 becomes index 0. |
| 912 | builder.removeEnumerator(index: 0); |
| 913 | QCOMPARE(builder.enumeratorCount(), 1); |
| 914 | enum2 = builder.enumerator(index: 0); |
| 915 | QCOMPARE(enum2.name(), QByteArray("bar" )); |
| 916 | QVERIFY(enum2.isFlag()); |
| 917 | QVERIFY(!enum2.isScoped()); |
| 918 | QCOMPARE(enum2.keyCount(), 2); |
| 919 | QCOMPARE(enum2.index(), 0); |
| 920 | QCOMPARE(enum2.key(0), QByteArray("XYZ" )); |
| 921 | QCOMPARE(enum2.key(1), QByteArray("UVW" )); |
| 922 | QCOMPARE(enum2.key(2), QByteArray()); |
| 923 | QCOMPARE(enum2.value(0), 10); |
| 924 | QCOMPARE(enum2.value(1), 19); |
| 925 | |
| 926 | // Perform index-based lookup again. |
| 927 | QCOMPARE(builder.indexOfEnumerator("foo" ), -1); |
| 928 | QCOMPARE(builder.indexOfEnumerator("bar" ), 0); |
| 929 | QCOMPARE(builder.indexOfEnumerator("baz" ), -1); |
| 930 | |
| 931 | // Check that nothing else changed. |
| 932 | QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Enumerators)); |
| 933 | } |
| 934 | |
| 935 | void tst_QMetaObjectBuilder::classInfo() |
| 936 | { |
| 937 | QMetaObjectBuilder builder; |
| 938 | |
| 939 | // Add two items of class information and check their attributes. |
| 940 | QCOMPARE(builder.addClassInfo("foo" , "value1" ), 0); |
| 941 | QCOMPARE(builder.addClassInfo("bar" , "value2" ), 1); |
| 942 | QCOMPARE(builder.classInfoName(0), QByteArray("foo" )); |
| 943 | QCOMPARE(builder.classInfoValue(0), QByteArray("value1" )); |
| 944 | QCOMPARE(builder.classInfoName(1), QByteArray("bar" )); |
| 945 | QCOMPARE(builder.classInfoValue(1), QByteArray("value2" )); |
| 946 | QCOMPARE(builder.classInfoName(9), QByteArray()); |
| 947 | QCOMPARE(builder.classInfoValue(9), QByteArray()); |
| 948 | QCOMPARE(builder.classInfoCount(), 2); |
| 949 | |
| 950 | // Perform index-based lookup. |
| 951 | QCOMPARE(builder.indexOfClassInfo("foo" ), 0); |
| 952 | QCOMPARE(builder.indexOfClassInfo("bar" ), 1); |
| 953 | QCOMPARE(builder.indexOfClassInfo("baz" ), -1); |
| 954 | |
| 955 | // Remove the first one and check again. |
| 956 | builder.removeClassInfo(index: 0); |
| 957 | QCOMPARE(builder.classInfoName(0), QByteArray("bar" )); |
| 958 | QCOMPARE(builder.classInfoValue(0), QByteArray("value2" )); |
| 959 | QCOMPARE(builder.classInfoCount(), 1); |
| 960 | |
| 961 | // Perform index-based lookup again. |
| 962 | QCOMPARE(builder.indexOfClassInfo("foo" ), -1); |
| 963 | QCOMPARE(builder.indexOfClassInfo("bar" ), 0); |
| 964 | QCOMPARE(builder.indexOfClassInfo("baz" ), -1); |
| 965 | |
| 966 | // Check that nothing else changed. |
| 967 | QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::ClassInfos)); |
| 968 | } |
| 969 | |
| 970 | void tst_QMetaObjectBuilder::relatedMetaObject() |
| 971 | { |
| 972 | QMetaObjectBuilder builder; |
| 973 | |
| 974 | // Add two related meta objects and check their attributes. |
| 975 | QCOMPARE(builder.addRelatedMetaObject(&QObject::staticMetaObject), 0); |
| 976 | QCOMPARE(builder.addRelatedMetaObject(&staticMetaObject), 1); |
| 977 | QCOMPARE(builder.relatedMetaObject(0), &QObject::staticMetaObject); |
| 978 | QCOMPARE(builder.relatedMetaObject(1), &staticMetaObject); |
| 979 | QCOMPARE(builder.relatedMetaObjectCount(), 2); |
| 980 | |
| 981 | // Remove the first one and check again. |
| 982 | builder.removeRelatedMetaObject(index: 0); |
| 983 | QCOMPARE(builder.relatedMetaObject(0), &staticMetaObject); |
| 984 | QCOMPARE(builder.relatedMetaObjectCount(), 1); |
| 985 | |
| 986 | // Check that nothing else changed. |
| 987 | QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::RelatedMetaObjects)); |
| 988 | } |
| 989 | |
| 990 | static void smetacall(QObject *, QMetaObject::Call, int, void **) |
| 991 | { |
| 992 | return; |
| 993 | } |
| 994 | |
| 995 | void tst_QMetaObjectBuilder::staticMetacall() |
| 996 | { |
| 997 | QMetaObjectBuilder builder; |
| 998 | QVERIFY(!builder.staticMetacallFunction()); |
| 999 | builder.setStaticMetacallFunction(smetacall); |
| 1000 | QVERIFY(builder.staticMetacallFunction() == smetacall); |
| 1001 | QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::StaticMetacall)); |
| 1002 | } |
| 1003 | |
| 1004 | // Copy the entire contents of a static QMetaObject and then check |
| 1005 | // that QMetaObjectBuilder will produce an exact copy as output. |
| 1006 | void tst_QMetaObjectBuilder::copyMetaObject() |
| 1007 | { |
| 1008 | QMetaObjectBuilder builder(&QObject::staticMetaObject); |
| 1009 | QMetaObject *meta = builder.toMetaObject(); |
| 1010 | QVERIFY(sameMetaObject(meta, &QObject::staticMetaObject)); |
| 1011 | free(ptr: meta); |
| 1012 | |
| 1013 | QMetaObjectBuilder builder2(&staticMetaObject); |
| 1014 | meta = builder2.toMetaObject(); |
| 1015 | QVERIFY(sameMetaObject(meta, &staticMetaObject)); |
| 1016 | free(ptr: meta); |
| 1017 | |
| 1018 | QMetaObjectBuilder builder3(&SomethingOfEverything::staticMetaObject); |
| 1019 | meta = builder3.toMetaObject(); |
| 1020 | QVERIFY(sameMetaObject(meta, &SomethingOfEverything::staticMetaObject)); |
| 1021 | free(ptr: meta); |
| 1022 | } |
| 1023 | |
| 1024 | // Serialize and deserialize a meta object and check that |
| 1025 | // it round-trips to the exact same value. |
| 1026 | void tst_QMetaObjectBuilder::serialize() |
| 1027 | { |
| 1028 | // Full QMetaObjectBuilder |
| 1029 | { |
| 1030 | QMetaObjectBuilder builder(&SomethingOfEverything::staticMetaObject); |
| 1031 | QMetaObject *meta = builder.toMetaObject(); |
| 1032 | |
| 1033 | QByteArray data; |
| 1034 | QDataStream stream(&data, QIODevice::WriteOnly | QIODevice::Append); |
| 1035 | builder.serialize(stream); |
| 1036 | |
| 1037 | QMetaObjectBuilder builder2; |
| 1038 | QDataStream stream2(data); |
| 1039 | QMap<QByteArray, const QMetaObject *> references; |
| 1040 | references.insert(akey: QByteArray("QLocale" ), avalue: &QLocale::staticMetaObject); |
| 1041 | builder2.deserialize(stream&: stream2, references); |
| 1042 | builder2.setStaticMetacallFunction(builder.staticMetacallFunction()); |
| 1043 | QMetaObject *meta2 = builder2.toMetaObject(); |
| 1044 | |
| 1045 | QVERIFY(sameMetaObject(meta, meta2)); |
| 1046 | free(ptr: meta); |
| 1047 | free(ptr: meta2); |
| 1048 | } |
| 1049 | |
| 1050 | // Partial QMetaObjectBuilder |
| 1051 | { |
| 1052 | QMetaObjectBuilder builder; |
| 1053 | builder.setClassName("Test" ); |
| 1054 | builder.addProperty(name: "foo" , type: "int" ); |
| 1055 | |
| 1056 | QByteArray data; |
| 1057 | QDataStream stream(&data, QIODevice::WriteOnly | QIODevice::Append); |
| 1058 | builder.serialize(stream); |
| 1059 | |
| 1060 | QMetaObjectBuilder builder2; |
| 1061 | QDataStream stream2(data); |
| 1062 | builder2.deserialize(stream&: stream2, references: QMap<QByteArray, const QMetaObject *>()); |
| 1063 | |
| 1064 | QCOMPARE(builder.superClass(), builder2.superClass()); |
| 1065 | QCOMPARE(builder.className(), builder2.className()); |
| 1066 | QCOMPARE(builder.propertyCount(), builder2.propertyCount()); |
| 1067 | QCOMPARE(builder.property(0).name(), builder2.property(0).name()); |
| 1068 | QCOMPARE(builder.property(0).type(), builder2.property(0).type()); |
| 1069 | } |
| 1070 | } |
| 1071 | |
| 1072 | void tst_QMetaObjectBuilder::relocatableData() |
| 1073 | { |
| 1074 | QMetaObjectBuilder builder; |
| 1075 | builder.setClassName("TestObject" ); |
| 1076 | |
| 1077 | QMetaMethodBuilder intPropChanged = builder.addSignal(signature: "intPropChanged(int)" ); |
| 1078 | intPropChanged.setParameterNames(QList<QByteArray>() << "newIntPropValue" ); |
| 1079 | |
| 1080 | QMetaPropertyBuilder prop = builder.addProperty(name: "intProp" , type: "int" ); |
| 1081 | prop.setNotifySignal(intPropChanged); |
| 1082 | |
| 1083 | QMetaMethodBuilder voidSlotInt = builder.addSlot(signature: "voidSlotInt(int)" ); |
| 1084 | voidSlotInt.setParameterNames(QList<QByteArray>() << "slotIntArg" ); |
| 1085 | |
| 1086 | QMetaMethodBuilder listInvokableQRealQString = builder.addMethod(signature: "listInvokableQRealQString(qreal,QString)" ); |
| 1087 | listInvokableQRealQString.setReturnType("QVariantList" ); |
| 1088 | listInvokableQRealQString.setParameterNames(QList<QByteArray>() << "qrealArg" << "qstringArg" ); |
| 1089 | |
| 1090 | bool ok = false; |
| 1091 | QByteArray data = builder.toRelocatableData(&ok); |
| 1092 | QVERIFY(ok); |
| 1093 | |
| 1094 | QMetaObjectBuilder builder2; |
| 1095 | QMetaObject meta2; |
| 1096 | builder2.fromRelocatableData(&meta2, &QObject::staticMetaObject, data); |
| 1097 | |
| 1098 | QMetaObject *meta = builder.toMetaObject(); |
| 1099 | |
| 1100 | QVERIFY(sameMetaObject(meta, &meta2)); |
| 1101 | |
| 1102 | QVERIFY(!meta2.d.extradata); |
| 1103 | QVERIFY(!meta2.d.relatedMetaObjects); |
| 1104 | QVERIFY(!meta2.d.static_metacall); |
| 1105 | |
| 1106 | free(ptr: meta); |
| 1107 | } |
| 1108 | |
| 1109 | |
| 1110 | // Check that removing a method updates notify signals appropriately |
| 1111 | void tst_QMetaObjectBuilder::removeNotifySignal() |
| 1112 | { |
| 1113 | QMetaObjectBuilder builder; |
| 1114 | |
| 1115 | builder.addSignal(signature: "foo(const QString&, int)" ); |
| 1116 | QMetaMethodBuilder method = builder.addSignal(signature: "bar(QString)" ); |
| 1117 | |
| 1118 | // Setup property |
| 1119 | QMetaPropertyBuilder prop = builder.addProperty(name: "prop" , type: "const QString &" ); |
| 1120 | prop.setNotifySignal(method); |
| 1121 | QVERIFY(prop.hasNotifySignal()); |
| 1122 | QCOMPARE(prop.notifySignal().index(), 1); |
| 1123 | |
| 1124 | // Remove non-notify signal |
| 1125 | builder.removeMethod(index: 0); |
| 1126 | QVERIFY(prop.hasNotifySignal()); |
| 1127 | QCOMPARE(prop.notifySignal().index(), 0); |
| 1128 | |
| 1129 | // Remove notify signal |
| 1130 | builder.removeMethod(index: 0); |
| 1131 | QVERIFY(!prop.hasNotifySignal()); |
| 1132 | } |
| 1133 | |
| 1134 | // Check that the only changes to a "builder" relative to the default |
| 1135 | // state is specified by "members". |
| 1136 | bool tst_QMetaObjectBuilder::checkForSideEffects |
| 1137 | (const QMetaObjectBuilder& builder, |
| 1138 | QMetaObjectBuilder::AddMembers members) |
| 1139 | { |
| 1140 | if ((members & QMetaObjectBuilder::ClassName) == 0) { |
| 1141 | if (!builder.className().isEmpty()) |
| 1142 | return false; |
| 1143 | } |
| 1144 | |
| 1145 | if ((members & QMetaObjectBuilder::SuperClass) == 0) { |
| 1146 | if (builder.superClass() != &QObject::staticMetaObject) |
| 1147 | return false; |
| 1148 | } |
| 1149 | |
| 1150 | if ((members & QMetaObjectBuilder::Methods) == 0) { |
| 1151 | if (builder.methodCount() != 0) |
| 1152 | return false; |
| 1153 | } |
| 1154 | |
| 1155 | if ((members & QMetaObjectBuilder::Constructors) == 0) { |
| 1156 | if (builder.constructorCount() != 0) |
| 1157 | return false; |
| 1158 | } |
| 1159 | |
| 1160 | if ((members & QMetaObjectBuilder::Properties) == 0) { |
| 1161 | if (builder.propertyCount() != 0) |
| 1162 | return false; |
| 1163 | } |
| 1164 | |
| 1165 | if ((members & QMetaObjectBuilder::Enumerators) == 0) { |
| 1166 | if (builder.enumeratorCount() != 0) |
| 1167 | return false; |
| 1168 | } |
| 1169 | |
| 1170 | if ((members & QMetaObjectBuilder::ClassInfos) == 0) { |
| 1171 | if (builder.classInfoCount() != 0) |
| 1172 | return false; |
| 1173 | } |
| 1174 | |
| 1175 | if ((members & QMetaObjectBuilder::RelatedMetaObjects) == 0) { |
| 1176 | if (builder.relatedMetaObjectCount() != 0) |
| 1177 | return false; |
| 1178 | } |
| 1179 | |
| 1180 | if ((members & QMetaObjectBuilder::StaticMetacall) == 0) { |
| 1181 | if (builder.staticMetacallFunction() != 0) |
| 1182 | return false; |
| 1183 | } |
| 1184 | |
| 1185 | return true; |
| 1186 | } |
| 1187 | |
| 1188 | static bool sameMethod(const QMetaMethod& method1, const QMetaMethod& method2) |
| 1189 | { |
| 1190 | if (method1.methodSignature() != method2.methodSignature()) |
| 1191 | return false; |
| 1192 | |
| 1193 | if (QByteArray(method1.typeName()) != QByteArray(method2.typeName())) |
| 1194 | return false; |
| 1195 | |
| 1196 | if (method1.parameterTypes() != method2.parameterTypes()) |
| 1197 | return false; |
| 1198 | |
| 1199 | if (method1.parameterNames() != method2.parameterNames()) |
| 1200 | return false; |
| 1201 | |
| 1202 | if (QByteArray(method1.tag()) != QByteArray(method2.tag())) |
| 1203 | return false; |
| 1204 | |
| 1205 | if (method1.access() != method2.access()) |
| 1206 | return false; |
| 1207 | |
| 1208 | if (method1.methodType() != method2.methodType()) |
| 1209 | return false; |
| 1210 | |
| 1211 | if (method1.attributes() != method2.attributes()) |
| 1212 | return false; |
| 1213 | |
| 1214 | if (method1.revision() != method2.revision()) |
| 1215 | return false; |
| 1216 | |
| 1217 | return true; |
| 1218 | } |
| 1219 | |
| 1220 | static bool sameProperty(const QMetaProperty& prop1, const QMetaProperty& prop2) |
| 1221 | { |
| 1222 | if (QByteArray(prop1.name()) != QByteArray(prop2.name())) |
| 1223 | return false; |
| 1224 | |
| 1225 | if (QByteArray(prop1.typeName()) != QByteArray(prop2.typeName())) |
| 1226 | return false; |
| 1227 | |
| 1228 | if (prop1.isReadable() != prop2.isReadable() || |
| 1229 | prop1.isWritable() != prop2.isWritable() || |
| 1230 | prop1.isResettable() != prop2.isResettable() || |
| 1231 | prop1.isDesignable() != prop2.isDesignable() || |
| 1232 | prop1.isScriptable() != prop2.isScriptable() || |
| 1233 | prop1.isStored() != prop2.isStored() || |
| 1234 | prop1.isEditable() != prop2.isEditable() || |
| 1235 | prop1.isUser() != prop2.isUser() || |
| 1236 | prop1.isFlagType() != prop2.isFlagType() || |
| 1237 | prop1.isEnumType() != prop2.isEnumType() || |
| 1238 | prop1.hasNotifySignal() != prop2.hasNotifySignal() || |
| 1239 | prop1.hasStdCppSet() != prop2.hasStdCppSet()) |
| 1240 | return false; |
| 1241 | |
| 1242 | if (prop1.hasNotifySignal()) { |
| 1243 | if (prop1.notifySignalIndex() != prop2.notifySignalIndex()) |
| 1244 | return false; |
| 1245 | } |
| 1246 | |
| 1247 | if (prop1.revision() != prop2.revision()) |
| 1248 | return false; |
| 1249 | |
| 1250 | return true; |
| 1251 | } |
| 1252 | |
| 1253 | static bool sameEnumerator(const QMetaEnum& enum1, const QMetaEnum& enum2) |
| 1254 | { |
| 1255 | if (QByteArray(enum1.name()) != QByteArray(enum2.name())) |
| 1256 | return false; |
| 1257 | |
| 1258 | if (enum1.isFlag() != enum2.isFlag()) |
| 1259 | return false; |
| 1260 | |
| 1261 | if (enum1.keyCount() != enum2.keyCount()) |
| 1262 | return false; |
| 1263 | |
| 1264 | for (int index = 0; index < enum1.keyCount(); ++index) { |
| 1265 | if (QByteArray(enum1.key(index)) != QByteArray(enum2.key(index))) |
| 1266 | return false; |
| 1267 | if (enum1.value(index) != enum2.value(index)) |
| 1268 | return false; |
| 1269 | } |
| 1270 | |
| 1271 | if (QByteArray(enum1.scope()) != QByteArray(enum2.scope())) |
| 1272 | return false; |
| 1273 | |
| 1274 | return true; |
| 1275 | } |
| 1276 | |
| 1277 | // Determine if two meta objects are identical. |
| 1278 | bool tst_QMetaObjectBuilder::sameMetaObject |
| 1279 | (const QMetaObject *meta1, const QMetaObject *meta2) |
| 1280 | { |
| 1281 | int index; |
| 1282 | |
| 1283 | if (strcmp(s1: meta1->className(), s2: meta2->className()) != 0) |
| 1284 | return false; |
| 1285 | |
| 1286 | if (meta1->superClass() != meta2->superClass()) |
| 1287 | return false; |
| 1288 | |
| 1289 | if (meta1->constructorCount() != meta2->constructorCount() || |
| 1290 | meta1->methodCount() != meta2->methodCount() || |
| 1291 | meta1->enumeratorCount() != meta2->enumeratorCount() || |
| 1292 | meta1->propertyCount() != meta2->propertyCount() || |
| 1293 | meta1->classInfoCount() != meta2->classInfoCount()) |
| 1294 | return false; |
| 1295 | |
| 1296 | for (index = 0; index < meta1->constructorCount(); ++index) { |
| 1297 | if (!sameMethod(method1: meta1->constructor(index), method2: meta2->constructor(index))) |
| 1298 | return false; |
| 1299 | } |
| 1300 | |
| 1301 | for (index = 0; index < meta1->methodCount(); ++index) { |
| 1302 | if (!sameMethod(method1: meta1->method(index), method2: meta2->method(index))) |
| 1303 | return false; |
| 1304 | } |
| 1305 | |
| 1306 | for (index = 0; index < meta1->propertyCount(); ++index) { |
| 1307 | if (!sameProperty(prop1: meta1->property(index), prop2: meta2->property(index))) |
| 1308 | return false; |
| 1309 | } |
| 1310 | |
| 1311 | for (index = 0; index < meta1->enumeratorCount(); ++index) { |
| 1312 | if (!sameEnumerator(enum1: meta1->enumerator(index), enum2: meta2->enumerator(index))) |
| 1313 | return false; |
| 1314 | } |
| 1315 | |
| 1316 | for (index = 0; index < meta1->classInfoCount(); ++index) { |
| 1317 | if (QByteArray(meta1->classInfo(index).name()) != |
| 1318 | QByteArray(meta2->classInfo(index).name())) |
| 1319 | return false; |
| 1320 | if (QByteArray(meta1->classInfo(index).value()) != |
| 1321 | QByteArray(meta2->classInfo(index).value())) |
| 1322 | return false; |
| 1323 | } |
| 1324 | |
| 1325 | const auto *objects1 = meta1->d.relatedMetaObjects; |
| 1326 | const auto *objects2 = meta2->d.relatedMetaObjects; |
| 1327 | if (objects1 && !objects2) |
| 1328 | return false; |
| 1329 | if (objects2 && !objects1) |
| 1330 | return false; |
| 1331 | if (objects1 && objects2) { |
| 1332 | while (*objects1 != 0 && *objects2 != 0) { |
| 1333 | if (*objects1 != *objects2) |
| 1334 | return false; |
| 1335 | ++objects1; |
| 1336 | ++objects2; |
| 1337 | } |
| 1338 | } |
| 1339 | |
| 1340 | return true; |
| 1341 | } |
| 1342 | |
| 1343 | |
| 1344 | // This class is used to test that the meta-object generated by QMOB can be |
| 1345 | // used by a real object. |
| 1346 | // The class manually implements the functions normally generated by moc, and |
| 1347 | // creates the corresponding meta-object using QMOB. The autotests check that |
| 1348 | // this object can be used by QObject/QMetaObject functionality (property |
| 1349 | // access, signals & slots, constructing instances, ...). |
| 1350 | |
| 1351 | class TestObject : public QObject |
| 1352 | { |
| 1353 | // Manually expanded from Q_OBJECT macro |
| 1354 | public: |
| 1355 | Q_OBJECT_CHECK |
| 1356 | static QMetaObject staticMetaObject; |
| 1357 | virtual const QMetaObject *metaObject() const; |
| 1358 | virtual void *qt_metacast(const char *); |
| 1359 | virtual int qt_metacall(QMetaObject::Call, int, void **); |
| 1360 | private: |
| 1361 | Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); |
| 1362 | |
| 1363 | //Q_PROPERTY(int intProp READ intProp WRITE setIntProp NOTIFY intPropChanged) |
| 1364 | public: |
| 1365 | TestObject(QObject *parent = 0); // Q_INVOKABLE |
| 1366 | ~TestObject(); |
| 1367 | |
| 1368 | // Property accessors |
| 1369 | int intProp() const; |
| 1370 | void setIntProp(int v); |
| 1371 | |
| 1372 | void emitIntPropChanged(); |
| 1373 | |
| 1374 | int voidSlotIntArgument() const; |
| 1375 | |
| 1376 | // Q_INVOKABLE |
| 1377 | QVariantList listInvokableQRealQString(qreal, const QString &); |
| 1378 | |
| 1379 | //public Q_SLOTS: |
| 1380 | void voidSlotInt(int); |
| 1381 | |
| 1382 | //Q_SIGNALS: |
| 1383 | void intPropChanged(int); |
| 1384 | |
| 1385 | private: |
| 1386 | static QMetaObject *buildMetaObject(); |
| 1387 | |
| 1388 | QMetaObject *m_metaObject; |
| 1389 | int m_intProp; |
| 1390 | int m_voidSlotIntArg; |
| 1391 | }; |
| 1392 | |
| 1393 | QMetaObject TestObject::staticMetaObject = { |
| 1394 | .d: { .superdata: nullptr, .stringdata: nullptr, .data: nullptr, .static_metacall: nullptr, .relatedMetaObjects: nullptr, .extradata: nullptr } |
| 1395 | }; |
| 1396 | |
| 1397 | TestObject::TestObject(QObject *parent) |
| 1398 | : QObject(parent), m_metaObject(buildMetaObject()), |
| 1399 | m_intProp(-1), m_voidSlotIntArg(-1) |
| 1400 | { |
| 1401 | staticMetaObject = *m_metaObject; |
| 1402 | } |
| 1403 | |
| 1404 | TestObject::~TestObject() |
| 1405 | { |
| 1406 | free(ptr: m_metaObject); |
| 1407 | } |
| 1408 | |
| 1409 | QMetaObject *TestObject::buildMetaObject() |
| 1410 | { |
| 1411 | QMetaObjectBuilder builder; |
| 1412 | // NOTE: If you change the meta-object, remember to adapt qt_metacall and |
| 1413 | // friends below accordingly. |
| 1414 | |
| 1415 | builder.setClassName("TestObject" ); |
| 1416 | |
| 1417 | builder.setStaticMetacallFunction(qt_static_metacall); |
| 1418 | |
| 1419 | QMetaMethodBuilder intPropChanged = builder.addSignal(signature: "intPropChanged(int)" ); |
| 1420 | intPropChanged.setParameterNames(QList<QByteArray>() << "newIntPropValue" ); |
| 1421 | |
| 1422 | QMetaPropertyBuilder prop = builder.addProperty(name: "intProp" , type: "int" ); |
| 1423 | prop.setNotifySignal(intPropChanged); |
| 1424 | |
| 1425 | QMetaMethodBuilder voidSlotInt = builder.addSlot(signature: "voidSlotInt(int)" ); |
| 1426 | voidSlotInt.setParameterNames(QList<QByteArray>() << "slotIntArg" ); |
| 1427 | |
| 1428 | QMetaMethodBuilder listInvokableQRealQString = builder.addMethod(signature: "listInvokableQRealQString(qreal,QString)" ); |
| 1429 | listInvokableQRealQString.setReturnType("QVariantList" ); |
| 1430 | listInvokableQRealQString.setParameterNames(QList<QByteArray>() << "qrealArg" << "qstringArg" ); |
| 1431 | |
| 1432 | builder.addConstructor(signature: "TestObject(QObject*)" ); |
| 1433 | builder.addConstructor(signature: "TestObject()" ); |
| 1434 | |
| 1435 | return builder.toMetaObject(); |
| 1436 | } |
| 1437 | |
| 1438 | int TestObject::intProp() const |
| 1439 | { |
| 1440 | return m_intProp; |
| 1441 | } |
| 1442 | |
| 1443 | void TestObject::setIntProp(int value) |
| 1444 | { |
| 1445 | if (m_intProp != value) { |
| 1446 | m_intProp = value; |
| 1447 | emit intPropChanged(value); |
| 1448 | } |
| 1449 | } |
| 1450 | |
| 1451 | void TestObject::emitIntPropChanged() |
| 1452 | { |
| 1453 | emit intPropChanged(m_intProp); |
| 1454 | } |
| 1455 | |
| 1456 | QVariantList TestObject::listInvokableQRealQString(qreal r, const QString &s) |
| 1457 | { |
| 1458 | return QVariantList() << r << s; |
| 1459 | } |
| 1460 | |
| 1461 | void TestObject::voidSlotInt(int value) |
| 1462 | { |
| 1463 | m_voidSlotIntArg = value; |
| 1464 | } |
| 1465 | |
| 1466 | int TestObject::voidSlotIntArgument() const |
| 1467 | { |
| 1468 | return m_voidSlotIntArg; |
| 1469 | } |
| 1470 | |
| 1471 | void TestObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) |
| 1472 | { |
| 1473 | if (_c == QMetaObject::CreateInstance) { |
| 1474 | switch (_id) { |
| 1475 | case 0: { TestObject *_r = new TestObject((*reinterpret_cast< QObject*(*)>(_a[1]))); |
| 1476 | if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break; |
| 1477 | case 1: { TestObject *_r = new TestObject(); |
| 1478 | if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break; |
| 1479 | default: { |
| 1480 | QMetaMethod ctor = _o->metaObject()->constructor(index: _id); |
| 1481 | qFatal(msg: "You forgot to add a case for CreateInstance %s" , ctor.methodSignature().constData()); |
| 1482 | } |
| 1483 | } |
| 1484 | } else if (_c == QMetaObject::InvokeMetaMethod) { |
| 1485 | Q_ASSERT(_o->metaObject()->cast(_o)); |
| 1486 | TestObject *_t = static_cast<TestObject *>(_o); |
| 1487 | switch (_id) { |
| 1488 | case 0: _t->intPropChanged((*reinterpret_cast< int(*)>(_a[1]))); break; |
| 1489 | case 1: _t->voidSlotInt(value: (*reinterpret_cast< int(*)>(_a[1]))); break; |
| 1490 | case 2: *reinterpret_cast<QVariantList(*)>(_a[0]) = _t->listInvokableQRealQString(r: *reinterpret_cast<qreal(*)>(_a[1]), s: *reinterpret_cast<QString(*)>(_a[2])); break; |
| 1491 | default: { |
| 1492 | QMetaMethod method = _o->metaObject()->method(index: _o->metaObject()->methodOffset() + _id); |
| 1493 | qFatal(msg: "You forgot to add a case for InvokeMetaMethod %s" , method.methodSignature().constData()); |
| 1494 | } |
| 1495 | } |
| 1496 | } else if (_c == QMetaObject::IndexOfMethod) { |
| 1497 | int *result = reinterpret_cast<int *>(_a[0]); |
| 1498 | void **func = reinterpret_cast<void **>(_a[1]); |
| 1499 | { |
| 1500 | typedef void (TestObject::*_t)(int ); |
| 1501 | if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TestObject::intPropChanged)) { |
| 1502 | *result = 0; |
| 1503 | } |
| 1504 | } |
| 1505 | { |
| 1506 | typedef void (TestObject::*_t)(int ); |
| 1507 | if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TestObject::voidSlotInt)) { |
| 1508 | *result = 1; |
| 1509 | } |
| 1510 | } |
| 1511 | { |
| 1512 | typedef QVariantList (TestObject::*_t)(qreal, const QString &); |
| 1513 | if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TestObject::listInvokableQRealQString)) { |
| 1514 | *result = 2; |
| 1515 | } |
| 1516 | } |
| 1517 | } |
| 1518 | } |
| 1519 | |
| 1520 | const QMetaObject *TestObject::metaObject() const |
| 1521 | { |
| 1522 | return m_metaObject; |
| 1523 | } |
| 1524 | |
| 1525 | void *TestObject::qt_metacast(const char *_clname) |
| 1526 | { |
| 1527 | if (!_clname) return 0; |
| 1528 | if (!strcmp(s1: _clname, s2: "TestObject" )) |
| 1529 | return static_cast<void*>(const_cast< TestObject*>(this)); |
| 1530 | return QObject::qt_metacast(_clname); |
| 1531 | } |
| 1532 | |
| 1533 | int TestObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a) |
| 1534 | { |
| 1535 | _id = QObject::qt_metacall(_c, _id, _a); |
| 1536 | if (_id < 0) |
| 1537 | return _id; |
| 1538 | int ownMethodCount = m_metaObject->methodCount() - m_metaObject->methodOffset(); |
| 1539 | int ownPropertyCount = m_metaObject->propertyCount() - m_metaObject->propertyOffset(); |
| 1540 | if (_c == QMetaObject::InvokeMetaMethod) { |
| 1541 | if (_id < ownMethodCount) |
| 1542 | qt_static_metacall(o: this, _c, _id, _a); |
| 1543 | _id -= ownMethodCount; |
| 1544 | } |
| 1545 | #ifndef QT_NO_PROPERTIES |
| 1546 | else if (_c == QMetaObject::ReadProperty) { |
| 1547 | void *_v = _a[0]; |
| 1548 | switch (_id) { |
| 1549 | case 0: *reinterpret_cast< int*>(_v) = intProp(); break; |
| 1550 | default: if (_id < ownPropertyCount) { |
| 1551 | QMetaProperty prop = m_metaObject->property(index: m_metaObject->propertyOffset() + _id); |
| 1552 | qFatal(msg: "You forgot to add a case for ReadProperty %s" , prop.name()); |
| 1553 | } |
| 1554 | } |
| 1555 | _id -= ownPropertyCount; |
| 1556 | } else if (_c == QMetaObject::WriteProperty) { |
| 1557 | void *_v = _a[0]; |
| 1558 | switch (_id) { |
| 1559 | case 0: setIntProp(*reinterpret_cast< int*>(_v)); break; |
| 1560 | default: if (_id < ownPropertyCount) { |
| 1561 | QMetaProperty prop = m_metaObject->property(index: m_metaObject->propertyOffset() + _id); |
| 1562 | qFatal(msg: "You forgot to add a case for WriteProperty %s" , prop.name()); |
| 1563 | } |
| 1564 | } |
| 1565 | _id -= ownPropertyCount; |
| 1566 | } else if (_c == QMetaObject::ResetProperty) { |
| 1567 | _id -= ownPropertyCount; |
| 1568 | } else if (_c == QMetaObject::QueryPropertyDesignable) { |
| 1569 | _id -= ownPropertyCount; |
| 1570 | } else if (_c == QMetaObject::QueryPropertyScriptable) { |
| 1571 | _id -= ownPropertyCount; |
| 1572 | } else if (_c == QMetaObject::QueryPropertyStored) { |
| 1573 | _id -= ownPropertyCount; |
| 1574 | } else if (_c == QMetaObject::QueryPropertyEditable) { |
| 1575 | _id -= ownPropertyCount; |
| 1576 | } else if (_c == QMetaObject::QueryPropertyUser) { |
| 1577 | _id -= ownPropertyCount; |
| 1578 | } |
| 1579 | #endif // QT_NO_PROPERTIES |
| 1580 | return _id; |
| 1581 | } |
| 1582 | |
| 1583 | // SIGNAL 0 |
| 1584 | void TestObject::intPropChanged(int _t1) |
| 1585 | { |
| 1586 | void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; |
| 1587 | QMetaObject::activate(sender: this, m_metaObject, local_signal_index: 0, argv: _a); |
| 1588 | } |
| 1589 | |
| 1590 | |
| 1591 | void tst_QMetaObjectBuilder::usage_signal() |
| 1592 | { |
| 1593 | QScopedPointer<TestObject> testObject(new TestObject); |
| 1594 | |
| 1595 | QSignalSpy propChangedSpy(testObject.data(), &TestObject::intPropChanged); |
| 1596 | testObject->emitIntPropChanged(); |
| 1597 | QCOMPARE(propChangedSpy.count(), 1); |
| 1598 | QCOMPARE(propChangedSpy.at(0).count(), 1); |
| 1599 | QCOMPARE(propChangedSpy.at(0).at(0).toInt(), testObject->intProp()); |
| 1600 | } |
| 1601 | |
| 1602 | void tst_QMetaObjectBuilder::usage_property() |
| 1603 | { |
| 1604 | QScopedPointer<TestObject> testObject(new TestObject); |
| 1605 | |
| 1606 | QVariant prop = testObject->property(name: "intProp" ); |
| 1607 | QCOMPARE(prop.type(), QVariant::Int); |
| 1608 | QCOMPARE(prop.toInt(), testObject->intProp()); |
| 1609 | |
| 1610 | QSignalSpy propChangedSpy(testObject.data(), &TestObject::intPropChanged); |
| 1611 | QVERIFY(testObject->intProp() != 123); |
| 1612 | testObject->setProperty(name: "intProp" , value: 123); |
| 1613 | QCOMPARE(propChangedSpy.count(), 1); |
| 1614 | prop = testObject->property(name: "intProp" ); |
| 1615 | QCOMPARE(prop.type(), QVariant::Int); |
| 1616 | QCOMPARE(prop.toInt(), 123); |
| 1617 | } |
| 1618 | |
| 1619 | void tst_QMetaObjectBuilder::usage_slot() |
| 1620 | { |
| 1621 | QScopedPointer<TestObject> testObject(new TestObject); |
| 1622 | |
| 1623 | int index = testObject->metaObject()->indexOfMethod(method: "voidSlotInt(int)" ); |
| 1624 | QVERIFY(index != -1); |
| 1625 | QMetaMethod voidSlotInt = testObject->metaObject()->method(index); |
| 1626 | |
| 1627 | QCOMPARE(testObject->voidSlotIntArgument(), -1); |
| 1628 | QVERIFY(voidSlotInt.invoke(testObject.data(), Q_ARG(int, 123))); |
| 1629 | QCOMPARE(testObject->voidSlotIntArgument(), 123); |
| 1630 | } |
| 1631 | |
| 1632 | void tst_QMetaObjectBuilder::usage_method() |
| 1633 | { |
| 1634 | QScopedPointer<TestObject> testObject(new TestObject); |
| 1635 | |
| 1636 | int index = testObject->metaObject()->indexOfMethod(method: "listInvokableQRealQString(qreal,QString)" ); |
| 1637 | QVERIFY(index != -1); |
| 1638 | QMetaMethod listInvokableQRealQString = testObject->metaObject()->method(index); |
| 1639 | QVariantList list; |
| 1640 | QVERIFY(listInvokableQRealQString.invoke(testObject.data(), Q_RETURN_ARG(QVariantList, list), |
| 1641 | Q_ARG(qreal, 123.0), Q_ARG(QString, "ciao" ))); |
| 1642 | QCOMPARE(list.size(), 2); |
| 1643 | QCOMPARE(list.at(0).type(), QVariant::Type(QMetaType::QReal)); |
| 1644 | QCOMPARE(list.at(0).toDouble(), double(123)); |
| 1645 | QCOMPARE(list.at(1).type(), QVariant::String); |
| 1646 | QCOMPARE(list.at(1).toString(), QString::fromLatin1("ciao" )); |
| 1647 | } |
| 1648 | |
| 1649 | void tst_QMetaObjectBuilder::usage_constructor() |
| 1650 | { |
| 1651 | QScopedPointer<TestObject> testObject(new TestObject); |
| 1652 | |
| 1653 | QCOMPARE(testObject->metaObject()->constructorCount(), 2); |
| 1654 | QScopedPointer<QObject> testInstance(testObject->metaObject()->newInstance()); |
| 1655 | QVERIFY(testInstance != 0); |
| 1656 | QScopedPointer<QObject> testInstance2(testObject->metaObject()->newInstance(Q_ARG(QObject*, testInstance.data()))); |
| 1657 | QVERIFY(testInstance2 != 0); |
| 1658 | QCOMPARE(testInstance2->parent(), testInstance.data()); |
| 1659 | } |
| 1660 | |
| 1661 | void tst_QMetaObjectBuilder::usage_connect() |
| 1662 | { |
| 1663 | QScopedPointer<TestObject> testObject(new TestObject); |
| 1664 | |
| 1665 | QVERIFY(QObject::connect(testObject.data(), SIGNAL(intPropChanged(int)), |
| 1666 | testObject.data(), SLOT(voidSlotInt(int)))); |
| 1667 | |
| 1668 | QCOMPARE(testObject->voidSlotIntArgument(), -1); |
| 1669 | testObject->setProperty(name: "intProp" , value: 123); |
| 1670 | QCOMPARE(testObject->voidSlotIntArgument(), 123); |
| 1671 | |
| 1672 | QVERIFY(QObject::disconnect(testObject.data(), SIGNAL(intPropChanged(int)), |
| 1673 | testObject.data(), SLOT(voidSlotInt(int)))); |
| 1674 | } |
| 1675 | |
| 1676 | void tst_QMetaObjectBuilder::usage_templateConnect() |
| 1677 | { |
| 1678 | QScopedPointer<TestObject> testObject(new TestObject); |
| 1679 | |
| 1680 | QMetaObject::Connection con = QObject::connect(sender: testObject.data(), signal: &TestObject::intPropChanged, |
| 1681 | receiver: testObject.data(), slot: &TestObject::voidSlotInt); |
| 1682 | QVERIFY(con); |
| 1683 | |
| 1684 | QCOMPARE(testObject->voidSlotIntArgument(), -1); |
| 1685 | testObject->setProperty(name: "intProp" , value: 123); |
| 1686 | QCOMPARE(testObject->voidSlotIntArgument(), 123); |
| 1687 | |
| 1688 | QVERIFY(QObject::disconnect(testObject.data(), &TestObject::intPropChanged, |
| 1689 | testObject.data(), &TestObject::voidSlotInt)); |
| 1690 | |
| 1691 | // Something that isn't a signal |
| 1692 | QTest::ignoreMessage(type: QtWarningMsg, message: "QObject::connect: signal not found in TestObject" ); |
| 1693 | con = QObject::connect(sender: testObject.data(), signal: &TestObject::setIntProp, |
| 1694 | receiver: testObject.data(), slot: &TestObject::intPropChanged); |
| 1695 | QVERIFY(!con); |
| 1696 | } |
| 1697 | |
| 1698 | void tst_QMetaObjectBuilder::classNameFirstInStringData() |
| 1699 | { |
| 1700 | QMetaObjectBuilder builder; |
| 1701 | builder.addMetaObject(prototype: &SomethingOfEverything::staticMetaObject); |
| 1702 | builder.setClassName(QByteArrayLiteral("TestClass" )); |
| 1703 | QMetaObject *mo = builder.toMetaObject(); |
| 1704 | |
| 1705 | QByteArrayDataPtr ; |
| 1706 | header.ptr = const_cast<QByteArrayData*>(mo->d.stringdata); |
| 1707 | QCOMPARE(QByteArray(header), QByteArrayLiteral("TestClass" )); |
| 1708 | |
| 1709 | free(ptr: mo); |
| 1710 | } |
| 1711 | |
| 1712 | QTEST_MAIN(tst_QMetaObjectBuilder) |
| 1713 | |
| 1714 | #include "tst_qmetaobjectbuilder.moc" |
| 1715 | |