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 | |