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 "interfaces.h"
30#include <qtest.h>
31#include <QtQml/qqmlengine.h>
32#include <QtQml/qqmlcomponent.h>
33#include <QtQml/qqmlcontext.h>
34#include <QtQml/qqmlproperty.h>
35#include <QtQml/private/qqmlproperty_p.h>
36#include <private/qqmlbinding_p.h>
37#include <private/qqmlboundsignal_p.h>
38#include <QtCore/qfileinfo.h>
39#include <QtCore/qdir.h>
40#if QT_CONFIG(regularexpression)
41#include <QtCore/qregularexpression.h>
42#endif
43#include <QtCore/private/qobject_p.h>
44#include "../../shared/util.h"
45#include "qobject.h"
46#include <QtQml/QQmlPropertyMap>
47
48#include <QDebug>
49class MyQmlObject : public QObject
50{
51 Q_OBJECT
52 Q_PROPERTY(QPoint pointProperty MEMBER m_point)
53public:
54 MyQmlObject(QObject *parent = nullptr) : QObject(parent) {}
55
56private:
57 QPoint m_point;
58};
59
60QML_DECLARE_TYPE(MyQmlObject);
61
62class MyQObject : public QObject
63{
64 Q_OBJECT
65public:
66 MyQObject(QObject *parent = nullptr) : QObject(parent), m_i(0) {}
67
68 int inc() { return ++m_i; }
69
70private:
71 int m_i;
72};
73
74
75class MyAttached : public QObject
76{
77 Q_OBJECT
78 Q_PROPERTY(int foo READ foo WRITE setFoo)
79public:
80 MyAttached(QObject *parent) : QObject(parent), m_foo(13) {}
81
82 int foo() const { return m_foo; }
83 void setFoo(int f) { m_foo = f; }
84
85private:
86 int m_foo;
87};
88
89class MyContainer : public QObject
90{
91 Q_OBJECT
92 Q_PROPERTY(QQmlListProperty<MyQmlObject> children READ children)
93public:
94 MyContainer() {}
95
96 QQmlListProperty<MyQmlObject> children() { return QQmlListProperty<MyQmlObject>(this, m_children); }
97
98 static MyAttached *qmlAttachedProperties(QObject *o) {
99 return new MyAttached(o);
100 }
101
102private:
103 QList<MyQmlObject*> m_children;
104};
105
106QML_DECLARE_TYPE(MyContainer);
107QML_DECLARE_TYPEINFO(MyContainer, QML_HAS_ATTACHED_PROPERTIES)
108
109class tst_qqmlproperty : public QQmlDataTest
110{
111 Q_OBJECT
112public:
113 tst_qqmlproperty() {}
114
115private slots:
116 void initTestCase();
117
118 // Constructors
119 void qmlmetaproperty();
120 void qmlmetaproperty_object();
121 void qmlmetaproperty_object_string();
122 void qmlmetaproperty_object_context();
123 void qmlmetaproperty_object_string_context();
124
125 // Methods
126 void name();
127 void read();
128 void write();
129 void reset();
130
131 // Functionality
132 void writeObjectToList();
133 void writeListToList();
134
135 //writeToReadOnly();
136
137 void urlHandling_data();
138 void urlHandling();
139
140 void variantMapHandling_data();
141 void variantMapHandling();
142
143 // Bugs
144 void crashOnValueProperty();
145 void aliasPropertyBindings_data();
146 void aliasPropertyBindings();
147 void noContext();
148 void assignEmptyVariantMap();
149 void warnOnInvalidBinding();
150 void registeredCompositeTypeProperty();
151 void deeplyNestedObject();
152 void readOnlyDynamicProperties();
153 void aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSystem();
154 void nullPropertyBinding();
155 void interfaceBinding();
156
157 void floatToStringPrecision_data();
158 void floatToStringPrecision();
159
160 void copy();
161
162 void bindingToAlias();
163
164 void nestedQQmlPropertyMap();
165
166 void underscorePropertyChangeHandler();
167
168 void signalExpressionWithoutObject();
169private:
170 QQmlEngine engine;
171};
172
173void tst_qqmlproperty::qmlmetaproperty()
174{
175 QQmlProperty prop;
176
177 QObject *obj = new QObject;
178
179 QQmlAbstractBinding::Ptr binding(QQmlBinding::create(nullptr, QLatin1String("null"), nullptr, QQmlContextData::get(context: engine.rootContext())));
180 QVERIFY(binding);
181 QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(obj, QObjectPrivate::get(o: obj)->signalIndex(signalName: "destroyed()"), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
182 QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
183 QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
184
185 QCOMPARE(prop.name(), QString());
186 QCOMPARE(prop.read(), QVariant());
187 QCOMPARE(prop.write(QVariant()), false);
188 QCOMPARE(prop.hasNotifySignal(), false);
189 QCOMPARE(prop.needsNotifySignal(), false);
190 QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false);
191 QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
192 QCOMPARE(prop.connectNotifySignal(obj, 0), false);
193 QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
194 QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
195 QCOMPARE(prop.connectNotifySignal(obj, -1), false);
196 QVERIFY(!prop.method().isValid());
197 QCOMPARE(prop.type(), QQmlProperty::Invalid);
198 QCOMPARE(prop.isProperty(), false);
199 QCOMPARE(prop.isWritable(), false);
200 QCOMPARE(prop.isDesignable(), false);
201 QCOMPARE(prop.isResettable(), false);
202 QCOMPARE(prop.isSignalProperty(), false);
203 QCOMPARE(prop.isValid(), false);
204 QCOMPARE(prop.object(), (QObject *)nullptr);
205 QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
206 QCOMPARE(prop.propertyType(), 0);
207 QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
208 QVERIFY(!prop.property().name());
209 QVERIFY(!QQmlPropertyPrivate::binding(prop));
210 QQmlPropertyPrivate::setBinding(that: prop, binding.data());
211 QVERIFY(binding->ref == 1);
212 QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
213 QQmlPropertyPrivate::takeSignalExpression(that: prop, sigExpr);
214 QVERIFY(sigExprWatcher.wasDeleted());
215 QCOMPARE(prop.index(), -1);
216 QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
217 QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
218
219 delete obj;
220}
221
222// 1 = equal, 0 = unknown, -1 = not equal.
223static int compareVariantAndListReference(const QVariant &v, QQmlListReference &r)
224{
225 if (QLatin1String(v.typeName()) != QLatin1String("QQmlListReference"))
226 return -1;
227
228 QQmlListReference lhs = v.value<QQmlListReference>();
229 if (lhs.isValid() != r.isValid())
230 return -1;
231
232 if (lhs.canCount() != r.canCount())
233 return -1;
234
235 if (!lhs.canCount()) {
236 if (lhs.canAt() != r.canAt())
237 return -1; // not equal.
238 return 0; // not sure if they're equal or not, and no way to tell.
239 }
240
241 // if we get here, we must be able to count.
242 if (lhs.count() != r.count())
243 return -1;
244
245 if (lhs.canAt() != r.canAt())
246 return -1;
247
248 if (!lhs.canAt())
249 return 0; // can count, but can't check element equality.
250
251 for (int i = 0; i < lhs.count(); ++i) {
252 if (lhs.at(i) != r.at(i)) {
253 return -1; // different elements :. not equal.
254 }
255 }
256
257 return 1; // equal.
258}
259
260void tst_qqmlproperty::registeredCompositeTypeProperty()
261{
262 // Composite type properties
263 {
264 QQmlEngine engine;
265 QQmlComponent component(&engine, testFileUrl(fileName: "registeredCompositeTypeProperty.qml"));
266 QObject *obj = component.create();
267 QVERIFY(obj);
268
269 // create property accessors and check types.
270 QQmlProperty p1(obj, "first");
271 QQmlProperty p2(obj, "second");
272 QQmlProperty p3(obj, "third");
273 QQmlProperty p1e(obj, "first", &engine);
274 QQmlProperty p2e(obj, "second", &engine);
275 QQmlProperty p3e(obj, "third", &engine);
276 QCOMPARE(p1.propertyType(), p2.propertyType());
277 QVERIFY(p1.propertyType() != p3.propertyType());
278
279 // check that the values are retrievable from CPP
280 QVariant first = obj->property(name: "first");
281 QVariant second = obj->property(name: "second");
282 QVariant third = obj->property(name: "third");
283 QVERIFY(first.isValid());
284 QVERIFY(second.isValid());
285 QVERIFY(third.isValid());
286 // ensure that conversion from qobject-derived-ptr to qobject-ptr works.
287 QVERIFY(first.value<QObject*>());
288 QVERIFY(second.value<QObject*>());
289 QVERIFY(third.value<QObject*>());
290
291 // check that the values retrieved via QQmlProperty match those retrieved via QMetaProperty::read().
292 QCOMPARE(p1.read().value<QObject*>(), first.value<QObject*>());
293 QCOMPARE(p2.read().value<QObject*>(), second.value<QObject*>());
294 QCOMPARE(p3.read().value<QObject*>(), third.value<QObject*>());
295 QCOMPARE(p1e.read().value<QObject*>(), first.value<QObject*>());
296 QCOMPARE(p2e.read().value<QObject*>(), second.value<QObject*>());
297 QCOMPARE(p3e.read().value<QObject*>(), third.value<QObject*>());
298
299 delete obj;
300 }
301
302 // List-of-composite-type type properties
303 {
304 QQmlEngine engine;
305 QQmlComponent component(&engine, testFileUrl(fileName: "registeredCompositeTypeProperty.qml"));
306 QObject *obj = component.create();
307 QVERIFY(obj);
308
309 // create list property accessors and check types
310 QQmlProperty lp1e(obj, "fclist", &engine);
311 QQmlProperty lp2e(obj, "sclistOne", &engine);
312 QQmlProperty lp3e(obj, "sclistTwo", &engine);
313 QVERIFY(lp1e.propertyType() != lp2e.propertyType());
314 QCOMPARE(lp2e.propertyType(), lp3e.propertyType());
315
316 // check that the list values are retrievable from CPP
317 QVariant firstList = obj->property(name: "fclist");
318 QVariant secondList = obj->property(name: "sclistOne");
319 QVariant thirdList = obj->property(name: "sclistTwo");
320 QVERIFY(firstList.isValid());
321 QVERIFY(secondList.isValid());
322 QVERIFY(thirdList.isValid());
323
324 // check that the value returned by QQmlProperty::read() is equivalent to the list reference.
325 QQmlListReference r1(obj, "fclist", &engine);
326 QQmlListReference r2(obj, "sclistOne", &engine);
327 QQmlListReference r3(obj, "sclistTwo", &engine);
328 QCOMPARE(compareVariantAndListReference(lp1e.read(), r1), 1);
329 QCOMPARE(compareVariantAndListReference(lp2e.read(), r2), 1);
330 QCOMPARE(compareVariantAndListReference(lp3e.read(), r3), 1);
331
332 delete obj;
333 }
334}
335
336class PropertyObject : public QObject
337{
338 Q_OBJECT
339 Q_PROPERTY(int defaultProperty READ defaultProperty)
340 Q_PROPERTY(QRect rectProperty READ rectProperty)
341 Q_PROPERTY(QRect wrectProperty READ wrectProperty WRITE setWRectProperty)
342 Q_PROPERTY(QUrl url READ url WRITE setUrl)
343 Q_PROPERTY(QVariantMap variantMap READ variantMap WRITE setVariantMap)
344 Q_PROPERTY(int resettableProperty READ resettableProperty WRITE setResettableProperty RESET resetProperty)
345 Q_PROPERTY(int propertyWithNotify READ propertyWithNotify WRITE setPropertyWithNotify NOTIFY oddlyNamedNotifySignal)
346 Q_PROPERTY(MyQmlObject *qmlObject READ qmlObject)
347 Q_PROPERTY(MyQObject *qObject READ qObject WRITE setQObject NOTIFY qObjectChanged)
348 Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty)
349 Q_PROPERTY(QChar qcharProperty READ qcharProperty WRITE setQcharProperty)
350 Q_PROPERTY(QChar constQChar READ constQChar STORED false CONSTANT FINAL)
351
352 Q_CLASSINFO("DefaultProperty", "defaultProperty")
353public:
354 PropertyObject() : m_resetProperty(9), m_qObject(nullptr), m_stringProperty("foo") {}
355
356 int defaultProperty() { return 10; }
357 QRect rectProperty() { return QRect(10, 10, 1, 209); }
358
359 QRect wrectProperty() { return m_rect; }
360 void setWRectProperty(const QRect &r) { m_rect = r; }
361
362 QUrl url() { return m_url; }
363 void setUrl(const QUrl &u) { m_url = u; }
364
365 QVariantMap variantMap() const { return m_variantMap; }
366 void setVariantMap(const QVariantMap &variantMap) { m_variantMap = variantMap; }
367
368 int resettableProperty() const { return m_resetProperty; }
369 void setResettableProperty(int r) { m_resetProperty = r; }
370 void resetProperty() { m_resetProperty = 9; }
371
372 int propertyWithNotify() const { return m_propertyWithNotify; }
373 void setPropertyWithNotify(int i) { m_propertyWithNotify = i; emit oddlyNamedNotifySignal(); }
374
375 MyQmlObject *qmlObject() { return &m_qmlObject; }
376
377 MyQObject *qObject() { return m_qObject; }
378 void setQObject(MyQObject *object)
379 {
380 if (m_qObject != object) {
381 m_qObject = object;
382 emit qObjectChanged();
383 }
384 }
385
386 QString stringProperty() const { return m_stringProperty;}
387 QChar qcharProperty() const { return m_qcharProperty; }
388
389 QChar constQChar() const { return 0x25cf; /* Unicode: black circle */ }
390
391 void setStringProperty(QString arg) { m_stringProperty = arg; }
392 void setQcharProperty(QChar arg) { m_qcharProperty = arg; }
393
394signals:
395 void clicked();
396 void oddlyNamedNotifySignal();
397 void qObjectChanged();
398
399private:
400 int m_resetProperty;
401 QRect m_rect;
402 QUrl m_url;
403 QVariantMap m_variantMap;
404 int m_propertyWithNotify;
405 MyQmlObject m_qmlObject;
406 MyQObject *m_qObject;
407 QString m_stringProperty;
408 QChar m_qcharProperty;
409};
410
411QML_DECLARE_TYPE(PropertyObject);
412
413void tst_qqmlproperty::qmlmetaproperty_object()
414{
415 QObject object; // Has no default property
416 PropertyObject dobject; // Has default property
417
418 {
419 QQmlProperty prop(&object);
420
421 QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(context: engine.rootContext())));
422 QVERIFY(binding);
423 QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(o: &object)->signalIndex(signalName: "destroyed()"), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
424 QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
425 QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
426
427 QObject *obj = new QObject;
428
429 QCOMPARE(prop.name(), QString());
430 QCOMPARE(prop.read(), QVariant());
431 QCOMPARE(prop.write(QVariant()), false);
432 QCOMPARE(prop.hasNotifySignal(), false);
433 QCOMPARE(prop.needsNotifySignal(), false);
434 QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false);
435 QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
436 QCOMPARE(prop.connectNotifySignal(obj, 0), false);
437 QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
438 QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
439 QCOMPARE(prop.connectNotifySignal(obj, -1), false);
440 QVERIFY(!prop.method().isValid());
441 QCOMPARE(prop.type(), QQmlProperty::Invalid);
442 QCOMPARE(prop.isProperty(), false);
443 QCOMPARE(prop.isWritable(), false);
444 QCOMPARE(prop.isDesignable(), false);
445 QCOMPARE(prop.isResettable(), false);
446 QCOMPARE(prop.isSignalProperty(), false);
447 QCOMPARE(prop.isValid(), false);
448 QCOMPARE(prop.object(), (QObject *)nullptr);
449 QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
450 QCOMPARE(prop.propertyType(), 0);
451 QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
452 QVERIFY(!prop.property().name());
453 QVERIFY(!QQmlPropertyPrivate::binding(prop));
454 QQmlPropertyPrivate::setBinding(that: prop, binding.data());
455 QVERIFY(binding->ref == 1);
456 QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
457 QQmlPropertyPrivate::takeSignalExpression(that: prop, sigExpr);
458 QVERIFY(sigExprWatcher.wasDeleted());
459 QCOMPARE(prop.index(), -1);
460 QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
461 QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
462
463 delete obj;
464 }
465
466 {
467 QQmlProperty prop(&dobject);
468
469 QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(context: engine.rootContext())));
470 static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
471 QVERIFY(binding);
472 QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(o: &dobject)->signalIndex(signalName: "clicked()"), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
473 QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
474 QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
475
476 QObject *obj = new QObject;
477
478 QCOMPARE(prop.name(), QString("defaultProperty"));
479 QCOMPARE(prop.read(), QVariant(10));
480 QCOMPARE(prop.write(QVariant()), false);
481 QCOMPARE(prop.hasNotifySignal(), false);
482 QCOMPARE(prop.needsNotifySignal(), true);
483 QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false);
484 QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
485 QCOMPARE(prop.connectNotifySignal(obj, 0), false);
486 QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
487 QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
488 QCOMPARE(prop.connectNotifySignal(obj, -1), false);
489 QVERIFY(!prop.method().isValid());
490 QCOMPARE(prop.type(), QQmlProperty::Property);
491 QCOMPARE(prop.isProperty(), true);
492 QCOMPARE(prop.isWritable(), false);
493 QCOMPARE(prop.isDesignable(), true);
494 QCOMPARE(prop.isResettable(), false);
495 QCOMPARE(prop.isSignalProperty(), false);
496 QCOMPARE(prop.isValid(), true);
497 QCOMPARE(prop.object(), qobject_cast<QObject*>(&dobject));
498 QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::Normal);
499 QCOMPARE(prop.propertyType(), (int)QVariant::Int);
500 QCOMPARE(prop.propertyTypeName(), "int");
501 QCOMPARE(QString(prop.property().name()), QString("defaultProperty"));
502 QVERIFY(!QQmlPropertyPrivate::binding(prop));
503 QTest::ignoreMessage(type: QtWarningMsg, message: "<Unknown File>: Unable to assign null to int");
504 QQmlPropertyPrivate::setBinding(that: prop, binding.data());
505 QVERIFY(binding);
506 QCOMPARE(QQmlPropertyPrivate::binding(prop), binding.data());
507 QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
508 QQmlPropertyPrivate::takeSignalExpression(that: prop, sigExpr);
509 QVERIFY(sigExprWatcher.wasDeleted());
510 QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty"));
511 QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
512 QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
513
514 delete obj;
515 }
516}
517
518void tst_qqmlproperty::qmlmetaproperty_object_string()
519{
520 QObject object;
521 PropertyObject dobject;
522
523 {
524 QQmlProperty prop(&object, QString("defaultProperty"));
525
526 QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(context: engine.rootContext())));
527 QVERIFY(binding);
528 QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(o: &object)->signalIndex(signalName: "destroyed()"), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
529 QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
530 QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
531
532 QObject *obj = new QObject;
533
534 QCOMPARE(prop.name(), QString());
535 QCOMPARE(prop.read(), QVariant());
536 QCOMPARE(prop.write(QVariant()), false);
537 QCOMPARE(prop.hasNotifySignal(), false);
538 QCOMPARE(prop.needsNotifySignal(), false);
539 QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false);
540 QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
541 QCOMPARE(prop.connectNotifySignal(obj, 0), false);
542 QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
543 QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
544 QCOMPARE(prop.connectNotifySignal(obj, -1), false);
545 QVERIFY(!prop.method().isValid());
546 QCOMPARE(prop.type(), QQmlProperty::Invalid);
547 QCOMPARE(prop.isProperty(), false);
548 QCOMPARE(prop.isWritable(), false);
549 QCOMPARE(prop.isDesignable(), false);
550 QCOMPARE(prop.isResettable(), false);
551 QCOMPARE(prop.isSignalProperty(), false);
552 QCOMPARE(prop.isValid(), false);
553 QCOMPARE(prop.object(), (QObject *)nullptr);
554 QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
555 QCOMPARE(prop.propertyType(), 0);
556 QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
557 QVERIFY(!prop.property().name());
558 QVERIFY(!QQmlPropertyPrivate::binding(prop));
559 QQmlPropertyPrivate::setBinding(that: prop, binding.data());
560 QVERIFY(binding->ref == 1);
561 QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
562 QQmlPropertyPrivate::takeSignalExpression(that: prop, sigExpr);
563 QVERIFY(sigExprWatcher.wasDeleted());
564 QCOMPARE(prop.index(), -1);
565 QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
566 QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
567
568 delete obj;
569 }
570
571 {
572 QQmlProperty prop(&dobject, QString("defaultProperty"));
573
574 QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(context: engine.rootContext())));
575 static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
576 QVERIFY(binding);
577 QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(o: &dobject)->signalIndex(signalName: "clicked()"), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
578 QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
579 QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
580
581 QObject *obj = new QObject;
582
583 QCOMPARE(prop.name(), QString("defaultProperty"));
584 QCOMPARE(prop.read(), QVariant(10));
585 QCOMPARE(prop.write(QVariant()), false);
586 QCOMPARE(prop.hasNotifySignal(), false);
587 QCOMPARE(prop.needsNotifySignal(), true);
588 QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false);
589 QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
590 QCOMPARE(prop.connectNotifySignal(obj, 0), false);
591 QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
592 QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
593 QCOMPARE(prop.connectNotifySignal(obj, -1), false);
594 QVERIFY(!prop.method().isValid());
595 QCOMPARE(prop.type(), QQmlProperty::Property);
596 QCOMPARE(prop.isProperty(), true);
597 QCOMPARE(prop.isWritable(), false);
598 QCOMPARE(prop.isDesignable(), true);
599 QCOMPARE(prop.isResettable(), false);
600 QCOMPARE(prop.isSignalProperty(), false);
601 QCOMPARE(prop.isValid(), true);
602 QCOMPARE(prop.object(), qobject_cast<QObject*>(&dobject));
603 QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::Normal);
604 QCOMPARE(prop.propertyType(), (int)QVariant::Int);
605 QCOMPARE(prop.propertyTypeName(), "int");
606 QCOMPARE(QString(prop.property().name()), QString("defaultProperty"));
607 QVERIFY(!QQmlPropertyPrivate::binding(prop));
608 QTest::ignoreMessage(type: QtWarningMsg, message: "<Unknown File>: Unable to assign null to int");
609 QQmlPropertyPrivate::setBinding(that: prop, binding.data());
610 QVERIFY(binding);
611 QCOMPARE(QQmlPropertyPrivate::binding(prop), binding.data());
612 QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
613 QQmlPropertyPrivate::takeSignalExpression(that: prop, sigExpr);
614 QVERIFY(sigExprWatcher.wasDeleted());
615 QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty"));
616 QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
617 QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
618
619 delete obj;
620 }
621
622 {
623 QQmlProperty prop(&dobject, QString("onClicked"));
624
625 QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(context: engine.rootContext())));
626 static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
627 QVERIFY(binding);
628 QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(p: prop)->signalIndex(), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
629 QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
630 QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
631
632 QObject *obj = new QObject;
633
634 QCOMPARE(prop.name(), QString("onClicked"));
635 QCOMPARE(prop.read(), QVariant());
636 QCOMPARE(prop.write(QVariant("Hello")), false);
637 QCOMPARE(prop.hasNotifySignal(), false);
638 QCOMPARE(prop.needsNotifySignal(), false);
639 QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false);
640 QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
641 QCOMPARE(prop.connectNotifySignal(obj, 0), false);
642 QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
643 QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
644 QCOMPARE(prop.connectNotifySignal(obj, -1), false);
645 QCOMPARE(QString(prop.method().methodSignature()), QString("clicked()"));
646 QCOMPARE(prop.type(), QQmlProperty::SignalProperty);
647 QCOMPARE(prop.isProperty(), false);
648 QCOMPARE(prop.isWritable(), false);
649 QCOMPARE(prop.isDesignable(), false);
650 QCOMPARE(prop.isResettable(), false);
651 QCOMPARE(prop.isSignalProperty(), true);
652 QCOMPARE(prop.isValid(), true);
653 QCOMPARE(prop.object(), qobject_cast<QObject*>(&dobject));
654 QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
655 QCOMPARE(prop.propertyType(), 0);
656 QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
657 QCOMPARE(prop.property().name(), (const char *)nullptr);
658 QVERIFY(!QQmlPropertyPrivate::binding(prop));
659 QQmlPropertyPrivate::setBinding(that: prop, binding.data());
660 QVERIFY(binding->ref == 1);
661 QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
662 QQmlPropertyPrivate::takeSignalExpression(that: prop, sigExpr);
663 QVERIFY(!sigExprWatcher.wasDeleted());
664 QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr);
665 QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("clicked()"));
666 QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
667 QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
668
669 delete obj;
670 }
671
672 {
673 QQmlProperty prop(&dobject, QString("onPropertyWithNotifyChanged"));
674
675 QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(context: engine.rootContext())));
676 static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
677 QVERIFY(binding);
678 QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(p: prop)->signalIndex(), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
679 QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
680 QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
681
682 QObject *obj = new QObject;
683
684 QCOMPARE(prop.name(), QString("onOddlyNamedNotifySignal"));
685 QCOMPARE(prop.read(), QVariant());
686 QCOMPARE(prop.write(QVariant("Hello")), false);
687 QCOMPARE(prop.hasNotifySignal(), false);
688 QCOMPARE(prop.needsNotifySignal(), false);
689 QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false);
690 QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
691 QCOMPARE(prop.connectNotifySignal(obj, 0), false);
692 QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
693 QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
694 QCOMPARE(prop.connectNotifySignal(obj, -1), false);
695 QCOMPARE(QString(prop.method().methodSignature()), QString("oddlyNamedNotifySignal()"));
696 QCOMPARE(prop.type(), QQmlProperty::SignalProperty);
697 QCOMPARE(prop.isProperty(), false);
698 QCOMPARE(prop.isWritable(), false);
699 QCOMPARE(prop.isDesignable(), false);
700 QCOMPARE(prop.isResettable(), false);
701 QCOMPARE(prop.isSignalProperty(), true);
702 QCOMPARE(prop.isValid(), true);
703 QCOMPARE(prop.object(), qobject_cast<QObject*>(&dobject));
704 QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
705 QCOMPARE(prop.propertyType(), 0);
706 QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
707 QCOMPARE(prop.property().name(), (const char *)nullptr);
708 QVERIFY(!QQmlPropertyPrivate::binding(prop));
709 QQmlPropertyPrivate::setBinding(that: prop, binding.data());
710 QVERIFY(binding->ref == 1);
711 QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
712 QQmlPropertyPrivate::takeSignalExpression(that: prop, sigExpr);
713 QVERIFY(!sigExprWatcher.wasDeleted());
714 QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr);
715 QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("oddlyNamedNotifySignal()"));
716 QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
717 QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
718
719 delete obj;
720 }
721}
722
723void tst_qqmlproperty::qmlmetaproperty_object_context()
724{
725 QObject object; // Has no default property
726 PropertyObject dobject; // Has default property
727
728 {
729 QQmlProperty prop(&object, engine.rootContext());
730
731 QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(context: engine.rootContext())));
732 QVERIFY(binding);
733 QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(o: &object)->signalIndex(signalName: "destroyed()"), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
734 QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
735 QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
736
737 QObject *obj = new QObject;
738
739 QCOMPARE(prop.name(), QString());
740 QCOMPARE(prop.read(), QVariant());
741 QCOMPARE(prop.write(QVariant()), false);
742 QCOMPARE(prop.hasNotifySignal(), false);
743 QCOMPARE(prop.needsNotifySignal(), false);
744 QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false);
745 QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
746 QCOMPARE(prop.connectNotifySignal(obj, 0), false);
747 QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
748 QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
749 QCOMPARE(prop.connectNotifySignal(obj, -1), false);
750 QVERIFY(!prop.method().isValid());
751 QCOMPARE(prop.type(), QQmlProperty::Invalid);
752 QCOMPARE(prop.isProperty(), false);
753 QCOMPARE(prop.isWritable(), false);
754 QCOMPARE(prop.isDesignable(), false);
755 QCOMPARE(prop.isResettable(), false);
756 QCOMPARE(prop.isSignalProperty(), false);
757 QCOMPARE(prop.isValid(), false);
758 QCOMPARE(prop.object(), (QObject *)nullptr);
759 QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
760 QCOMPARE(prop.propertyType(), 0);
761 QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
762 QVERIFY(!prop.property().name());
763 QVERIFY(!QQmlPropertyPrivate::binding(prop));
764 QQmlPropertyPrivate::setBinding(that: prop, binding.data());
765 QVERIFY(binding->ref == 1);
766 QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
767 QQmlPropertyPrivate::takeSignalExpression(that: prop, sigExpr);
768 QVERIFY(sigExprWatcher.wasDeleted());
769 QCOMPARE(prop.index(), -1);
770 QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
771 QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
772
773 delete obj;
774 }
775
776 {
777 QQmlProperty prop(&dobject, engine.rootContext());
778
779 QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(context: engine.rootContext())));
780 static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
781 QVERIFY(binding);
782 QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(o: &dobject)->signalIndex(signalName: "clicked()"), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
783 QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
784 QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
785
786 QObject *obj = new QObject;
787
788 QCOMPARE(prop.name(), QString("defaultProperty"));
789 QCOMPARE(prop.read(), QVariant(10));
790 QCOMPARE(prop.write(QVariant()), false);
791 QCOMPARE(prop.hasNotifySignal(), false);
792 QCOMPARE(prop.needsNotifySignal(), true);
793 QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false);
794 QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
795 QCOMPARE(prop.connectNotifySignal(obj, 0), false);
796 QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
797 QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
798 QCOMPARE(prop.connectNotifySignal(obj, -1), false);
799 QVERIFY(!prop.method().isValid());
800 QCOMPARE(prop.type(), QQmlProperty::Property);
801 QCOMPARE(prop.isProperty(), true);
802 QCOMPARE(prop.isWritable(), false);
803 QCOMPARE(prop.isDesignable(), true);
804 QCOMPARE(prop.isResettable(), false);
805 QCOMPARE(prop.isSignalProperty(), false);
806 QCOMPARE(prop.isValid(), true);
807 QCOMPARE(prop.object(), qobject_cast<QObject*>(&dobject));
808 QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::Normal);
809 QCOMPARE(prop.propertyType(), (int)QVariant::Int);
810 QCOMPARE(prop.propertyTypeName(), "int");
811 QCOMPARE(QString(prop.property().name()), QString("defaultProperty"));
812 QVERIFY(!QQmlPropertyPrivate::binding(prop));
813 QTest::ignoreMessage(type: QtWarningMsg, message: "<Unknown File>: Unable to assign null to int");
814 QQmlPropertyPrivate::setBinding(that: prop, binding.data());
815 QVERIFY(binding);
816 QCOMPARE(QQmlPropertyPrivate::binding(prop), binding.data());
817 QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
818 QQmlPropertyPrivate::takeSignalExpression(that: prop, sigExpr);
819 QVERIFY(sigExprWatcher.wasDeleted());
820 QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty"));
821 QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
822 QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
823
824 delete obj;
825 }
826}
827
828void tst_qqmlproperty::qmlmetaproperty_object_string_context()
829{
830 QObject object;
831 PropertyObject dobject;
832
833 {
834 QQmlProperty prop(&object, QString("defaultProperty"), engine.rootContext());
835
836 QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(context: engine.rootContext())));
837 QVERIFY(binding);
838 QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(o: &object)->signalIndex(signalName: "destroyed()"), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
839 QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
840 QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
841
842 QObject *obj = new QObject;
843
844 QCOMPARE(prop.name(), QString());
845 QCOMPARE(prop.read(), QVariant());
846 QCOMPARE(prop.write(QVariant()), false);
847 QCOMPARE(prop.hasNotifySignal(), false);
848 QCOMPARE(prop.needsNotifySignal(), false);
849 QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false);
850 QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
851 QCOMPARE(prop.connectNotifySignal(obj, 0), false);
852 QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
853 QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
854 QCOMPARE(prop.connectNotifySignal(obj, -1), false);
855 QVERIFY(!prop.method().isValid());
856 QCOMPARE(prop.type(), QQmlProperty::Invalid);
857 QCOMPARE(prop.isProperty(), false);
858 QCOMPARE(prop.isWritable(), false);
859 QCOMPARE(prop.isDesignable(), false);
860 QCOMPARE(prop.isResettable(), false);
861 QCOMPARE(prop.isSignalProperty(), false);
862 QCOMPARE(prop.isValid(), false);
863 QCOMPARE(prop.object(), (QObject *)nullptr);
864 QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
865 QCOMPARE(prop.propertyType(), 0);
866 QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
867 QVERIFY(!prop.property().name());
868 QVERIFY(!QQmlPropertyPrivate::binding(prop));
869 QQmlPropertyPrivate::setBinding(that: prop, binding.data());
870 QVERIFY(binding->ref == 1);
871 QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
872 QQmlPropertyPrivate::takeSignalExpression(that: prop, sigExpr);
873 QVERIFY(sigExprWatcher.wasDeleted());
874 QCOMPARE(prop.index(), -1);
875 QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
876 QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
877
878 delete obj;
879 }
880
881 {
882 QQmlProperty prop(&dobject, QString("defaultProperty"), engine.rootContext());
883
884 QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(context: engine.rootContext())));
885 static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
886 QVERIFY(binding);
887 QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(o: &dobject)->signalIndex(signalName: "clicked()"), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
888 QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
889 QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
890
891 QObject *obj = new QObject;
892
893 QCOMPARE(prop.name(), QString("defaultProperty"));
894 QCOMPARE(prop.read(), QVariant(10));
895 QCOMPARE(prop.write(QVariant()), false);
896 QCOMPARE(prop.hasNotifySignal(), false);
897 QCOMPARE(prop.needsNotifySignal(), true);
898 QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false);
899 QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
900 QCOMPARE(prop.connectNotifySignal(obj, 0), false);
901 QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
902 QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
903 QCOMPARE(prop.connectNotifySignal(obj, -1), false);
904 QVERIFY(!prop.method().isValid());
905 QCOMPARE(prop.type(), QQmlProperty::Property);
906 QCOMPARE(prop.isProperty(), true);
907 QCOMPARE(prop.isWritable(), false);
908 QCOMPARE(prop.isDesignable(), true);
909 QCOMPARE(prop.isResettable(), false);
910 QCOMPARE(prop.isSignalProperty(), false);
911 QCOMPARE(prop.isValid(), true);
912 QCOMPARE(prop.object(), qobject_cast<QObject*>(&dobject));
913 QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::Normal);
914 QCOMPARE(prop.propertyType(), (int)QVariant::Int);
915 QCOMPARE(prop.propertyTypeName(), "int");
916 QCOMPARE(QString(prop.property().name()), QString("defaultProperty"));
917 QVERIFY(!QQmlPropertyPrivate::binding(prop));
918 QTest::ignoreMessage(type: QtWarningMsg, message: "<Unknown File>: Unable to assign null to int");
919 QQmlPropertyPrivate::setBinding(that: prop, binding.data());
920 QVERIFY(binding);
921 QCOMPARE(QQmlPropertyPrivate::binding(prop), binding.data());
922 QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
923 QQmlPropertyPrivate::takeSignalExpression(that: prop, sigExpr);
924 QVERIFY(sigExprWatcher.wasDeleted());
925 QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty"));
926 QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
927 QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
928
929 delete obj;
930 }
931
932 {
933 QQmlProperty prop(&dobject, QString("onClicked"), engine.rootContext());
934
935 QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(context: engine.rootContext())));
936 static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
937 QVERIFY(binding);
938 QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(p: prop)->signalIndex(), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
939 QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
940 QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
941
942 QObject *obj = new QObject;
943
944 QCOMPARE(prop.name(), QString("onClicked"));
945 QCOMPARE(prop.read(), QVariant());
946 QCOMPARE(prop.write(QVariant("Hello")), false);
947 QCOMPARE(prop.hasNotifySignal(), false);
948 QCOMPARE(prop.needsNotifySignal(), false);
949 QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false);
950 QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
951 QCOMPARE(prop.connectNotifySignal(obj, 0), false);
952 QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
953 QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
954 QCOMPARE(prop.connectNotifySignal(obj, -1), false);
955 QCOMPARE(QString(prop.method().methodSignature()), QString("clicked()"));
956 QCOMPARE(prop.type(), QQmlProperty::SignalProperty);
957 QCOMPARE(prop.isProperty(), false);
958 QCOMPARE(prop.isWritable(), false);
959 QCOMPARE(prop.isDesignable(), false);
960 QCOMPARE(prop.isResettable(), false);
961 QCOMPARE(prop.isSignalProperty(), true);
962 QCOMPARE(prop.isValid(), true);
963 QCOMPARE(prop.object(), qobject_cast<QObject*>(&dobject));
964 QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
965 QCOMPARE(prop.propertyType(), 0);
966 QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
967 QCOMPARE(prop.property().name(), (const char *)nullptr);
968 QVERIFY(!QQmlPropertyPrivate::binding(prop));
969 QQmlPropertyPrivate::setBinding(that: prop, binding.data());
970 QVERIFY(binding->ref == 1);
971 QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
972 QQmlPropertyPrivate::takeSignalExpression(that: prop, sigExpr);
973 QVERIFY(!sigExprWatcher.wasDeleted());
974 QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr);
975 QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("clicked()"));
976 QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
977 QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
978
979 delete obj;
980 }
981
982 {
983 QQmlProperty prop(&dobject, QString("onPropertyWithNotifyChanged"), engine.rootContext());
984
985 QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(p: prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(context: engine.rootContext())));
986 static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
987 QVERIFY(binding);
988 QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(p: prop)->signalIndex(), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
989 QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
990 QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
991
992 QObject *obj = new QObject;
993
994 QCOMPARE(prop.name(), QString("onOddlyNamedNotifySignal"));
995 QCOMPARE(prop.read(), QVariant());
996 QCOMPARE(prop.write(QVariant("Hello")), false);
997 QCOMPARE(prop.hasNotifySignal(), false);
998 QCOMPARE(prop.needsNotifySignal(), false);
999 QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false);
1000 QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
1001 QCOMPARE(prop.connectNotifySignal(obj, 0), false);
1002 QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
1003 QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
1004 QCOMPARE(prop.connectNotifySignal(obj, -1), false);
1005 QCOMPARE(QString(prop.method().methodSignature()), QString("oddlyNamedNotifySignal()"));
1006 QCOMPARE(prop.type(), QQmlProperty::SignalProperty);
1007 QCOMPARE(prop.isProperty(), false);
1008 QCOMPARE(prop.isWritable(), false);
1009 QCOMPARE(prop.isDesignable(), false);
1010 QCOMPARE(prop.isResettable(), false);
1011 QCOMPARE(prop.isSignalProperty(), true);
1012 QCOMPARE(prop.isValid(), true);
1013 QCOMPARE(prop.object(), qobject_cast<QObject*>(&dobject));
1014 QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
1015 QCOMPARE(prop.propertyType(), 0);
1016 QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
1017 QCOMPARE(prop.property().name(), (const char *)nullptr);
1018 QVERIFY(!QQmlPropertyPrivate::binding(prop));
1019 QQmlPropertyPrivate::setBinding(that: prop, binding.data());
1020 QVERIFY(binding->ref == 1);
1021 QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
1022 QQmlPropertyPrivate::takeSignalExpression(that: prop, sigExpr);
1023 QVERIFY(!sigExprWatcher.wasDeleted());
1024 QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr);
1025 QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("oddlyNamedNotifySignal()"));
1026 QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
1027 QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
1028
1029 delete obj;
1030 }
1031}
1032
1033void tst_qqmlproperty::name()
1034{
1035 {
1036 QQmlProperty p;
1037 QCOMPARE(p.name(), QString());
1038 }
1039
1040 {
1041 PropertyObject o;
1042 QQmlProperty p(&o);
1043 QCOMPARE(p.name(), QString("defaultProperty"));
1044 }
1045
1046 {
1047 QObject o;
1048 QQmlProperty p(&o, QString("objectName"));
1049 QCOMPARE(p.name(), QString("objectName"));
1050 }
1051
1052 {
1053 PropertyObject o;
1054 QQmlProperty p(&o, "onClicked");
1055 QCOMPARE(p.name(), QString("onClicked"));
1056 }
1057
1058 {
1059 QObject o;
1060 QQmlProperty p(&o, "onClicked");
1061 QCOMPARE(p.name(), QString());
1062 }
1063
1064 {
1065 PropertyObject o;
1066 QQmlProperty p(&o, "onPropertyWithNotifyChanged");
1067 QCOMPARE(p.name(), QString("onOddlyNamedNotifySignal"));
1068 }
1069
1070 {
1071 QObject o;
1072 QQmlProperty p(&o, "onPropertyWithNotifyChanged");
1073 QCOMPARE(p.name(), QString());
1074 }
1075
1076 {
1077 QObject o;
1078 QQmlProperty p(&o, "foo");
1079 QCOMPARE(p.name(), QString());
1080 }
1081
1082 {
1083 QQmlProperty p(nullptr, "foo");
1084 QCOMPARE(p.name(), QString());
1085 }
1086
1087 {
1088 PropertyObject o;
1089 QQmlProperty p(&o, "rectProperty");
1090 QCOMPARE(p.name(), QString("rectProperty"));
1091 }
1092
1093 {
1094 PropertyObject o;
1095 QQmlProperty p(&o, "rectProperty.x");
1096 QCOMPARE(p.name(), QString("rectProperty.x"));
1097 }
1098
1099 {
1100 PropertyObject o;
1101 QQmlProperty p(&o, "rectProperty.foo");
1102 QCOMPARE(p.name(), QString());
1103 }
1104}
1105
1106void tst_qqmlproperty::read()
1107{
1108 // Invalid
1109 {
1110 QQmlProperty p;
1111 QCOMPARE(p.read(), QVariant());
1112 }
1113
1114 // Default prop
1115 {
1116 PropertyObject o;
1117 QQmlProperty p(&o);
1118 QCOMPARE(p.read(), QVariant(10));
1119 }
1120
1121 // Invalid default prop
1122 {
1123 QObject o;
1124 QQmlProperty p(&o);
1125 QCOMPARE(p.read(), QVariant());
1126 }
1127
1128 // Value prop by name
1129 {
1130 QObject o;
1131
1132 QQmlProperty p(&o, "objectName");
1133 QCOMPARE(p.read(), QVariant(QString()));
1134
1135 o.setObjectName("myName");
1136
1137 QCOMPARE(p.read(), QVariant("myName"));
1138 }
1139
1140 // Value prop by name (static)
1141 {
1142 QObject o;
1143
1144 QCOMPARE(QQmlProperty::read(&o, "objectName"), QVariant(QString()));
1145
1146 o.setObjectName("myName");
1147
1148 QCOMPARE(QQmlProperty::read(&o, "objectName"), QVariant("myName"));
1149 }
1150
1151 // Value-type prop
1152 {
1153 PropertyObject o;
1154 QQmlProperty p(&o, "rectProperty.x");
1155 QCOMPARE(p.read(), QVariant(10));
1156 }
1157
1158 // Invalid value-type prop
1159 {
1160 PropertyObject o;
1161 QQmlProperty p(&o, "rectProperty.foo");
1162 QCOMPARE(p.read(), QVariant());
1163 }
1164
1165 // Signal property
1166 {
1167 PropertyObject o;
1168 QQmlProperty p(&o, "onClicked");
1169 QCOMPARE(p.read(), QVariant());
1170
1171 QQmlPropertyPrivate::takeSignalExpression(that: p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1));
1172 QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p));
1173
1174 QCOMPARE(p.read(), QVariant());
1175 }
1176
1177 // Automatic signal property
1178 {
1179 PropertyObject o;
1180 QQmlProperty p(&o, "onPropertyWithNotifyChanged");
1181 QCOMPARE(p.read(), QVariant());
1182
1183 QQmlPropertyPrivate::takeSignalExpression(that: p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1));
1184 QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p));
1185
1186 QCOMPARE(p.read(), QVariant());
1187 }
1188
1189 // Deleted object
1190 {
1191 PropertyObject *o = new PropertyObject;
1192 QQmlProperty p(o, "rectProperty.x");
1193 QCOMPARE(p.read(), QVariant(10));
1194 delete o;
1195 QCOMPARE(p.read(), QVariant());
1196 }
1197
1198 // Object property registered with Qt, but not registered with QML.
1199 {
1200 PropertyObject o;
1201 QQmlProperty p(&o, "qObject");
1202 QCOMPARE(p.propertyTypeCategory(), QQmlProperty::Object);
1203
1204 QCOMPARE(p.propertyType(), qMetaTypeId<MyQObject*>());
1205 QVariant v = p.read();
1206 QVERIFY(v.canConvert(QMetaType::QObjectStar));
1207 QVERIFY(qvariant_cast<QObject *>(v) == o.qObject());
1208 }
1209 {
1210 QQmlEngine engine;
1211 PropertyObject o;
1212 QQmlProperty p(&o, "qObject", &engine);
1213 QCOMPARE(p.propertyTypeCategory(), QQmlProperty::Object);
1214
1215 QCOMPARE(p.propertyType(), qMetaTypeId<MyQObject*>());
1216 QVariant v = p.read();
1217 QVERIFY(v.canConvert(QMetaType::QObjectStar));
1218 QVERIFY(qvariant_cast<QObject *>(v) == o.qObject());
1219 }
1220
1221 // Object property
1222 {
1223 PropertyObject o;
1224 QQmlProperty p(&o, "qmlObject");
1225 QCOMPARE(p.propertyTypeCategory(), QQmlProperty::Object);
1226 QCOMPARE(p.propertyType(), qMetaTypeId<MyQmlObject*>());
1227 QVariant v = p.read();
1228 QCOMPARE(v.userType(), int(QMetaType::QObjectStar));
1229 QVERIFY(qvariant_cast<QObject *>(v) == o.qmlObject());
1230 }
1231 {
1232 QQmlComponent component(&engine, testFileUrl(fileName: "readSynthesizedObject.qml"));
1233 QScopedPointer<QObject> object(component.create());
1234 QVERIFY(object != nullptr);
1235
1236 QQmlProperty p(object.data(), "test", &engine);
1237
1238 QCOMPARE(p.propertyTypeCategory(), QQmlProperty::Object);
1239 QVERIFY(p.propertyType() != QMetaType::QObjectStar);
1240
1241 QVariant v = p.read();
1242 QCOMPARE(v.userType(), int(QMetaType::QObjectStar));
1243 QCOMPARE(qvariant_cast<QObject *>(v)->property("a").toInt(), 10);
1244 QCOMPARE(qvariant_cast<QObject *>(v)->property("b").toInt(), 19);
1245 }
1246 { // static
1247 QQmlComponent component(&engine, testFileUrl(fileName: "readSynthesizedObject.qml"));
1248 QScopedPointer<QObject> object(component.create());
1249 QVERIFY(object != nullptr);
1250
1251 QVariant v = QQmlProperty::read(object.data(), "test", &engine);
1252 QCOMPARE(v.userType(), int(QMetaType::QObjectStar));
1253 QCOMPARE(qvariant_cast<QObject *>(v)->property("a").toInt(), 10);
1254 QCOMPARE(qvariant_cast<QObject *>(v)->property("b").toInt(), 19);
1255 }
1256
1257 // Attached property
1258 {
1259 QQmlComponent component(&engine);
1260 component.setData("import Test 1.0\nMyContainer { }", baseUrl: QUrl());
1261 QScopedPointer<QObject> object(component.create());
1262 QVERIFY(object != nullptr);
1263
1264 QQmlProperty p(object.data(), "MyContainer.foo", qmlContext(object.data()));
1265 QCOMPARE(p.read(), QVariant(13));
1266 }
1267 {
1268 QQmlComponent component(&engine);
1269 component.setData("import Test 1.0\nMyContainer { MyContainer.foo: 10 }", baseUrl: QUrl());
1270 QScopedPointer<QObject> object(component.create());
1271 QVERIFY(object != nullptr);
1272
1273 QQmlProperty p(object.data(), "MyContainer.foo", qmlContext(object.data()));
1274 QCOMPARE(p.read(), QVariant(10));
1275 }
1276 {
1277 QQmlComponent component(&engine);
1278 component.setData("import Test 1.0 as Foo\nFoo.MyContainer { Foo.MyContainer.foo: 10 }", baseUrl: QUrl());
1279 QScopedPointer<QObject> object(component.create());
1280 QVERIFY(object != nullptr);
1281
1282 QQmlProperty p(object.data(), "Foo.MyContainer.foo", qmlContext(object.data()));
1283 QCOMPARE(p.read(), QVariant(10));
1284 }
1285 { // static
1286 QQmlComponent component(&engine);
1287 component.setData("import Test 1.0 as Foo\nFoo.MyContainer { Foo.MyContainer.foo: 10 }", baseUrl: QUrl());
1288 QScopedPointer<QObject> object(component.create());
1289 QVERIFY(object != nullptr);
1290
1291 QCOMPARE(QQmlProperty::read(object.data(), "Foo.MyContainer.foo",
1292 qmlContext(object.data())), QVariant(10));
1293 }
1294}
1295
1296void tst_qqmlproperty::write()
1297{
1298 // Invalid
1299 {
1300 QQmlProperty p;
1301 QCOMPARE(p.write(QVariant(10)), false);
1302 }
1303
1304 // Read-only default prop
1305 {
1306 PropertyObject o;
1307 QQmlProperty p(&o);
1308 QCOMPARE(p.write(QVariant(10)), false);
1309 }
1310
1311 // Invalid default prop
1312 {
1313 QObject o;
1314 QQmlProperty p(&o);
1315 QCOMPARE(p.write(QVariant(10)), false);
1316 }
1317
1318 // Read-only prop by name
1319 {
1320 PropertyObject o;
1321 QQmlProperty p(&o, QString("defaultProperty"));
1322 QCOMPARE(p.write(QVariant(10)), false);
1323 }
1324
1325 // Writable prop by name
1326 {
1327 PropertyObject o;
1328 QQmlProperty p(&o, QString("objectName"));
1329 QCOMPARE(o.objectName(), QString());
1330 QCOMPARE(p.write(QVariant(QString("myName"))), true);
1331 QCOMPARE(o.objectName(), QString("myName"));
1332 }
1333
1334 // Writable prop by name (static)
1335 {
1336 PropertyObject o;
1337 QCOMPARE(QQmlProperty::write(&o, QString("objectName"), QVariant(QString("myName"))), true);
1338 QCOMPARE(o.objectName(), QString("myName"));
1339 }
1340
1341 // Deleted object
1342 {
1343 PropertyObject *o = new PropertyObject;
1344 QQmlProperty p(o, QString("objectName"));
1345 QCOMPARE(p.write(QVariant(QString("myName"))), true);
1346 QCOMPARE(o->objectName(), QString("myName"));
1347
1348 delete o;
1349
1350 QCOMPARE(p.write(QVariant(QString("myName"))), false);
1351 }
1352
1353 // Signal property
1354 {
1355 PropertyObject o;
1356 QQmlProperty p(&o, "onClicked");
1357 QCOMPARE(p.write(QVariant("console.log(1921)")), false);
1358
1359 QQmlPropertyPrivate::takeSignalExpression(that: p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1));
1360 QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p));
1361
1362 QCOMPARE(p.write(QVariant("console.log(1921)")), false);
1363
1364 QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p));
1365 }
1366
1367 // Automatic signal property
1368 {
1369 PropertyObject o;
1370 QQmlProperty p(&o, "onPropertyWithNotifyChanged");
1371 QCOMPARE(p.write(QVariant("console.log(1921)")), false);
1372
1373 QQmlPropertyPrivate::takeSignalExpression(that: p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(context: engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1));
1374 QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p));
1375
1376 QCOMPARE(p.write(QVariant("console.log(1921)")), false);
1377
1378 QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p));
1379 }
1380
1381 // Value-type property
1382 {
1383 PropertyObject o;
1384 QQmlProperty p(&o, "wrectProperty");
1385
1386 QCOMPARE(o.wrectProperty(), QRect());
1387 QCOMPARE(p.write(QRect(1, 13, 99, 8)), true);
1388 QCOMPARE(o.wrectProperty(), QRect(1, 13, 99, 8));
1389
1390 QQmlProperty p2(&o, "wrectProperty.x");
1391 QCOMPARE(p2.read(), QVariant(1));
1392 QCOMPARE(p2.write(QVariant(6)), true);
1393 QCOMPARE(p2.read(), QVariant(6));
1394 QCOMPARE(o.wrectProperty(), QRect(6, 13, 99, 8));
1395 }
1396
1397 // URL-property
1398 {
1399 PropertyObject o;
1400 QQmlProperty p(&o, "url");
1401
1402 QCOMPARE(p.write(QUrl("main.qml")), true);
1403 QCOMPARE(o.url(), QUrl("main.qml"));
1404
1405 QQmlProperty p2(&o, "url", engine.rootContext());
1406
1407 QUrl result = engine.baseUrl().resolved(relative: QUrl("main.qml"));
1408 QVERIFY(result != QUrl("main.qml"));
1409
1410 QCOMPARE(p2.write(QUrl("main.qml")), true);
1411 QCOMPARE(o.url(), result);
1412 }
1413 { // static
1414 PropertyObject o;
1415
1416 QCOMPARE(QQmlProperty::write(&o, "url", QUrl("main.qml")), true);
1417 QCOMPARE(o.url(), QUrl("main.qml"));
1418
1419 QUrl result = engine.baseUrl().resolved(relative: QUrl("main.qml"));
1420 QVERIFY(result != QUrl("main.qml"));
1421
1422 QCOMPARE(QQmlProperty::write(&o, "url", QUrl("main.qml"), engine.rootContext()), true);
1423 QCOMPARE(o.url(), result);
1424 }
1425
1426 // Char/string-property
1427 {
1428 PropertyObject o;
1429 QQmlProperty qcharProperty(&o, "qcharProperty");
1430 QQmlProperty stringProperty(&o, "stringProperty");
1431
1432 const int black_circle = 0x25cf;
1433
1434 QCOMPARE(qcharProperty.write(QString("foo")), false);
1435 QCOMPARE(qcharProperty.write('Q'), true);
1436 QCOMPARE(qcharProperty.read(), QVariant('Q'));
1437 QCOMPARE(qcharProperty.write(QChar(black_circle)), true);
1438 QCOMPARE(qcharProperty.read(), QVariant(QChar(black_circle)));
1439
1440 QCOMPARE(o.stringProperty(), QString("foo")); // Default value
1441 QCOMPARE(stringProperty.write(QString("bar")), true);
1442 QCOMPARE(o.stringProperty(), QString("bar"));
1443 QCOMPARE(stringProperty.write(QVariant(1234)), true);
1444 QCOMPARE(stringProperty.read().toString(), QString::number(1234));
1445 QCOMPARE(stringProperty.write('A'), true);
1446 QCOMPARE(stringProperty.read().toString(), QString::number('A'));
1447 QCOMPARE(stringProperty.write(QChar(black_circle)), true);
1448 QCOMPARE(stringProperty.read(), QVariant(QChar(black_circle)));
1449
1450 { // QChar -> QString
1451 QQmlComponent component(&engine);
1452 component.setData("import Test 1.0\nPropertyObject { stringProperty: constQChar }", baseUrl: QUrl());
1453 QScopedPointer<QObject> object(component.create());
1454 PropertyObject *propertyObject = qobject_cast<PropertyObject*>(object: object.data());
1455 QVERIFY(propertyObject != nullptr);
1456 if (propertyObject) {
1457 QQmlProperty stringProperty(propertyObject, "stringProperty");
1458 QCOMPARE(stringProperty.read(), QVariant(QString(propertyObject->constQChar())));
1459 }
1460 }
1461
1462 }
1463
1464 // VariantMap-property
1465 QVariantMap vm;
1466 vm.insert(key: "key", value: "value");
1467
1468 {
1469 PropertyObject o;
1470 QQmlProperty p(&o, "variantMap");
1471
1472 QCOMPARE(p.write(vm), true);
1473 QCOMPARE(o.variantMap(), vm);
1474
1475 QQmlProperty p2(&o, "variantMap", engine.rootContext());
1476
1477 QCOMPARE(p2.write(vm), true);
1478 QCOMPARE(o.variantMap(), vm);
1479 }
1480 { // static
1481 PropertyObject o;
1482
1483 QCOMPARE(QQmlProperty::write(&o, "variantMap", vm), true);
1484 QCOMPARE(o.variantMap(), vm);
1485
1486 QCOMPARE(QQmlProperty::write(&o, "variantMap", vm, engine.rootContext()), true);
1487 QCOMPARE(o.variantMap(), vm);
1488 }
1489
1490 // Attached property
1491 {
1492 QQmlComponent component(&engine);
1493 component.setData("import Test 1.0\nMyContainer { }", baseUrl: QUrl());
1494 QObject *object = component.create();
1495 QVERIFY(object != nullptr);
1496
1497 QQmlProperty p(object, "MyContainer.foo", qmlContext(object));
1498 p.write(QVariant(99));
1499 QCOMPARE(p.read(), QVariant(99));
1500 delete object;
1501 }
1502 {
1503 QQmlComponent component(&engine);
1504 component.setData("import Test 1.0 as Foo\nFoo.MyContainer { Foo.MyContainer.foo: 10 }", baseUrl: QUrl());
1505 QObject *object = component.create();
1506 QVERIFY(object != nullptr);
1507
1508 QQmlProperty p(object, "Foo.MyContainer.foo", qmlContext(object));
1509 p.write(QVariant(99));
1510 QCOMPARE(p.read(), QVariant(99));
1511 delete object;
1512 }
1513 // Writable pointer to QObject derived
1514 {
1515 PropertyObject o;
1516 QQmlProperty p(&o, QString("qObject"));
1517 QCOMPARE(o.qObject(), (QObject*)nullptr);
1518 QObject *newObject = new MyQObject(this);
1519 QCOMPARE(p.write(QVariant::fromValue(newObject)), true);
1520 QCOMPARE(o.qObject(), newObject);
1521 QVariant data = p.read();
1522 QCOMPARE(data.value<QObject*>(), newObject);
1523 QCOMPARE(data.value<MyQObject*>(), newObject);
1524 // Incompatible types can not be written.
1525 QCOMPARE(p.write(QVariant::fromValue(new MyQmlObject(this))), false);
1526 QVariant newData = p.read();
1527 QCOMPARE(newData.value<QObject*>(), newObject);
1528 QCOMPARE(newData.value<MyQObject*>(), newObject);
1529 }
1530 {
1531 QQmlEngine engine;
1532 PropertyObject o;
1533 QQmlProperty p(&o, QString("qObject"), &engine);
1534 QCOMPARE(o.qObject(), (QObject*)nullptr);
1535 QObject *newObject = new MyQObject(this);
1536 QCOMPARE(p.write(QVariant::fromValue(newObject)), true);
1537 QCOMPARE(o.qObject(), newObject);
1538 QVariant data = p.read();
1539 QCOMPARE(data.value<QObject*>(), newObject);
1540 QCOMPARE(data.value<MyQObject*>(), newObject);
1541 // Incompatible types can not be written.
1542 QCOMPARE(p.write(QVariant::fromValue(new MyQmlObject(this))), false);
1543 QVariant newData = p.read();
1544 QCOMPARE(newData.value<QObject*>(), newObject);
1545 QCOMPARE(newData.value<MyQObject*>(), newObject);
1546 }
1547}
1548
1549void tst_qqmlproperty::reset()
1550{
1551 // Invalid
1552 {
1553 QQmlProperty p;
1554 QCOMPARE(p.isResettable(), false);
1555 QCOMPARE(p.reset(), false);
1556 }
1557
1558 // Read-only default prop
1559 {
1560 PropertyObject o;
1561 QQmlProperty p(&o);
1562 QCOMPARE(p.isResettable(), false);
1563 QCOMPARE(p.reset(), false);
1564 }
1565
1566 // Invalid default prop
1567 {
1568 QObject o;
1569 QQmlProperty p(&o);
1570 QCOMPARE(p.isResettable(), false);
1571 QCOMPARE(p.reset(), false);
1572 }
1573
1574 // Non-resettable-only prop by name
1575 {
1576 PropertyObject o;
1577 QQmlProperty p(&o, QString("defaultProperty"));
1578 QCOMPARE(p.isResettable(), false);
1579 QCOMPARE(p.reset(), false);
1580 }
1581
1582 // Resettable prop by name
1583 {
1584 PropertyObject o;
1585 QQmlProperty p(&o, QString("resettableProperty"));
1586
1587 QCOMPARE(p.read(), QVariant(9));
1588 QCOMPARE(p.write(QVariant(11)), true);
1589 QCOMPARE(p.read(), QVariant(11));
1590
1591 QCOMPARE(p.isResettable(), true);
1592 QCOMPARE(p.reset(), true);
1593
1594 QCOMPARE(p.read(), QVariant(9));
1595 }
1596
1597 // Deleted object
1598 {
1599 PropertyObject *o = new PropertyObject;
1600
1601 QQmlProperty p(o, QString("resettableProperty"));
1602
1603 QCOMPARE(p.isResettable(), true);
1604 QCOMPARE(p.reset(), true);
1605
1606 delete o;
1607
1608 QCOMPARE(p.isResettable(), false);
1609 QCOMPARE(p.reset(), false);
1610 }
1611
1612 // Signal property
1613 {
1614 PropertyObject o;
1615 QQmlProperty p(&o, "onClicked");
1616
1617 QCOMPARE(p.isResettable(), false);
1618 QCOMPARE(p.reset(), false);
1619 }
1620
1621 // Automatic signal property
1622 {
1623 PropertyObject o;
1624 QQmlProperty p(&o, "onPropertyWithNotifyChanged");
1625
1626 QCOMPARE(p.isResettable(), false);
1627 QCOMPARE(p.reset(), false);
1628 }
1629}
1630
1631void tst_qqmlproperty::writeObjectToList()
1632{
1633 QQmlComponent containerComponent(&engine);
1634 containerComponent.setData("import Test 1.0\nMyContainer { children: MyQmlObject {} }", baseUrl: QUrl());
1635 QScopedPointer<QObject> object(containerComponent.create());
1636 MyContainer *container = qobject_cast<MyContainer*>(object: object.data());
1637 QVERIFY(container != nullptr);
1638 QQmlListReference list(container, "children");
1639 QCOMPARE(list.count(), 1);
1640
1641 QScopedPointer<MyQmlObject> childObject(new MyQmlObject);
1642 QQmlProperty prop(container, "children");
1643 prop.write(QVariant::fromValue(value: childObject.data()));
1644 QCOMPARE(list.count(), 1);
1645 QCOMPARE(list.at(0), qobject_cast<QObject*>(childObject.data()));
1646}
1647
1648void tst_qqmlproperty::writeListToList()
1649{
1650 QQmlComponent containerComponent(&engine);
1651 containerComponent.setData("import Test 1.0\nMyContainer { children: MyQmlObject {} }", baseUrl: QUrl());
1652 QScopedPointer<QObject> object(containerComponent.create());
1653 MyContainer *container = qobject_cast<MyContainer*>(object: object.data());
1654 QVERIFY(container != nullptr);
1655 QQmlListReference list(container, "children");
1656 QCOMPARE(list.count(), 1);
1657
1658 QList<QObject*> objList;
1659 objList << new MyQmlObject(this) << new MyQmlObject(this)
1660 << new MyQmlObject(this) << new MyQmlObject(this);
1661 QQmlProperty prop(container, "children");
1662 prop.write(QVariant::fromValue(value: objList));
1663 QCOMPARE(list.count(), 4);
1664
1665 //XXX need to try this with read/write prop (for read-only it correctly doesn't write)
1666 /*QList<MyQmlObject*> typedObjList;
1667 typedObjList << new MyQmlObject();
1668 prop.write(QVariant::fromValue(&typedObjList));
1669 QCOMPARE(container->children()->size(), 1);*/
1670}
1671
1672void tst_qqmlproperty::urlHandling_data()
1673{
1674 QTest::addColumn<QByteArray>(name: "input");
1675 QTest::addColumn<QString>(name: "scheme");
1676 QTest::addColumn<QString>(name: "path");
1677 QTest::addColumn<QByteArray>(name: "encoded");
1678
1679 QTest::newRow(dataTag: "unspecifiedFile")
1680 << QByteArray("main.qml")
1681 << QString("")
1682 << QString("main.qml")
1683 << QByteArray("main.qml");
1684
1685 QTest::newRow(dataTag: "specifiedFile")
1686 << QByteArray("file:///main.qml")
1687 << QString("file")
1688 << QString("/main.qml")
1689 << QByteArray("file:///main.qml");
1690
1691 QTest::newRow(dataTag: "httpFile")
1692 << QByteArray("http://www.example.com/main.qml")
1693 << QString("http")
1694 << QString("/main.qml")
1695 << QByteArray("http://www.example.com/main.qml");
1696
1697 QTest::newRow(dataTag: "pathFile")
1698 << QByteArray("http://www.example.com/resources/main.qml")
1699 << QString("http")
1700 << QString("/resources/main.qml")
1701 << QByteArray("http://www.example.com/resources/main.qml");
1702
1703 QTest::newRow(dataTag: "encodableName")
1704 << QByteArray("http://www.example.com/main file.qml")
1705 << QString("http")
1706 << QString("/main file.qml")
1707 << QByteArray("http://www.example.com/main%20file.qml");
1708
1709 QTest::newRow(dataTag: "preencodedName")
1710 << QByteArray("http://www.example.com/resources%7Cmain%20file.qml")
1711 << QString("http")
1712 << QString("/resources|main file.qml")
1713 << QByteArray("http://www.example.com/resources%7Cmain%20file.qml");
1714
1715 QTest::newRow(dataTag: "encodableQuery")
1716 << QByteArray("http://www.example.com/main.qml?type=text/qml&comment=now working?")
1717 << QString("http")
1718 << QString("/main.qml")
1719 << QByteArray("http://www.example.com/main.qml?type=text/qml&comment=now%20working?");
1720
1721 // Although 'text%2Fqml' is pre-encoded, it will be decoded to allow correct QUrl classification
1722 QTest::newRow(dataTag: "preencodedQuery")
1723 << QByteArray("http://www.example.com/main.qml?type=text%2Fqml&comment=now working%3F")
1724 << QString("http")
1725 << QString("/main.qml")
1726 << QByteArray("http://www.example.com/main.qml?type=text/qml&comment=now%20working%3F");
1727
1728 QTest::newRow(dataTag: "encodableFragment")
1729 << QByteArray("http://www.example.com/main.qml?type=text/qml#start+30000|volume+50%")
1730 << QString("http")
1731 << QString("/main.qml")
1732 << QByteArray("http://www.example.com/main.qml?type=text/qml#start+30000%7Cvolume+50%25");
1733
1734 QTest::newRow(dataTag: "improperlyEncodedFragment")
1735 << QByteArray("http://www.example.com/main.qml?type=text/qml#start+30000%7Cvolume%2B50%")
1736 << QString("http")
1737 << QString("/main.qml")
1738 << QByteArray("http://www.example.com/main.qml?type=text/qml#start+30000%257Cvolume%252B50%25");
1739}
1740
1741void tst_qqmlproperty::urlHandling()
1742{
1743 QFETCH(QByteArray, input);
1744 QFETCH(QString, scheme);
1745 QFETCH(QString, path);
1746 QFETCH(QByteArray, encoded);
1747
1748 QString inputString(QString::fromUtf8(str: input));
1749
1750 {
1751 PropertyObject o;
1752 QQmlProperty p(&o, "url");
1753
1754 // Test url written as QByteArray
1755 QCOMPARE(p.write(input), true);
1756 QUrl byteArrayResult(o.url());
1757
1758 QCOMPARE(byteArrayResult.scheme(), scheme);
1759 QCOMPARE(byteArrayResult.path(), path);
1760 QCOMPARE(byteArrayResult.toString(QUrl::FullyEncoded), QString::fromUtf8(encoded));
1761 QCOMPARE(byteArrayResult.toEncoded(), encoded);
1762 }
1763
1764 {
1765 PropertyObject o;
1766 QQmlProperty p(&o, "url");
1767
1768 // Test url written as QString
1769 QCOMPARE(p.write(inputString), true);
1770 QUrl stringResult(o.url());
1771
1772 QCOMPARE(stringResult.scheme(), scheme);
1773 QCOMPARE(stringResult.path(), path);
1774 QCOMPARE(stringResult.toString(QUrl::FullyEncoded), QString::fromUtf8(encoded));
1775 QCOMPARE(stringResult.toEncoded(), encoded);
1776 }
1777}
1778
1779void tst_qqmlproperty::variantMapHandling_data()
1780{
1781 QTest::addColumn<QVariantMap>(name: "vm");
1782
1783 // Object literals
1784 {
1785 QVariantMap m;
1786 QTest::newRow(dataTag: "{}") << m;
1787 }
1788 {
1789 QVariantMap m;
1790 m["a"] = QVariantMap();
1791 QTest::newRow(dataTag: "{ a:{} }") << m;
1792 }
1793 {
1794 QVariantMap m, m2;
1795 m2["b"] = 10;
1796 m2["c"] = 20;
1797 m["a"] = m2;
1798 QTest::newRow(dataTag: "{ a:{b:10, c:20} }") << m;
1799 }
1800 {
1801 QVariantMap m;
1802 m["a"] = 10;
1803 m["b"] = QVariantList() << 20 << 30;
1804 QTest::newRow(dataTag: "{ a:10, b:[20, 30]}") << m;
1805 }
1806
1807 // Cyclic objects
1808 {
1809 QVariantMap m;
1810 m["p"] = QVariantMap();
1811 QTest::newRow(dataTag: "var o={}; o.p=o") << m;
1812 }
1813 {
1814 QVariantMap m;
1815 m["p"] = 123;
1816 m["q"] = QVariantMap();
1817 QTest::newRow(dataTag: "var o={}; o.p=123; o.q=o") << m;
1818 }
1819}
1820
1821void tst_qqmlproperty::variantMapHandling()
1822{
1823 QFETCH(QVariantMap, vm);
1824
1825 PropertyObject o;
1826 QQmlProperty p(&o, "variantMap");
1827
1828 QCOMPARE(p.write(vm), true);
1829 QCOMPARE(o.variantMap(), vm);
1830}
1831
1832void tst_qqmlproperty::crashOnValueProperty()
1833{
1834 QQmlEngine *engine = new QQmlEngine;
1835 QQmlComponent component(engine);
1836
1837 component.setData("import Test 1.0\nPropertyObject { wrectProperty.x: 10 }", baseUrl: QUrl());
1838 QScopedPointer<QObject> object(component.create());
1839 PropertyObject *propertyObject = qobject_cast<PropertyObject*>(object: object.data());
1840 QVERIFY(propertyObject != nullptr);
1841
1842 QQmlProperty p(propertyObject, "wrectProperty.x", qmlContext(propertyObject));
1843 QCOMPARE(p.name(), QString("wrectProperty.x"));
1844
1845 QCOMPARE(p.read(), QVariant(10));
1846
1847 //don't crash once the engine is deleted
1848 delete engine;
1849 engine = nullptr;
1850
1851 QCOMPARE(p.propertyTypeName(), "int");
1852 QCOMPARE(p.read(), QVariant(10));
1853 p.write(QVariant(20));
1854 QCOMPARE(p.read(), QVariant(20));
1855}
1856
1857void tst_qqmlproperty::aliasPropertyBindings_data()
1858{
1859 QTest::addColumn<QString>(name: "file");
1860 QTest::addColumn<QString>(name: "subObject");
1861
1862 QTest::newRow(dataTag: "same object") << "aliasPropertyBindings.qml" << "";
1863 QTest::newRow(dataTag: "different objects") << "aliasPropertyBindings2.qml" << "innerObject";
1864}
1865
1866// QTBUG-13719, QTBUG-58271
1867void tst_qqmlproperty::aliasPropertyBindings()
1868{
1869 QFETCH(QString, file);
1870 QFETCH(QString, subObject);
1871
1872 QQmlComponent component(&engine, testFileUrl(fileName: file));
1873
1874 QObject *object = component.create();
1875 QVERIFY(object != nullptr);
1876
1877 // the object where realProperty lives
1878 QObject *realPropertyObject = object;
1879 if (!subObject.isEmpty())
1880 realPropertyObject = object->property(name: subObject.toLatin1()).value<QObject*>();
1881
1882 QCOMPARE(realPropertyObject->property("realProperty").toReal(), 90.);
1883 QCOMPARE(object->property("aliasProperty").toReal(), 90.);
1884
1885 object->setProperty(name: "test", value: 10);
1886
1887 QCOMPARE(realPropertyObject->property("realProperty").toReal(), 110.);
1888 QCOMPARE(object->property("aliasProperty").toReal(), 110.);
1889
1890 QQmlProperty realProperty(realPropertyObject, QLatin1String("realProperty"));
1891 QQmlProperty aliasProperty(object, QLatin1String("aliasProperty"));
1892
1893 // Check there is a binding on these two properties
1894 QVERIFY(QQmlPropertyPrivate::binding(realProperty) != nullptr);
1895 QVERIFY(QQmlPropertyPrivate::binding(aliasProperty) != nullptr);
1896
1897 // Check that its the same binding on these two properties
1898 QCOMPARE(QQmlPropertyPrivate::binding(realProperty),
1899 QQmlPropertyPrivate::binding(aliasProperty));
1900
1901 // Change the binding
1902 object->setProperty(name: "state", value: QString("switch"));
1903
1904 QVERIFY(QQmlPropertyPrivate::binding(realProperty) != nullptr);
1905 QVERIFY(QQmlPropertyPrivate::binding(aliasProperty) != nullptr);
1906 QCOMPARE(QQmlPropertyPrivate::binding(realProperty),
1907 QQmlPropertyPrivate::binding(aliasProperty));
1908
1909 QCOMPARE(realPropertyObject->property("realProperty").toReal(), 96.);
1910 QCOMPARE(object->property("aliasProperty").toReal(), 96.);
1911
1912 // Check the old binding really has not effect any more
1913 object->setProperty(name: "test", value: 4);
1914
1915 QCOMPARE(realPropertyObject->property("realProperty").toReal(), 96.);
1916 QCOMPARE(object->property("aliasProperty").toReal(), 96.);
1917
1918 object->setProperty(name: "test2", value: 9);
1919
1920 QCOMPARE(realPropertyObject->property("realProperty").toReal(), 288.);
1921 QCOMPARE(object->property("aliasProperty").toReal(), 288.);
1922
1923 // Revert
1924 object->setProperty(name: "state", value: QString(""));
1925
1926 QVERIFY(QQmlPropertyPrivate::binding(realProperty) != nullptr);
1927 QVERIFY(QQmlPropertyPrivate::binding(aliasProperty) != nullptr);
1928 QCOMPARE(QQmlPropertyPrivate::binding(realProperty),
1929 QQmlPropertyPrivate::binding(aliasProperty));
1930
1931 QCOMPARE(realPropertyObject->property("realProperty").toReal(), 20.);
1932 QCOMPARE(object->property("aliasProperty").toReal(), 20.);
1933
1934 object->setProperty(name: "test2", value: 3);
1935
1936 QCOMPARE(realPropertyObject->property("realProperty").toReal(), 20.);
1937 QCOMPARE(object->property("aliasProperty").toReal(), 20.);
1938
1939 delete object;
1940}
1941
1942void tst_qqmlproperty::copy()
1943{
1944 PropertyObject object;
1945
1946 QScopedPointer<QQmlProperty> property(
1947 new QQmlProperty(&object, QLatin1String("defaultProperty")));
1948 QCOMPARE(property->name(), QString("defaultProperty"));
1949 QCOMPARE(property->read(), QVariant(10));
1950 QCOMPARE(property->type(), QQmlProperty::Property);
1951 QCOMPARE(property->propertyTypeCategory(), QQmlProperty::Normal);
1952 QCOMPARE(property->propertyType(), (int)QVariant::Int);
1953
1954 QQmlProperty p1(*property);
1955 QCOMPARE(p1.name(), QString("defaultProperty"));
1956 QCOMPARE(p1.read(), QVariant(10));
1957 QCOMPARE(p1.type(), QQmlProperty::Property);
1958 QCOMPARE(p1.propertyTypeCategory(), QQmlProperty::Normal);
1959 QCOMPARE(p1.propertyType(), (int)QVariant::Int);
1960
1961 QQmlProperty p2(&object, QLatin1String("url"));
1962 QCOMPARE(p2.name(), QString("url"));
1963 p2 = *property;
1964 QCOMPARE(p2.name(), QString("defaultProperty"));
1965 QCOMPARE(p2.read(), QVariant(10));
1966 QCOMPARE(p2.type(), QQmlProperty::Property);
1967 QCOMPARE(p2.propertyTypeCategory(), QQmlProperty::Normal);
1968 QCOMPARE(p2.propertyType(), (int)QVariant::Int);
1969
1970 property.reset();
1971
1972 QCOMPARE(p1.name(), QString("defaultProperty"));
1973 QCOMPARE(p1.read(), QVariant(10));
1974 QCOMPARE(p1.type(), QQmlProperty::Property);
1975 QCOMPARE(p1.propertyTypeCategory(), QQmlProperty::Normal);
1976 QCOMPARE(p1.propertyType(), (int)QVariant::Int);
1977
1978 QCOMPARE(p2.name(), QString("defaultProperty"));
1979 QCOMPARE(p2.read(), QVariant(10));
1980 QCOMPARE(p2.type(), QQmlProperty::Property);
1981 QCOMPARE(p2.propertyTypeCategory(), QQmlProperty::Normal);
1982 QCOMPARE(p2.propertyType(), (int)QVariant::Int);
1983
1984 p1 = QQmlProperty();
1985 QQmlPropertyPrivate *p2d = QQmlPropertyPrivate::get(p: p2);
1986 QCOMPARE(p2d->count(), 1);
1987
1988 // Use a pointer to avoid compiler warning about self-assignment.
1989 QQmlProperty *p2p = &p2;
1990 *p2p = p2;
1991
1992 QCOMPARE(p2d->count(), 1);
1993}
1994
1995void tst_qqmlproperty::noContext()
1996{
1997 QQmlComponent compA(&engine, testFileUrl(fileName: "NoContextTypeA.qml"));
1998 QQmlComponent compB(&engine, testFileUrl(fileName: "NoContextTypeB.qml"));
1999
2000 QObject *a = compA.create();
2001 QVERIFY(a != nullptr);
2002 QObject *b = compB.create();
2003 QVERIFY(b != nullptr);
2004
2005 QVERIFY(QQmlProperty::write(b, "myTypeA", QVariant::fromValue(a), &engine));
2006
2007 delete a;
2008 delete b;
2009}
2010
2011void tst_qqmlproperty::assignEmptyVariantMap()
2012{
2013 PropertyObject o;
2014
2015 QVariantMap map;
2016 map.insert(key: "key", value: "value");
2017 o.setVariantMap(map);
2018 QCOMPARE(o.variantMap().count(), 1);
2019 QCOMPARE(o.variantMap().isEmpty(), false);
2020
2021
2022 QQmlComponent component(&engine, testFileUrl(fileName: "assignEmptyVariantMap.qml"));
2023 QObject *obj = component.createWithInitialProperties(initialProperties: {{"o", QVariant::fromValue(value: &o)}});
2024 QVERIFY(obj);
2025
2026 QCOMPARE(o.variantMap().count(), 0);
2027 QCOMPARE(o.variantMap().isEmpty(), true);
2028
2029 delete obj;
2030}
2031
2032void tst_qqmlproperty::warnOnInvalidBinding()
2033{
2034 QUrl testUrl(testFileUrl(fileName: "invalidBinding.qml"));
2035 QString expectedWarning;
2036
2037 // V4 error message for property-to-property binding
2038 expectedWarning = testUrl.toString() + QString::fromLatin1(str: ":6:5: Unable to assign QQuickText to QQuickRectangle");
2039 QTest::ignoreMessage(type: QtWarningMsg, message: expectedWarning.toLatin1().constData());
2040
2041 // V8 error message for function-to-property binding
2042 expectedWarning = testUrl.toString() + QString::fromLatin1(str: ":7:5: Unable to assign QQuickText to QQuickRectangle");
2043 QTest::ignoreMessage(type: QtWarningMsg, message: expectedWarning.toLatin1().constData());
2044
2045#if QT_CONFIG(regularexpression)
2046 // V8 error message for invalid binding to anchor
2047 const QRegularExpression warning(
2048 "^" + testUrl.toString()
2049 + ":14:9: Unable to assign QQuickItem_QML_\\d+ to QQuickAnchorLine$");
2050 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: warning);
2051#endif
2052
2053 QQmlComponent component(&engine, testUrl);
2054 QObject *obj = component.create();
2055 QVERIFY(obj);
2056 delete obj;
2057}
2058
2059void tst_qqmlproperty::deeplyNestedObject()
2060{
2061 PropertyObject o;
2062 QQmlProperty p(&o, "qmlObject.pointProperty.x");
2063 QCOMPARE(p.isValid(), true);
2064
2065 p.write(14);
2066 QCOMPARE(p.read(), QVariant(14));
2067}
2068
2069void tst_qqmlproperty::readOnlyDynamicProperties()
2070{
2071 QQmlComponent comp(&engine, testFileUrl(fileName: "readonlyPrimitiveVsVar.qml"));
2072 QObject *obj = comp.create();
2073 QVERIFY(obj != nullptr);
2074
2075 QVERIFY(!obj->metaObject()->property(obj->metaObject()->indexOfProperty("r_var")).isWritable());
2076 QVERIFY(!obj->metaObject()->property(obj->metaObject()->indexOfProperty("r_int")).isWritable());
2077 QVERIFY(obj->metaObject()->property(obj->metaObject()->indexOfProperty("w_var")).isWritable());
2078 QVERIFY(obj->metaObject()->property(obj->metaObject()->indexOfProperty("w_int")).isWritable());
2079
2080 delete obj;
2081}
2082
2083void tst_qqmlproperty::aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSystem()
2084{
2085 const QUrl url = testFileUrl(fileName: "aliasToIdWithMatchingQmlFileName.qml");
2086 QQmlEngine engine;
2087 QQmlComponent component(&engine, url);
2088 QScopedPointer<QObject> root(component.create());
2089
2090 QQmlProperty property(root.data(), "testType.objectName", QQmlEngine::contextForObject(root.data()));
2091 QVERIFY(property.isValid());
2092}
2093
2094// QTBUG-77027
2095void tst_qqmlproperty::nullPropertyBinding()
2096{
2097 const QUrl url = testFileUrl(fileName: "nullPropertyBinding.qml");
2098 QQmlEngine engine;
2099 QQmlComponent component(&engine, url);
2100 QScopedPointer<QObject> root(component.create());
2101 QVERIFY(root);
2102 QTest::ignoreMessage(type: QtMsgType::QtInfoMsg, message: "undefined");
2103 QMetaObject::invokeMethod(obj: root.get(), member: "tog");
2104 QTest::ignoreMessage(type: QtMsgType::QtInfoMsg, message: "defined");
2105 QMetaObject::invokeMethod(obj: root.get(), member: "tog");
2106 QTest::ignoreMessage(type: QtMsgType::QtInfoMsg, message: "undefined");
2107 QMetaObject::invokeMethod(obj: root.get(), member: "tog");
2108}
2109
2110void tst_qqmlproperty::interfaceBinding()
2111{
2112 qmlRegisterInterface<Interface>(typeName: "Interface");
2113 qmlRegisterType<A>(uri: "io.qt.bugreports", versionMajor: 1, versionMinor: 0, qmlName: "A");
2114 qmlRegisterType<B>(uri: "io.qt.bugreports", versionMajor: 1, versionMinor: 0, qmlName: "B");
2115 qmlRegisterType<C>(uri: "io.qt.bugreports", versionMajor: 1, versionMinor: 0, qmlName: "C");
2116 qmlRegisterType<InterfaceConsumer>(uri: "io.qt.bugreports", versionMajor: 1, versionMinor: 0, qmlName: "InterfaceConsumer");
2117
2118 const QVector<QUrl> urls = {
2119 testFileUrl(fileName: "interfaceBinding.qml"),
2120 testFileUrl(fileName: "interfaceBinding2.qml")
2121 };
2122
2123 for (const QUrl &url : urls) {
2124 QQmlEngine engine;
2125 QQmlComponent component(&engine, url);
2126 QScopedPointer<QObject> root(component.create());
2127 QVERIFY(root);
2128 QCOMPARE(root->findChild<QObject*>("a1")->property("testValue").toInt(), 42);
2129 QCOMPARE(root->findChild<QObject*>("a2")->property("testValue").toInt(), 43);
2130 QCOMPARE(root->findChild<QObject*>("a3")->property("testValue").toInt(), 44);
2131 }
2132}
2133
2134void tst_qqmlproperty::floatToStringPrecision_data()
2135{
2136 QTest::addColumn<QString>(name: "propertyName");
2137 QTest::addColumn<double>(name: "number");
2138 QTest::addColumn<QString>(name: "qtString");
2139 QTest::addColumn<QString>(name: "jsString");
2140
2141 QTest::newRow(dataTag: "3.4") << "a" << 3.4 << "3.4" << "3.4";
2142 QTest::newRow(dataTag: "0.035003945") << "b" << 0.035003945 << "0.035003945" << "0.035003945";
2143 QTest::newRow(dataTag: "0.0000012345") << "c" << 0.0000012345 << "1.2345e-06" << "0.0000012345";
2144 QTest::newRow(dataTag: "0.00000012345") << "d" << 0.00000012345 << "1.2345e-07" << "1.2345e-7";
2145 QTest::newRow(dataTag: "1e20") << "e" << 1e20 << "1e+20" << "100000000000000000000";
2146 QTest::newRow(dataTag: "1e21") << "f" << 1e21 << "1e+21" << "1e+21";
2147}
2148
2149void tst_qqmlproperty::floatToStringPrecision()
2150{
2151 QQmlComponent comp(&engine, testFileUrl(fileName: "floatToStringPrecision.qml"));
2152 QScopedPointer<QObject> obj(comp.create());
2153 QVERIFY(obj != nullptr);
2154
2155 QFETCH(QString, propertyName);
2156 QFETCH(double, number);
2157 QFETCH(QString, qtString);
2158 QFETCH(QString, jsString);
2159
2160 QByteArray name = propertyName.toLatin1();
2161 QCOMPARE(obj->property(name).toDouble(), number);
2162 QCOMPARE(obj->property(name).toString(), qtString);
2163
2164 QByteArray name1 = (propertyName + QLatin1Char('1')).toLatin1();
2165 QCOMPARE(obj->property(name1).toDouble(), number);
2166 QCOMPARE(obj->property(name1).toString(), qtString);
2167
2168 QByteArray name2 = (propertyName + QLatin1Char('2')).toLatin1();
2169 QCOMPARE(obj->property(name2).toDouble(), number);
2170 QCOMPARE(obj->property(name2).toString(), jsString);
2171}
2172
2173void tst_qqmlproperty::initTestCase()
2174{
2175 QQmlDataTest::initTestCase();
2176 qmlRegisterType<MyQmlObject>(uri: "Test",versionMajor: 1,versionMinor: 0,qmlName: "MyQmlObject");
2177 qmlRegisterType<PropertyObject>(uri: "Test",versionMajor: 1,versionMinor: 0,qmlName: "PropertyObject");
2178 qmlRegisterType<MyContainer>(uri: "Test",versionMajor: 1,versionMinor: 0,qmlName: "MyContainer");
2179}
2180
2181// QTBUG-60908
2182void tst_qqmlproperty::bindingToAlias()
2183{
2184 QQmlEngine engine;
2185 QQmlComponent component(&engine, testFileUrl(fileName: "aliasToBinding.qml"));
2186 QScopedPointer<QObject> o(component.create());
2187 QVERIFY(!o.isNull());
2188}
2189
2190void tst_qqmlproperty::nestedQQmlPropertyMap()
2191{
2192 QQmlPropertyMap mainPropertyMap;
2193 QQmlPropertyMap nestedPropertyMap;
2194 QQmlPropertyMap deeplyNestedPropertyMap;
2195
2196 mainPropertyMap.insert(key: "nesting1", value: QVariant::fromValue(value: &nestedPropertyMap));
2197 nestedPropertyMap.insert(key: "value", value: 42);
2198 nestedPropertyMap.insert(key: "nesting2", value: QVariant::fromValue(value: &deeplyNestedPropertyMap));
2199 deeplyNestedPropertyMap.insert(key: "value", value: "success");
2200
2201 QQmlProperty value{&mainPropertyMap, "nesting1.value"};
2202 QCOMPARE(value.read().toInt(), 42);
2203
2204 QQmlProperty success{&mainPropertyMap, "nesting1.nesting2.value"};
2205 QCOMPARE(success.read().toString(), QLatin1String("success"));
2206}
2207
2208void tst_qqmlproperty::underscorePropertyChangeHandler()
2209{
2210 QQmlEngine engine;
2211 QQmlComponent component(&engine);
2212 component.setData(R"(
2213 import QtQuick 2.12
2214
2215 Item {
2216 property int __withUnderScore
2217 }
2218 )", baseUrl: QUrl::fromLocalFile(localfile: "."));
2219 QScopedPointer<QObject> root { component.create() };
2220 QVERIFY(root);
2221 QQmlProperty changeHandler(root.get(), "on__WithUnderScoreChanged");
2222 QVERIFY(changeHandler.isValid());
2223 QVERIFY(changeHandler.isSignalProperty());
2224}
2225
2226void tst_qqmlproperty::signalExpressionWithoutObject()
2227{
2228 QQmlProperty invalid;
2229 QQmlPropertyPrivate::setSignalExpression(that: invalid, nullptr);
2230 QQmlBoundSignalExpression *expr = QQmlPropertyPrivate::signalExpression(that: invalid);
2231 QVERIFY(!expr);
2232}
2233
2234QTEST_MAIN(tst_qqmlproperty)
2235
2236#include "tst_qqmlproperty.moc"
2237

source code of qtdeclarative/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp