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 <QtQml/qqmlcomponent.h> |
31 | #include <QtQml/qqmlengine.h> |
32 | #include "../../shared/util.h" |
33 | |
34 | Q_DECLARE_METATYPE(QMetaMethod::MethodType) |
35 | |
36 | class MyQmlObject : public QObject |
37 | { |
38 | Q_OBJECT |
39 | }; |
40 | QML_DECLARE_TYPE(MyQmlObject) |
41 | |
42 | class tst_QQmlMetaObject : public QQmlDataTest |
43 | { |
44 | Q_OBJECT |
45 | private slots: |
46 | void initTestCase(); |
47 | |
48 | void property_data(); |
49 | void property(); |
50 | void method_data(); |
51 | void method(); |
52 | |
53 | private: |
54 | MyQmlObject myQmlObject; |
55 | }; |
56 | |
57 | void tst_QQmlMetaObject::initTestCase() |
58 | { |
59 | QQmlDataTest::initTestCase(); |
60 | |
61 | qmlRegisterType<MyQmlObject>(uri: "Qt.test" , versionMajor: 1,versionMinor: 0, qmlName: "MyQmlObject" ); |
62 | } |
63 | |
64 | void tst_QQmlMetaObject::property_data() |
65 | { |
66 | QTest::addColumn<QString>(name: "testFile" ); |
67 | QTest::addColumn<QByteArray>(name: "cppTypeName" ); |
68 | QTest::addColumn<int>(name: "cppType" ); |
69 | QTest::addColumn<bool>(name: "isDefault" ); |
70 | QTest::addColumn<QVariant>(name: "expectedValue" ); |
71 | QTest::addColumn<bool>(name: "isWritable" ); |
72 | QTest::addColumn<QVariant>(name: "newValue" ); |
73 | |
74 | QTest::newRow(dataTag: "int" ) << "property.int.qml" |
75 | << QByteArray("int" ) << int(QMetaType::Int) |
76 | << false // default |
77 | << QVariant(19) << true << QVariant(42); |
78 | QTest::newRow(dataTag: "bool" ) << "property.bool.qml" |
79 | << QByteArray("bool" ) << int(QMetaType::Bool) |
80 | << true // default |
81 | << QVariant(true) << true << QVariant(false); |
82 | QTest::newRow(dataTag: "double" ) << "property.double.qml" |
83 | << QByteArray("double" ) << int(QMetaType::Double) |
84 | << false // default |
85 | << QVariant(double(1234567890.)) |
86 | << true // writable |
87 | << QVariant(double(1.23456789)); |
88 | QTest::newRow(dataTag: "real" ) << "property.real.qml" |
89 | << QByteArray("double" ) << int(QMetaType::Double) |
90 | << false // default |
91 | << QVariant(double(1234567890.)) |
92 | << true // writable |
93 | << QVariant(double(1.23456789)); |
94 | QTest::newRow(dataTag: "string" ) << "property.string.qml" |
95 | << QByteArray("QString" ) << int(QMetaType::QString) |
96 | << true // default |
97 | << QVariant(QString::fromLatin1(str: "dog" )) |
98 | << true // writable |
99 | << QVariant(QString::fromLatin1(str: "food" )); |
100 | QTest::newRow(dataTag: "url" ) << "property.url.qml" |
101 | << QByteArray("QUrl" ) << int(QMetaType::QUrl) |
102 | << false // default |
103 | << QVariant(QUrl("http://foo.bar" )) |
104 | << true //writable |
105 | << QVariant(QUrl("http://bar.baz" )); |
106 | QTest::newRow(dataTag: "color" ) << "property.color.qml" |
107 | << QByteArray("QColor" ) << int(QMetaType::QColor) |
108 | << true // default |
109 | << QVariant(QColor("#ff0000" )) |
110 | << true // writable |
111 | << QVariant(QColor("#00ff00" )); |
112 | QTest::newRow(dataTag: "date" ) << "property.date.qml" |
113 | << QByteArray("QDateTime" ) << int(QMetaType::QDateTime) |
114 | << false // default |
115 | << QVariant(QDate(2012, 2, 7).startOfDay()) |
116 | << true // writable |
117 | << QVariant(QDate(2010, 7, 2).startOfDay()); |
118 | QTest::newRow(dataTag: "variant" ) << "property.variant.qml" |
119 | << QByteArray("QVariant" ) << int(QMetaType::QVariant) |
120 | << true // default |
121 | << QVariant(QPointF(12, 34)) |
122 | << true // writable |
123 | << QVariant(QSizeF(45, 67)); |
124 | QTest::newRow(dataTag: "var" ) << "property.var.qml" |
125 | << QByteArray("QVariant" ) << int(QMetaType::QVariant) |
126 | << false // default |
127 | << QVariant(QVariantList() << 5 << true << "ciao" ) |
128 | << true // writable |
129 | << QVariant(QVariantList() << 17.0); |
130 | QTest::newRow(dataTag: "QtObject" ) << "property.QtObject.qml" |
131 | << QByteArray("QObject*" ) << int(QMetaType::QObjectStar) |
132 | << false // default |
133 | << QVariant() |
134 | << true // writable |
135 | << QVariant::fromValue(value: static_cast<QObject*>(this)); |
136 | QTest::newRow(dataTag: "list<QtObject>" ) << "property.list.QtObject.qml" |
137 | << QByteArray("QQmlListProperty<QObject>" ) |
138 | << qMetaTypeId<QQmlListProperty<QObject> >() |
139 | << false // default |
140 | << QVariant() |
141 | << false // writable |
142 | << QVariant(); |
143 | QTest::newRow(dataTag: "MyQmlObject" ) << "property.MyQmlObject.qml" |
144 | << QByteArray("MyQmlObject*" ) << qMetaTypeId<MyQmlObject*>() |
145 | << false // default |
146 | << QVariant() |
147 | << true // writable |
148 | << QVariant::fromValue(value: &myQmlObject); |
149 | QTest::newRow(dataTag: "list<MyQmlObject>" ) << "property.list.MyQmlObject.qml" |
150 | << QByteArray("QQmlListProperty<MyQmlObject>" ) |
151 | << qMetaTypeId<QQmlListProperty<MyQmlObject> >() |
152 | << false // default |
153 | << QVariant() |
154 | << false // writable |
155 | << QVariant(); |
156 | QTest::newRow(dataTag: "alias" ) << "property.alias.qml" |
157 | << QByteArray("QString" ) << int(QMetaType::QString) |
158 | << false // default |
159 | << QVariant(QString::fromLatin1(str: "Joe" )) |
160 | << true // writable |
161 | << QVariant(QString::fromLatin1(str: "Bob" )); |
162 | QTest::newRow(dataTag: "alias-2" ) << "property.alias.2.qml" |
163 | << QByteArray("QObject*" ) << int(QMetaType::QObjectStar) |
164 | << false // default |
165 | << QVariant() |
166 | << false // writable |
167 | << QVariant(); |
168 | QTest::newRow(dataTag: "alias-3" ) << "property.alias.3.qml" |
169 | << QByteArray("QString" ) << int(QMetaType::QString) |
170 | << false // default |
171 | << QVariant(QString::fromLatin1(str: "Arial" )) |
172 | << true // writable |
173 | << QVariant(QString::fromLatin1(str: "Helvetica" )); |
174 | } |
175 | |
176 | void tst_QQmlMetaObject::property() |
177 | { |
178 | QFETCH(QString, testFile); |
179 | QFETCH(QByteArray, cppTypeName); |
180 | QFETCH(int, cppType); |
181 | QFETCH(bool, isDefault); |
182 | QFETCH(QVariant, expectedValue); |
183 | QFETCH(bool, isWritable); |
184 | QFETCH(QVariant, newValue); |
185 | |
186 | QQmlEngine engine; |
187 | QQmlComponent component(&engine, testFileUrl(fileName: testFile)); |
188 | QObject *object = component.create(); |
189 | QVERIFY(object != nullptr); |
190 | |
191 | const QMetaObject *mo = object->metaObject(); |
192 | QVERIFY(mo->superClass() != nullptr); |
193 | QVERIFY(QByteArray(mo->className()).contains("_QML_" )); |
194 | QCOMPARE(mo->propertyOffset(), mo->superClass()->propertyCount()); |
195 | QCOMPARE(mo->propertyCount(), mo->superClass()->propertyCount() + 1); |
196 | |
197 | QMetaProperty prop = mo->property(index: mo->propertyOffset()); |
198 | QCOMPARE(prop.name(), "test" ); |
199 | |
200 | QCOMPARE(QByteArray(prop.typeName()), cppTypeName); |
201 | if (prop.userType() < QMetaType::User) |
202 | QCOMPARE(prop.type(), QVariant::Type(cppType)); |
203 | else |
204 | QCOMPARE(prop.type(), QVariant::UserType); |
205 | QCOMPARE(prop.userType(), cppType); |
206 | |
207 | QVERIFY(!prop.isConstant()); |
208 | QVERIFY(!prop.isDesignable()); |
209 | QVERIFY(!prop.isEnumType()); |
210 | QVERIFY(!prop.isFinal()); |
211 | QVERIFY(!prop.isFlagType()); |
212 | QVERIFY(prop.isReadable()); |
213 | QVERIFY(!prop.isResettable()); |
214 | QVERIFY(prop.isScriptable()); |
215 | QVERIFY(!prop.isStored()); |
216 | QVERIFY(!prop.isUser()); |
217 | QVERIFY(prop.isValid()); |
218 | QCOMPARE(prop.isWritable(), isWritable); |
219 | |
220 | QCOMPARE(mo->classInfoOffset(), mo->superClass()->classInfoCount()); |
221 | QCOMPARE(mo->classInfoCount(), mo->superClass()->classInfoCount() + (isDefault ? 1 : 0)); |
222 | if (isDefault) { |
223 | QMetaClassInfo info = mo->classInfo(index: mo->classInfoOffset()); |
224 | QCOMPARE(info.name(), "DefaultProperty" ); |
225 | QCOMPARE(info.value(), "test" ); |
226 | } |
227 | |
228 | QCOMPARE(mo->methodOffset(), mo->superClass()->methodCount()); |
229 | QCOMPARE(mo->methodCount(), mo->superClass()->methodCount() + 1); // the signal |
230 | |
231 | QVERIFY(prop.notifySignalIndex() != -1); |
232 | QMetaMethod signal = prop.notifySignal(); |
233 | QCOMPARE(signal.methodType(), QMetaMethod::Signal); |
234 | QCOMPARE(signal.name(), QByteArray("testChanged" )); |
235 | QCOMPARE(signal.methodSignature(), QByteArray("testChanged()" )); |
236 | QCOMPARE(signal.access(), QMetaMethod::Public); |
237 | QCOMPARE(signal.parameterCount(), 0); |
238 | QCOMPARE(signal.parameterTypes(), QList<QByteArray>()); |
239 | QCOMPARE(signal.parameterNames(), QList<QByteArray>()); |
240 | QCOMPARE(signal.tag(), "" ); |
241 | QCOMPARE(signal.typeName(), "void" ); |
242 | QCOMPARE(signal.returnType(), int(QMetaType::Void)); |
243 | |
244 | QSignalSpy changedSpy(object, SIGNAL(testChanged())); |
245 | QObject::connect(sender: object, SIGNAL(testChanged()), receiver: object, SLOT(deleteLater())); |
246 | |
247 | QVariant value = prop.read(obj: object); |
248 | if (value.userType() == qMetaTypeId<QJSValue>()) |
249 | value = value.value<QJSValue>().toVariant(); |
250 | if (expectedValue.isValid()) |
251 | QCOMPARE(value, expectedValue); |
252 | else |
253 | QVERIFY(value.isValid()); |
254 | QCOMPARE(changedSpy.count(), 0); |
255 | |
256 | if (isWritable) { |
257 | QVERIFY(prop.write(object, newValue)); |
258 | QCOMPARE(changedSpy.count(), 1); |
259 | QVariant value = prop.read(obj: object); |
260 | if (value.userType() == qMetaTypeId<QJSValue>()) |
261 | value = value.value<QJSValue>().toVariant(); |
262 | QCOMPARE(value, newValue); |
263 | } else { |
264 | QVERIFY(!prop.write(object, prop.read(object))); |
265 | QCOMPARE(changedSpy.count(), 0); |
266 | } |
267 | |
268 | delete object; |
269 | } |
270 | |
271 | void tst_QQmlMetaObject::method_data() |
272 | { |
273 | QTest::addColumn<QString>(name: "testFile" ); |
274 | QTest::addColumn<QString>(name: "signature" ); |
275 | QTest::addColumn<QMetaMethod::MethodType>(name: "methodType" ); |
276 | QTest::addColumn<int>(name: "returnType" ); |
277 | QTest::addColumn<QString>(name: "returnTypeName" ); |
278 | QTest::addColumn<QList<int> >(name: "parameterTypes" ); |
279 | QTest::addColumn<QList<QByteArray> >(name: "parameterTypeNames" ); |
280 | QTest::addColumn<QList<QByteArray> >(name: "parameterNames" ); |
281 | |
282 | QTest::newRow(dataTag: "testFunction()" ) << "method.1.qml" |
283 | << "testFunction()" |
284 | << QMetaMethod::Slot |
285 | << int(QMetaType::QVariant) << "QVariant" |
286 | << QList<int>() |
287 | << QList<QByteArray>() |
288 | << QList<QByteArray>(); |
289 | QTest::newRow(dataTag: "testFunction(foo)" ) << "method.2.qml" |
290 | << "testFunction(QVariant)" |
291 | << QMetaMethod::Slot |
292 | << int(QMetaType::QVariant) << "QVariant" |
293 | << (QList<int>() << QMetaType::QVariant) |
294 | << (QList<QByteArray>() << "QVariant" ) |
295 | << (QList<QByteArray>() << "foo" ); |
296 | QTest::newRow(dataTag: "testFunction(foo, bar, baz)" ) << "method.3.qml" |
297 | << "testFunction(QVariant,QVariant,QVariant)" |
298 | << QMetaMethod::Slot |
299 | << int(QMetaType::QVariant) << "QVariant" |
300 | << (QList<int>() << QMetaType::QVariant << QMetaType::QVariant << QMetaType::QVariant) |
301 | << (QList<QByteArray>() << "QVariant" << "QVariant" << "QVariant" ) |
302 | << (QList<QByteArray>() << "foo" << "bar" << "baz" ); |
303 | QTest::newRow(dataTag: "testSignal" ) << "signal.1.qml" |
304 | << "testSignal()" |
305 | << QMetaMethod::Signal |
306 | << int(QMetaType::Void) << "void" |
307 | << QList<int>() |
308 | << QList<QByteArray>() |
309 | << QList<QByteArray>(); |
310 | QTest::newRow(dataTag: "testSignal(string foo)" ) << "signal.2.qml" |
311 | << "testSignal(QString)" |
312 | << QMetaMethod::Signal |
313 | << int(QMetaType::Void) << "void" |
314 | << (QList<int>() << QMetaType::QString) |
315 | << (QList<QByteArray>() << "QString" ) |
316 | << (QList<QByteArray>() << "foo" ); |
317 | QTest::newRow(dataTag: "testSignal(int foo, bool bar, real baz)" ) << "signal.3.qml" |
318 | << "testSignal(int,bool,double)" |
319 | << QMetaMethod::Signal |
320 | << int(QMetaType::Void) << "void" |
321 | << (QList<int>() << QMetaType::Int << QMetaType::Bool << QMetaType::Double) |
322 | << (QList<QByteArray>() << "int" << "bool" << "double" ) |
323 | << (QList<QByteArray>() << "foo" << "bar" << "baz" ); |
324 | QTest::newRow(dataTag: "testSignal(variant foo, var bar)" ) << "signal.4.qml" |
325 | << "testSignal(QVariant,QVariant)" |
326 | << QMetaMethod::Signal |
327 | << int(QMetaType::Void) << "void" |
328 | << (QList<int>() << QMetaType::QVariant << QMetaType::QVariant) |
329 | << (QList<QByteArray>() << "QVariant" << "QVariant" ) |
330 | << (QList<QByteArray>() << "foo" << "bar" ); |
331 | QTest::newRow(dataTag: "testSignal(color foo, date bar, url baz)" ) << "signal.5.qml" |
332 | << "testSignal(QColor,QDateTime,QUrl)" |
333 | << QMetaMethod::Signal |
334 | << int(QMetaType::Void) << "void" |
335 | << (QList<int>() << QMetaType::QColor << QMetaType::QDateTime << QMetaType::QUrl) |
336 | << (QList<QByteArray>() << "QColor" << "QDateTime" << "QUrl" ) |
337 | << (QList<QByteArray>() << "foo" << "bar" << "baz" ); |
338 | QTest::newRow(dataTag: "testSignal(double foo)" ) << "signal.6.qml" |
339 | << "testSignal(double)" |
340 | << QMetaMethod::Signal |
341 | << int(QMetaType::Void) << "void" |
342 | << (QList<int>() << QMetaType::Double) |
343 | << (QList<QByteArray>() << "double" ) |
344 | << (QList<QByteArray>() << "foo" ); |
345 | } |
346 | |
347 | void tst_QQmlMetaObject::method() |
348 | { |
349 | QFETCH(QString, testFile); |
350 | QFETCH(QString, signature); |
351 | QFETCH(QMetaMethod::MethodType, methodType); |
352 | QFETCH(int, returnType); |
353 | QFETCH(QString, returnTypeName); |
354 | QFETCH(QList<int>, parameterTypes); |
355 | QFETCH(QList<QByteArray>, parameterTypeNames); |
356 | QFETCH(QList<QByteArray>, parameterNames); |
357 | |
358 | QCOMPARE(parameterTypes.size(), parameterTypeNames.size()); |
359 | QCOMPARE(parameterTypeNames.size(), parameterNames.size()); |
360 | |
361 | QQmlEngine engine; |
362 | QQmlComponent component(&engine, testFileUrl(fileName: testFile)); |
363 | QObject *object = component.create(); |
364 | QVERIFY(object != nullptr); |
365 | |
366 | const QMetaObject *mo = object->metaObject(); |
367 | QVERIFY(mo->superClass() != nullptr); |
368 | QVERIFY(QByteArray(mo->className()).contains("_QML_" )); |
369 | QCOMPARE(mo->methodOffset(), mo->superClass()->methodCount()); |
370 | QCOMPARE(mo->methodCount(), mo->superClass()->methodCount() + 1); |
371 | |
372 | QMetaMethod method = mo->method(index: mo->methodOffset()); |
373 | QCOMPARE(method.methodType(), methodType); |
374 | QCOMPARE(QString::fromUtf8(method.methodSignature().constData()), signature); |
375 | QCOMPARE(method.access(), QMetaMethod::Public); |
376 | |
377 | QString computedName = signature.left(n: signature.indexOf(c: '(')); |
378 | QCOMPARE(QString::fromUtf8(method.name()), computedName); |
379 | |
380 | QCOMPARE(method.parameterCount(), parameterTypes.size()); |
381 | for (int i = 0; i < parameterTypes.size(); ++i) |
382 | QCOMPARE(method.parameterType(i), parameterTypes.at(i)); |
383 | QCOMPARE(method.parameterTypes(), parameterTypeNames); |
384 | QCOMPARE(method.tag(), "" ); |
385 | |
386 | QCOMPARE(QString::fromUtf8(method.typeName()), returnTypeName); |
387 | QCOMPARE(method.returnType(), returnType); |
388 | |
389 | delete object; |
390 | } |
391 | |
392 | QTEST_MAIN(tst_QQmlMetaObject) |
393 | |
394 | #include "tst_qqmlmetaobject.moc" |
395 | |