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 <qstandardpaths.h>
30#include <qtest.h>
31#include <qqml.h>
32#include <qqmlprivate.h>
33#include <qqmlengine.h>
34#include <qqmlcomponent.h>
35
36#include <private/qqmlmetatype_p.h>
37#include <private/qqmlpropertyvalueinterceptor_p.h>
38#include <private/qqmlengine_p.h>
39#include "../../shared/util.h"
40
41class tst_qqmlmetatype : public QQmlDataTest
42{
43 Q_OBJECT
44public:
45 tst_qqmlmetatype() {}
46
47private slots:
48 void initTestCase();
49
50 void qmlParserStatusCast();
51 void qmlPropertyValueSourceCast();
52 void qmlPropertyValueInterceptorCast();
53 void qmlType();
54 void invalidQmlTypeName();
55 void prettyTypeName();
56 void registrationType();
57 void compositeType();
58 void externalEnums();
59
60 void isList();
61
62 void defaultObject();
63 void unregisterCustomType();
64 void unregisterCustomSingletonType();
65
66 void normalizeUrls();
67 void unregisterAttachedProperties();
68 void revisionedGroupedProperties();
69
70 void enumsInRecursiveImport_data();
71 void enumsInRecursiveImport();
72};
73
74class TestType : public QObject
75{
76 Q_OBJECT
77 Q_PROPERTY(int foo READ foo)
78
79 Q_CLASSINFO("DefaultProperty", "foo")
80public:
81 int foo() { return 0; }
82};
83QML_DECLARE_TYPE(TestType);
84
85class TestType2 : public QObject
86{
87 Q_OBJECT
88};
89
90class TestType3 : public QObject
91{
92 Q_OBJECT
93};
94
95class ExternalEnums : public QObject
96{
97 Q_OBJECT
98 Q_ENUMS(QStandardPaths::StandardLocation QStandardPaths::LocateOptions)
99public:
100 ExternalEnums(QObject *parent = nullptr) : QObject(parent) {}
101
102 static QObject *create(QQmlEngine *engine, QJSEngine *scriptEngine) {
103 Q_UNUSED(scriptEngine);
104 return new ExternalEnums(engine);
105 }
106};
107QML_DECLARE_TYPE(ExternalEnums);
108
109QObject *testTypeProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
110{
111 Q_UNUSED(engine);
112 Q_UNUSED(scriptEngine);
113 return new TestType();
114}
115
116class ParserStatusTestType : public QObject, public QQmlParserStatus
117{
118 Q_OBJECT
119 void classBegin(){}
120 void componentComplete(){}
121 Q_CLASSINFO("DefaultProperty", "foo") // Missing default property
122 Q_INTERFACES(QQmlParserStatus)
123};
124QML_DECLARE_TYPE(ParserStatusTestType);
125
126class ValueSourceTestType : public QObject, public QQmlPropertyValueSource
127{
128 Q_OBJECT
129 Q_INTERFACES(QQmlPropertyValueSource)
130public:
131 virtual void setTarget(const QQmlProperty &) {}
132};
133QML_DECLARE_TYPE(ValueSourceTestType);
134
135class ValueInterceptorTestType : public QObject, public QQmlPropertyValueInterceptor
136{
137 Q_OBJECT
138 Q_INTERFACES(QQmlPropertyValueInterceptor)
139public:
140 virtual void setTarget(const QQmlProperty &) {}
141 virtual void write(const QVariant &) {}
142};
143QML_DECLARE_TYPE(ValueInterceptorTestType);
144
145void tst_qqmlmetatype::initTestCase()
146{
147 QQmlDataTest::initTestCase();
148 qmlRegisterType<TestType>(uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "TestType");
149 qmlRegisterSingletonType<TestType>(uri: "Test", versionMajor: 1, versionMinor: 0, typeName: "TestTypeSingleton", callback: testTypeProvider);
150 qmlRegisterType<ParserStatusTestType>(uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "ParserStatusTestType");
151 qmlRegisterType<ValueSourceTestType>(uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "ValueSourceTestType");
152 qmlRegisterType<ValueInterceptorTestType>(uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "ValueInterceptorTestType");
153
154 QUrl testTypeUrl(testFileUrl(fileName: "CompositeType.qml"));
155 qmlRegisterType(url: testTypeUrl, uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "TestTypeComposite");
156}
157
158void tst_qqmlmetatype::qmlParserStatusCast()
159{
160 QVERIFY(!QQmlMetaType::qmlType(QMetaType::Int).isValid());
161 QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).isValid());
162 QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).parserStatusCast(), -1);
163 QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ValueSourceTestType *>()).isValid());
164 QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<ValueSourceTestType *>()).parserStatusCast(), -1);
165
166 QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()).isValid());
167 int cast = QQmlMetaType::qmlType(typeId: qMetaTypeId<ParserStatusTestType *>()).parserStatusCast();
168 QVERIFY(cast != -1);
169 QVERIFY(cast != 0);
170
171 ParserStatusTestType t;
172 QVERIFY(reinterpret_cast<char *>((QObject *)&t) != reinterpret_cast<char *>((QQmlParserStatus *)&t));
173
174 QQmlParserStatus *status = reinterpret_cast<QQmlParserStatus *>(reinterpret_cast<char *>((QObject *)&t) + cast);
175 QCOMPARE(status, (QQmlParserStatus*)&t);
176}
177
178void tst_qqmlmetatype::qmlPropertyValueSourceCast()
179{
180 QVERIFY(!QQmlMetaType::qmlType(QMetaType::Int).isValid());
181 QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).isValid());
182 QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).propertyValueSourceCast(), -1);
183 QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()).isValid());
184 QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()).propertyValueSourceCast(), -1);
185
186 QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ValueSourceTestType *>()).isValid());
187 int cast = QQmlMetaType::qmlType(typeId: qMetaTypeId<ValueSourceTestType *>()).propertyValueSourceCast();
188 QVERIFY(cast != -1);
189 QVERIFY(cast != 0);
190
191 ValueSourceTestType t;
192 QVERIFY(reinterpret_cast<char *>((QObject *)&t) != reinterpret_cast<char *>((QQmlPropertyValueSource *)&t));
193
194 QQmlPropertyValueSource *source = reinterpret_cast<QQmlPropertyValueSource *>(reinterpret_cast<char *>((QObject *)&t) + cast);
195 QCOMPARE(source, (QQmlPropertyValueSource*)&t);
196}
197
198void tst_qqmlmetatype::qmlPropertyValueInterceptorCast()
199{
200 QVERIFY(!QQmlMetaType::qmlType(QMetaType::Int).isValid());
201 QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).isValid());
202 QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).propertyValueInterceptorCast(), -1);
203 QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()).isValid());
204 QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()).propertyValueInterceptorCast(), -1);
205
206 QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ValueInterceptorTestType *>()).isValid());
207 int cast = QQmlMetaType::qmlType(typeId: qMetaTypeId<ValueInterceptorTestType *>()).propertyValueInterceptorCast();
208 QVERIFY(cast != -1);
209 QVERIFY(cast != 0);
210
211 ValueInterceptorTestType t;
212 QVERIFY(reinterpret_cast<char *>((QObject *)&t) != reinterpret_cast<char *>((QQmlPropertyValueInterceptor *)&t));
213
214 QQmlPropertyValueInterceptor *interceptor = reinterpret_cast<QQmlPropertyValueInterceptor *>(reinterpret_cast<char *>((QObject *)&t) + cast);
215 QCOMPARE(interceptor, (QQmlPropertyValueInterceptor*)&t);
216}
217
218void tst_qqmlmetatype::qmlType()
219{
220 QQmlType type = QQmlMetaType::qmlType(name: QString("ParserStatusTestType"), module: QString("Test"), 1, 0);
221 QVERIFY(type.isValid());
222 QVERIFY(type.module() == QLatin1String("Test"));
223 QVERIFY(type.elementName() == QLatin1String("ParserStatusTestType"));
224 QCOMPARE(type.qmlTypeName(), QLatin1String("Test/ParserStatusTestType"));
225
226 type = QQmlMetaType::qmlType(qualifiedName: "Test/ParserStatusTestType", 1, 0);
227 QVERIFY(type.isValid());
228 QVERIFY(type.module() == QLatin1String("Test"));
229 QVERIFY(type.elementName() == QLatin1String("ParserStatusTestType"));
230 QCOMPARE(type.qmlTypeName(), QLatin1String("Test/ParserStatusTestType"));
231}
232
233void tst_qqmlmetatype::invalidQmlTypeName()
234{
235 QTest::ignoreMessage(type: QtWarningMsg, message: "Invalid QML element name \"testtype\"; type names must begin with an uppercase letter");
236 QTest::ignoreMessage(type: QtWarningMsg, message: "Invalid QML element name \"Test$Type\"");
237 QTest::ignoreMessage(type: QtWarningMsg, message: "Invalid QML element name \"EndingInSlash/\"");
238
239 QCOMPARE(qmlRegisterType<TestType>("TestNamespace", 1, 0, "Test$Type"), -1); // should fail due to invalid QML type name.
240 QCOMPARE(qmlRegisterType<TestType>("Test", 1, 0, "EndingInSlash/"), -1);
241 QCOMPARE(qmlRegisterType<TestType>("Test", 1, 0, "testtype"), -1);
242}
243
244void tst_qqmlmetatype::prettyTypeName()
245{
246 TestType2 obj2;
247 QCOMPARE(QQmlMetaType::prettyTypeName(&obj2), QString("TestType2"));
248 QVERIFY(qmlRegisterType<TestType2>("Test", 1, 0, "") >= 0);
249 QCOMPARE(QQmlMetaType::prettyTypeName(&obj2), QString("TestType2"));
250
251 TestType3 obj3;
252 QCOMPARE(QQmlMetaType::prettyTypeName(&obj3), QString("TestType3"));
253 QVERIFY(qmlRegisterType<TestType3>("Test", 1, 0, "OtherName") >= 0);
254 QCOMPARE(QQmlMetaType::prettyTypeName(&obj3), QString("OtherName"));
255}
256
257void tst_qqmlmetatype::isList()
258{
259 QCOMPARE(QQmlMetaType::isList(QMetaType::UnknownType), false);
260 QCOMPARE(QQmlMetaType::isList(QMetaType::Int), false);
261
262 QQmlListProperty<TestType> list;
263
264 QCOMPARE(QQmlMetaType::isList(qMetaTypeId<QQmlListProperty<TestType> >()), true);
265}
266
267void tst_qqmlmetatype::defaultObject()
268{
269 QVERIFY(!QQmlMetaType::defaultProperty(&QObject::staticMetaObject).name());
270 QVERIFY(!QQmlMetaType::defaultProperty(&ParserStatusTestType::staticMetaObject).name());
271 QCOMPARE(QString(QQmlMetaType::defaultProperty(&TestType::staticMetaObject).name()), QString("foo"));
272
273 QObject o;
274 TestType t;
275 ParserStatusTestType p;
276
277 QVERIFY(QQmlMetaType::defaultProperty((QObject *)nullptr).name() == nullptr);
278 QVERIFY(!QQmlMetaType::defaultProperty(&o).name());
279 QVERIFY(!QQmlMetaType::defaultProperty(&p).name());
280 QCOMPARE(QString(QQmlMetaType::defaultProperty(&t).name()), QString("foo"));
281}
282
283void tst_qqmlmetatype::registrationType()
284{
285 QQmlType type = QQmlMetaType::qmlType(name: QString("TestType"), module: QString("Test"), 1, 0);
286 QVERIFY(type.isValid());
287 QVERIFY(!type.isInterface());
288 QVERIFY(!type.isSingleton());
289 QVERIFY(!type.isComposite());
290
291 type = QQmlMetaType::qmlType(name: QString("TestTypeSingleton"), module: QString("Test"), 1, 0);
292 QVERIFY(type.isValid());
293 QVERIFY(!type.isInterface());
294 QVERIFY(type.isSingleton());
295 QVERIFY(!type.isComposite());
296
297 type = QQmlMetaType::qmlType(name: QString("TestTypeComposite"), module: QString("Test"), 1, 0);
298 QVERIFY(type.isValid());
299 QVERIFY(!type.isInterface());
300 QVERIFY(!type.isSingleton());
301 QVERIFY(type.isComposite());
302}
303
304void tst_qqmlmetatype::compositeType()
305{
306 QQmlEngine engine;
307
308 //Loading the test file also loads all composite types it imports
309 QQmlComponent c(&engine, testFileUrl(fileName: "testImplicitComposite.qml"));
310 QScopedPointer<QObject> obj(c.create());
311 QVERIFY(obj);
312
313 QQmlType type = QQmlMetaType::qmlType(name: QString("ImplicitType"), module: QString(""), 1, 0);
314 QVERIFY(type.isValid());
315 QVERIFY(type.module().isEmpty());
316 QCOMPARE(type.elementName(), QLatin1String("ImplicitType"));
317 QCOMPARE(type.qmlTypeName(), QLatin1String("ImplicitType"));
318 QCOMPARE(type.sourceUrl(), testFileUrl("ImplicitType.qml"));
319}
320
321void tst_qqmlmetatype::externalEnums()
322{
323 QQmlEngine engine;
324 qmlRegisterSingletonType<ExternalEnums>(uri: "x.y.z", versionMajor: 1, versionMinor: 0, typeName: "ExternalEnums", callback: ExternalEnums::create);
325
326 QQmlComponent c(&engine, testFileUrl(fileName: "testExternalEnums.qml"));
327 QScopedPointer<QObject> obj(c.create());
328 QVERIFY(obj);
329 QVariant a = obj->property(name: "a");
330 QCOMPARE(a.userType(), QVariant::Int);
331 QCOMPARE(a.toInt(), int(QStandardPaths::DocumentsLocation));
332 QVariant b = obj->property(name: "b");
333 QCOMPARE(b.userType(), QVariant::Int);
334 QCOMPARE(b.toInt(), int(QStandardPaths::DocumentsLocation));
335
336}
337
338class Controller1 : public QObject
339{
340 Q_OBJECT
341 Q_PROPERTY(QString string MEMBER m_string)
342 Q_PROPERTY(Controller1Enum enumVal MEMBER m_enumVal)
343public:
344 enum Controller1Enum {
345 ENUM_VALUE_1 = 1,
346 ENUM_VALUE_2 = 2
347 };
348 Q_ENUMS(Controller1Enum)
349
350 Controller1(QObject *parent = nullptr) : QObject(parent), m_string("Controller #1"),
351 m_enumVal(ENUM_VALUE_1)
352 {}
353private:
354 QString m_string;
355 Controller1Enum m_enumVal;
356};
357
358class Controller2 : public QObject
359{
360 Q_OBJECT
361 Q_PROPERTY(QString string MEMBER m_string)
362 Q_PROPERTY(Controller2Enum enumVal MEMBER m_enumVal)
363public:
364 enum Controller2Enum {
365 ENUM_VALUE_1 = 111,
366 ENUM_VALUE_2 = 222
367 };
368 Q_ENUMS(Controller2Enum)
369
370 Controller2(QObject *parent = nullptr) : QObject(parent), m_string("Controller #2"),
371 m_enumVal(ENUM_VALUE_1)
372 {}
373private:
374 QString m_string;
375 Controller2Enum m_enumVal;
376};
377
378void tst_qqmlmetatype::unregisterCustomType()
379{
380 int controllerId = 0;
381 {
382 QQmlEngine engine;
383 QQmlType type = QQmlMetaType::qmlType(name: QString("Controller"), module: QString("mytypes"), 1, 0);
384 QVERIFY(!type.isValid());
385 controllerId = qmlRegisterType<Controller1>(uri: "mytypes", versionMajor: 1, versionMinor: 0, qmlName: "Controller");
386 type = QQmlMetaType::qmlType(name: QString("Controller"), module: QString("mytypes"), 1, 0);
387 QVERIFY(type.isValid());
388 QVERIFY(!type.isInterface());
389 QVERIFY(!type.isSingleton());
390 QVERIFY(!type.isComposite());
391 QQmlComponent c(&engine, testFileUrl(fileName: "testUnregisterCustomType.qml"));
392 QScopedPointer<QObject> obj(c.create());
393 QVERIFY(obj);
394 QObject *controller = obj->findChild<QObject *>(aName: "controller");
395 QVERIFY(qobject_cast<Controller1 *>(controller));
396 QVariant stringVal = controller->property(name: "string");
397 QCOMPARE(stringVal.userType(), QVariant::String);
398 QCOMPARE(stringVal.toString(), QStringLiteral("Controller #1"));
399 QVariant enumVal = controller->property(name: "enumVal");
400 QCOMPARE(enumVal.userType(), QVariant::Int);
401 QCOMPARE(enumVal.toInt(), 1);
402 }
403 QQmlMetaType::unregisterType(type: controllerId);
404 {
405 QQmlEngine engine;
406 QQmlType type = QQmlMetaType::qmlType(name: QString("Controller"), module: QString("mytypes"), 1, 0);
407 QVERIFY(!type.isValid());
408 controllerId = qmlRegisterType<Controller2>(uri: "mytypes", versionMajor: 1, versionMinor: 0, qmlName: "Controller");
409 type = QQmlMetaType::qmlType(name: QString("Controller"), module: QString("mytypes"), 1, 0);
410 QVERIFY(type.isValid());
411 QVERIFY(!type.isInterface());
412 QVERIFY(!type.isSingleton());
413 QVERIFY(!type.isComposite());
414 QQmlComponent c(&engine, testFileUrl(fileName: "testUnregisterCustomType.qml"));
415 QScopedPointer<QObject> obj(c.create());
416 QVERIFY(obj);
417 QObject *controller = obj->findChild<QObject *>(aName: "controller");
418 QVERIFY(qobject_cast<Controller2 *>(controller));
419 QVariant stringVal = controller->property(name: "string");
420 QCOMPARE(stringVal.userType(), QVariant::String);
421 QCOMPARE(stringVal.toString(), QStringLiteral("Controller #2"));
422 QVariant enumVal = controller->property(name: "enumVal");
423 QCOMPARE(enumVal.userType(), QVariant::Int);
424 QCOMPARE(enumVal.toInt(), 111);
425 }
426 QQmlMetaType::unregisterType(type: controllerId);
427 {
428 QQmlEngine engine;
429 QQmlType type = QQmlMetaType::qmlType(name: QString("Controller"), module: QString("mytypes"), 1, 0);
430 QVERIFY(!type.isValid());
431 controllerId = qmlRegisterType<Controller1>(uri: "mytypes", versionMajor: 1, versionMinor: 0, qmlName: "Controller");
432 type = QQmlMetaType::qmlType(name: QString("Controller"), module: QString("mytypes"), 1, 0);
433 QVERIFY(type.isValid());
434 QVERIFY(!type.isInterface());
435 QVERIFY(!type.isSingleton());
436 QVERIFY(!type.isComposite());
437 QQmlComponent c(&engine, testFileUrl(fileName: "testUnregisterCustomType.qml"));
438 QScopedPointer<QObject> obj(c.create());
439 QVERIFY(obj);
440 QObject *controller = obj->findChild<QObject *>(aName: "controller");
441 QVERIFY(qobject_cast<Controller1 *>(controller));
442 QVariant stringVal = controller->property(name: "string");
443 QCOMPARE(stringVal.userType(), QVariant::String);
444 QCOMPARE(stringVal.toString(), QStringLiteral("Controller #1"));
445 QVariant enumVal = controller->property(name: "enumVal");
446 QCOMPARE(enumVal.userType(), QVariant::Int);
447 QCOMPARE(enumVal.toInt(), 1);
448 }
449}
450
451class StaticProvider1 : public QObject
452{
453 Q_OBJECT
454public:
455 StaticProvider1(QObject *parent = nullptr) : QObject(parent) {}
456 Q_INVOKABLE QString singletonGetString() { return "StaticProvider #1"; }
457};
458
459static QObject* createStaticProvider1(QQmlEngine *, QJSEngine *)
460{
461 return new StaticProvider1;
462}
463
464class StaticProvider2 : public QObject
465{
466 Q_OBJECT
467public:
468 StaticProvider2(QObject *parent = nullptr) : QObject(parent) {}
469 Q_INVOKABLE QString singletonGetString() { return "StaticProvider #2"; }
470};
471
472static QObject* createStaticProvider2(QQmlEngine *, QJSEngine *)
473{
474 return new StaticProvider2;
475}
476
477void tst_qqmlmetatype::unregisterCustomSingletonType()
478{
479 int staticProviderId = 0;
480 {
481 QQmlEngine engine;
482 staticProviderId = qmlRegisterSingletonType<StaticProvider1>(uri: "mytypes", versionMajor: 1, versionMinor: 0, typeName: "StaticProvider", callback: createStaticProvider1);
483 QQmlType type = QQmlMetaType::qmlType(name: QString("StaticProvider"), module: QString("mytypes"), 1, 0);
484 QVERIFY(type.isValid());
485 QVERIFY(!type.isInterface());
486 QVERIFY(type.isSingleton());
487 QVERIFY(!type.isComposite());
488 QQmlComponent c(&engine, testFileUrl(fileName: "testUnregisterCustomSingletonType.qml"));
489 QScopedPointer<QObject> obj(c.create());
490 QVERIFY(obj.data());
491 QVariant stringVal = obj->property(name: "text");
492 QCOMPARE(stringVal.userType(), QVariant::String);
493 QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1"));
494 }
495 QQmlMetaType::unregisterType(type: staticProviderId);
496 {
497 QQmlEngine engine;
498 staticProviderId = qmlRegisterSingletonType<StaticProvider2>(uri: "mytypes", versionMajor: 1, versionMinor: 0, typeName: "StaticProvider", callback: createStaticProvider2);
499 QQmlType type = QQmlMetaType::qmlType(name: QString("StaticProvider"), module: QString("mytypes"), 1, 0);
500 QVERIFY(type.isValid());
501 QVERIFY(!type.isInterface());
502 QVERIFY(type.isSingleton());
503 QVERIFY(!type.isComposite());
504 QQmlComponent c(&engine, testFileUrl(fileName: "testUnregisterCustomSingletonType.qml"));
505 QScopedPointer<QObject> obj(c.create());
506 QVERIFY(obj.data());
507 QVariant stringVal = obj->property(name: "text");
508 QCOMPARE(stringVal.userType(), QVariant::String);
509 QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #2"));
510 }
511 QQmlMetaType::unregisterType(type: staticProviderId);
512 {
513 QQmlEngine engine;
514 staticProviderId = qmlRegisterSingletonType<StaticProvider1>(uri: "mytypes", versionMajor: 1, versionMinor: 0, typeName: "StaticProvider", callback: createStaticProvider1);
515 QQmlType type = QQmlMetaType::qmlType(name: QString("StaticProvider"), module: QString("mytypes"), 1, 0);
516 QVERIFY(type.isValid());
517 QVERIFY(!type.isInterface());
518 QVERIFY(type.isSingleton());
519 QVERIFY(!type.isComposite());
520 QQmlComponent c(&engine, testFileUrl(fileName: "testUnregisterCustomSingletonType.qml"));
521 QScopedPointer<QObject> obj(c.create());
522 QVERIFY(obj.data());
523 QVariant stringVal = obj->property(name: "text");
524 QCOMPARE(stringVal.userType(), QVariant::String);
525 QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1"));
526 }
527}
528
529void tst_qqmlmetatype::normalizeUrls()
530{
531 const QUrl url("qrc:///tstqqmlmetatype/data/CompositeType.qml");
532 QVERIFY(!QQmlMetaType::qmlType(url).isValid());
533 const auto registrationId = qmlRegisterType(url, uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "ResourceCompositeType");
534 QVERIFY(QQmlMetaType::qmlType(url, /*includeNonFileImports=*/true).isValid());
535 QUrl normalizedURL("qrc:/tstqqmlmetatype/data/CompositeType.qml");
536 QVERIFY(QQmlMetaType::qmlType(normalizedURL, /*includeNonFileImports=*/true).isValid());
537 QQmlMetaType::unregisterType(type: registrationId);
538 QVERIFY(!QQmlMetaType::qmlType(url, /*includeNonFileImports=*/true).isValid());
539}
540
541void tst_qqmlmetatype::unregisterAttachedProperties()
542{
543 qmlClearTypeRegistrations();
544
545 const QUrl dummy("qrc:///doesnotexist.qml");
546 {
547 QQmlEngine e;
548 QQmlComponent c(&e);
549 c.setData("import QtQuick 2.2\n Item { }", baseUrl: dummy);
550
551 const QQmlType attachedType = QQmlMetaType::qmlType(qualifiedName: "QtQuick/KeyNavigation", 2, 2);
552 QCOMPARE(attachedType.attachedPropertiesType(QQmlEnginePrivate::get(&e)),
553 attachedType.metaObject());
554
555 QScopedPointer<QObject> obj(c.create());
556 QVERIFY(obj);
557 }
558
559 qmlClearTypeRegistrations();
560 {
561 QQmlEngine e;
562 QQmlComponent c(&e);
563
564 // The extra import shuffles the type IDs around, so that we
565 // get a different ID for the attached properties. If the attached
566 // properties aren't properly cleared, this will crash.
567 c.setData("import QtQml.StateMachine 1.0 \n"
568 "import QtQuick 2.2 \n"
569 "Item { KeyNavigation.up: null }", baseUrl: dummy);
570
571 const QQmlType attachedType = QQmlMetaType::qmlType(qualifiedName: "QtQuick/KeyNavigation", 2, 2);
572 QCOMPARE(attachedType.attachedPropertiesType(QQmlEnginePrivate::get(&e)),
573 attachedType.metaObject());
574
575 QScopedPointer<QObject> obj(c.create());
576 QVERIFY(obj);
577 }
578}
579
580class Grouped : public QObject
581{
582 Q_OBJECT
583 Q_PROPERTY(int prop READ prop WRITE setProp NOTIFY propChanged REVISION 1)
584public:
585 int prop() const { return m_prop; }
586 void setProp(int prop)
587 {
588 if (prop != m_prop) {
589 m_prop = prop;
590 emit propChanged(prop);
591 }
592 }
593
594signals:
595 Q_REVISION(1) void propChanged(int prop);
596
597private:
598 int m_prop = 0;
599};
600
601class MyItem : public QObject
602{
603 Q_OBJECT
604 Q_PROPERTY(Grouped *grouped READ grouped CONSTANT)
605public:
606 MyItem() : m_grouped(new Grouped) {}
607 Grouped *grouped() const { return m_grouped.data(); }
608
609private:
610 QScopedPointer<Grouped> m_grouped;
611};
612
613void tst_qqmlmetatype::revisionedGroupedProperties()
614{
615 qmlClearTypeRegistrations();
616 qmlRegisterType<MyItem>(uri: "GroupedTest", versionMajor: 1, versionMinor: 0, qmlName: "MyItem");
617 qmlRegisterType<MyItem, 1>(uri: "GroupedTest", versionMajor: 1, versionMinor: 1, qmlName: "MyItem");
618 qmlRegisterUncreatableType<Grouped>(uri: "GroupedTest", versionMajor: 1, versionMinor: 0, qmlName: "Grouped", reason: "Grouped");
619 qmlRegisterUncreatableType<Grouped, 1>(uri: "GroupedTest", versionMajor: 1, versionMinor: 1, qmlName: "Grouped", reason: "Grouped");
620
621 {
622 QQmlEngine engine;
623 QQmlComponent valid(&engine, testFileUrl(fileName: "revisionedGroupedPropertiesValid.qml"));
624 QVERIFY(valid.isReady());
625 QScopedPointer<QObject> obj(valid.create());
626 QVERIFY(!obj.isNull());
627 }
628
629 {
630 QQmlEngine engine;
631 QQmlComponent invalid(&engine, testFileUrl(fileName: "revisionedGroupedPropertiesInvalid.qml"));
632 QVERIFY(invalid.isError());
633 }
634}
635
636void tst_qqmlmetatype::enumsInRecursiveImport_data()
637{
638 QTest::addColumn<QString>(name: "importPath");
639 QTest::addColumn<QUrl>(name: "componentUrl");
640
641 QTest::addRow(format: "data directory") << dataDirectory()
642 << testFileUrl(fileName: "enumsInRecursiveImport.qml");
643
644 // The qrc case behaves differently because we failed to detect the recursion in type loading
645 // due to varying numbers of slashes after the "qrc:" in the URLs.
646 QTest::addRow(format: "resources") << QStringLiteral("qrc:/data")
647 << QUrl("qrc:/data/enumsInRecursiveImport.qml");
648}
649
650void tst_qqmlmetatype::enumsInRecursiveImport()
651{
652 QFETCH(QString, importPath);
653 QFETCH(QUrl, componentUrl);
654
655 qmlClearTypeRegistrations();
656 QQmlEngine engine;
657 engine.addImportPath(dir: importPath);
658 QQmlComponent c(&engine, componentUrl);
659 QVERIFY(c.isReady());
660 QScopedPointer<QObject> obj(c.create());
661 QVERIFY(!obj.isNull());
662 QTRY_COMPARE(obj->property("color").toString(), QString("green"));
663}
664
665QTEST_MAIN(tst_qqmlmetatype)
666
667#include "tst_qqmlmetatype.moc"
668

source code of qtdeclarative/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp