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 <qtest.h>
30#include <QQmlEngine>
31#include <QQmlComponent>
32#include <QDebug>
33#include <QJSValueIterator>
34#include <private/qquickvaluetypes_p.h>
35#include <private/qqmlglobal_p.h>
36#include <private/qv4engine_p.h>
37#include <private/qv4variantobject_p.h>
38#include "../../shared/util.h"
39#include "testtypes.h"
40
41QT_BEGIN_NAMESPACE
42extern int qt_defaultDpi(void);
43QT_END_NAMESPACE
44
45class tst_qqmlvaluetypes : public QQmlDataTest
46{
47 Q_OBJECT
48public:
49 tst_qqmlvaluetypes() {}
50
51private slots:
52 void initTestCase();
53
54 void point();
55 void pointf();
56 void size();
57 void sizef();
58 void sizereadonly();
59 void rect();
60 void rectf();
61 void vector2d();
62 void vector3d();
63 void vector4d();
64 void quaternion();
65 void matrix4x4();
66 void font();
67 void color();
68 void variant();
69 void locale();
70 void qmlproperty();
71
72 void bindingAssignment();
73 void bindingRead();
74 void staticAssignment();
75 void scriptAccess();
76 void autoBindingRemoval();
77 void valueSources();
78 void valueInterceptors();
79 void bindingConflict();
80 void deletedObject();
81 void bindingVariantCopy();
82 void scriptVariantCopy();
83 void enums();
84 void conflictingBindings();
85 void returnValues();
86 void varAssignment();
87 void bindingsSpliceCorrectly();
88 void nonValueTypeComparison();
89 void initializeByWrite();
90 void groupedInterceptors();
91 void groupedInterceptors_data();
92 void customValueType();
93 void customValueTypeInQml();
94 void gadgetInheritance();
95 void gadgetTemplateInheritance();
96 void sequences();
97 void toStringConversion();
98 void enumerableProperties();
99 void enumProperties();
100 void scarceTypes();
101 void nonValueTypes();
102
103private:
104 QQmlEngine engine;
105};
106
107void tst_qqmlvaluetypes::initTestCase()
108{
109 QQmlDataTest::initTestCase();
110 registerTypes();
111}
112
113void tst_qqmlvaluetypes::point()
114{
115 {
116 QQmlComponent component(&engine, testFileUrl(fileName: "point_read.qml"));
117 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
118 QVERIFY(object != nullptr);
119
120 QCOMPARE(object->property("p_x").toInt(), 10);
121 QCOMPARE(object->property("p_y").toInt(), 4);
122 QCOMPARE(object->property("copy"), QVariant(QPoint(10, 4)));
123
124 delete object;
125 }
126
127 {
128 QQmlComponent component(&engine, testFileUrl(fileName: "point_write.qml"));
129 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
130 QVERIFY(object != nullptr);
131
132 QCOMPARE(object->point(), QPoint(11, 12));
133
134 delete object;
135 }
136
137 {
138 QQmlComponent component(&engine, testFileUrl(fileName: "point_compare.qml"));
139 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
140 QVERIFY(object != nullptr);
141
142 QString tostring = QLatin1String("QPoint(10, 4)");
143 QCOMPARE(object->property("tostring").toString(), tostring);
144 QCOMPARE(object->property("equalsString").toBool(), true);
145 QCOMPARE(object->property("equalsColor").toBool(), false);
146 QCOMPARE(object->property("equalsVector3d").toBool(), false);
147 QCOMPARE(object->property("equalsSize").toBool(), false);
148 QCOMPARE(object->property("equalsPoint").toBool(), true);
149 QCOMPARE(object->property("equalsRect").toBool(), false);
150 QCOMPARE(object->property("equalsSelf").toBool(), true);
151 QCOMPARE(object->property("equalsOther").toBool(), false);
152 QCOMPARE(object->property("pointEqualsPointf").toBool(), true);
153
154 delete object;
155 }
156}
157
158void tst_qqmlvaluetypes::pointf()
159{
160 {
161 QQmlComponent component(&engine, testFileUrl(fileName: "pointf_read.qml"));
162 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
163 QVERIFY(object != nullptr);
164
165 QCOMPARE(float(object->property("p_x").toDouble()), float(11.3));
166 QCOMPARE(float(object->property("p_y").toDouble()), float(-10.9));
167 QCOMPARE(object->property("copy"), QVariant(QPointF(11.3, -10.9)));
168
169 delete object;
170 }
171
172 {
173 QQmlComponent component(&engine, testFileUrl(fileName: "pointf_write.qml"));
174 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
175 QVERIFY(object != nullptr);
176
177 QCOMPARE(object->pointf(), QPointF(6.8, 9.3));
178
179 delete object;
180 }
181
182 {
183 QQmlComponent component(&engine, testFileUrl(fileName: "pointf_compare.qml"));
184 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
185 QVERIFY(object != nullptr);
186
187 QString tostring = QLatin1String("QPointF(11.3, -10.9)");
188 QCOMPARE(object->property("tostring").toString(), tostring);
189 QCOMPARE(object->property("equalsString").toBool(), true);
190 QCOMPARE(object->property("equalsColor").toBool(), false);
191 QCOMPARE(object->property("equalsVector3d").toBool(), false);
192 QCOMPARE(object->property("equalsSize").toBool(), false);
193 QCOMPARE(object->property("equalsPoint").toBool(), true);
194 QCOMPARE(object->property("equalsRect").toBool(), false);
195 QCOMPARE(object->property("equalsSelf").toBool(), true);
196 QCOMPARE(object->property("equalsOther").toBool(), false);
197 QCOMPARE(object->property("pointfEqualsPoint").toBool(), true);
198
199 delete object;
200 }
201}
202
203void tst_qqmlvaluetypes::size()
204{
205 {
206 QQmlComponent component(&engine, testFileUrl(fileName: "size_read.qml"));
207 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
208 QVERIFY(object != nullptr);
209
210 QCOMPARE(object->property("s_width").toInt(), 1912);
211 QCOMPARE(object->property("s_height").toInt(), 1913);
212 QCOMPARE(object->property("copy"), QVariant(QSize(1912, 1913)));
213
214 delete object;
215 }
216
217 {
218 QQmlComponent component(&engine, testFileUrl(fileName: "size_write.qml"));
219 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
220 QVERIFY(object != nullptr);
221
222 QCOMPARE(object->size(), QSize(13, 88));
223
224 delete object;
225 }
226
227 {
228 QQmlComponent component(&engine, testFileUrl(fileName: "size_compare.qml"));
229 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
230 QVERIFY(object != nullptr);
231
232 QString tostring = QLatin1String("QSize(1912, 1913)");
233 QCOMPARE(object->property("tostring").toString(), tostring);
234 QCOMPARE(object->property("equalsString").toBool(), true);
235 QCOMPARE(object->property("equalsColor").toBool(), false);
236 QCOMPARE(object->property("equalsVector3d").toBool(), false);
237 QCOMPARE(object->property("equalsSize").toBool(), true);
238 QCOMPARE(object->property("equalsPoint").toBool(), false);
239 QCOMPARE(object->property("equalsRect").toBool(), false);
240 QCOMPARE(object->property("equalsSelf").toBool(), true);
241 QCOMPARE(object->property("equalsOther").toBool(), false);
242 QCOMPARE(object->property("sizeEqualsSizef").toBool(), true);
243
244 delete object;
245 }
246}
247
248void tst_qqmlvaluetypes::sizef()
249{
250 {
251 QQmlComponent component(&engine, testFileUrl(fileName: "sizef_read.qml"));
252 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
253 QVERIFY(object != nullptr);
254
255 QCOMPARE(float(object->property("s_width").toDouble()), float(0.1));
256 QCOMPARE(float(object->property("s_height").toDouble()), float(100923.2));
257 QCOMPARE(object->property("copy"), QVariant(QSizeF(0.1, 100923.2)));
258
259 delete object;
260 }
261
262 {
263 QQmlComponent component(&engine, testFileUrl(fileName: "sizef_write.qml"));
264 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
265 QVERIFY(object != nullptr);
266
267 QCOMPARE(object->sizef(), QSizeF(44.3, 92.8));
268
269 delete object;
270 }
271
272 {
273 QQmlComponent component(&engine, testFileUrl(fileName: "sizef_compare.qml"));
274 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
275 QVERIFY(object != nullptr);
276
277 QString tostring = QLatin1String("QSizeF(0.1, 100923)");
278 QCOMPARE(object->property("tostring").toString(), tostring);
279 QCOMPARE(object->property("equalsString").toBool(), true);
280 QCOMPARE(object->property("equalsColor").toBool(), false);
281 QCOMPARE(object->property("equalsVector3d").toBool(), false);
282 QCOMPARE(object->property("equalsSize").toBool(), true);
283 QCOMPARE(object->property("equalsPoint").toBool(), false);
284 QCOMPARE(object->property("equalsRect").toBool(), false);
285 QCOMPARE(object->property("equalsSelf").toBool(), true);
286 QCOMPARE(object->property("equalsOther").toBool(), false);
287 QCOMPARE(object->property("sizefEqualsSize").toBool(), true);
288
289 delete object;
290 }
291}
292
293void tst_qqmlvaluetypes::variant()
294{
295 {
296 QQmlComponent component(&engine, testFileUrl(fileName: "variant_read.qml"));
297 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
298 QVERIFY(object != nullptr);
299
300 QCOMPARE(float(object->property("s_width").toDouble()), float(0.1));
301 QCOMPARE(float(object->property("s_height").toDouble()), float(100923.2));
302 QCOMPARE(object->property("copy"), QVariant(QSizeF(0.1, 100923.2)));
303
304 delete object;
305 }
306
307 {
308 QQmlComponent component(&engine, testFileUrl(fileName: "variant_write.1.qml"));
309 QObject *object = component.create();
310 QVERIFY(object != nullptr);
311 QVERIFY(object->property("complete").toBool());
312 QVERIFY(object->property("success").toBool());
313 delete object;
314 }
315
316 {
317 QQmlComponent component(&engine, testFileUrl(fileName: "variant_write.2.qml"));
318 QObject *object = component.create();
319 QVERIFY(object != nullptr);
320 QVERIFY(object->property("complete").toBool());
321 QVERIFY(object->property("success").toBool());
322 delete object;
323 }
324}
325
326void tst_qqmlvaluetypes::locale()
327{
328 {
329 QQmlComponent component(&engine, testFileUrl(fileName: "locale_read.qml"));
330 QScopedPointer<QObject> object(component.create());
331 QVERIFY(!object.isNull());
332
333#if QT_CONFIG(im)
334 QVERIFY(QQml_guiProvider()->inputMethod());
335 QInputMethod *inputMethod = qobject_cast<QInputMethod*>(object: QQml_guiProvider()->inputMethod());
336 QLocale locale = inputMethod->locale();
337
338 QCOMPARE(object->property("amText").toString(), locale.amText());
339 QCOMPARE(object->property("decimalPoint").toString().at(0), locale.decimalPoint());
340 QCOMPARE(object->property("exponential").toString().at(0), locale.exponential());
341 // Sunday is 0 in JavaScript.
342 QCOMPARE(object->property("firstDayOfWeek").toInt(), int(locale.firstDayOfWeek() == Qt::Sunday ? 0 : locale.firstDayOfWeek()));
343 QCOMPARE(object->property("groupSeparator").toString().at(0), locale.groupSeparator());
344 QCOMPARE(object->property("measurementSystem").toInt(), int(locale.measurementSystem()));
345 QCOMPARE(object->property("name").toString(), locale.name());
346 QCOMPARE(object->property("nativeCountryName").toString(), locale.nativeCountryName());
347 QCOMPARE(object->property("nativeLanguageName").toString(), locale.nativeLanguageName());
348 QCOMPARE(object->property("negativeSign").toString().at(0), locale.negativeSign());
349 QCOMPARE(object->property("percent").toString().at(0), locale.percent());
350 QCOMPARE(object->property("pmText").toString(), locale.pmText());
351 QCOMPARE(object->property("positiveSign").toString().at(0), locale.positiveSign());
352 QCOMPARE(object->property("textDirection").toInt(), int(locale.textDirection()));
353 QCOMPARE(object->property("uiLanguages").toStringList(), locale.uiLanguages());
354 QList<Qt::DayOfWeek> weekDays;
355 foreach (const QVariant &weekDay, object->property("weekDays").toList()) {
356 weekDays.append(t: Qt::DayOfWeek(weekDay.toInt()));
357 }
358 QCOMPARE(weekDays, locale.weekdays());
359 QCOMPARE(object->property("zeroDigit").toString().at(0), locale.zeroDigit());
360#endif // im
361 }
362}
363
364void tst_qqmlvaluetypes::qmlproperty()
365{
366 QQmlComponent component(&engine, testFileUrl(fileName: "qmlproperty_read.qml"));
367 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
368 QVERIFY(object != nullptr);
369
370 QCOMPARE(object->property("colorPropertyObject").value<QObject *>(), object);
371 QCOMPARE(object->property("colorPropertyName").toString(), "color");
372 QCOMPARE(object->property("invalidPropertyObject").value<QObject *>(), nullptr);
373 QCOMPARE(object->property("invalidPropertyName").toString(), "");
374
375 delete object;
376}
377
378void tst_qqmlvaluetypes::sizereadonly()
379{
380 {
381 QQmlComponent component(&engine, testFileUrl(fileName: "sizereadonly_read.qml"));
382 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
383 QVERIFY(object != nullptr);
384
385 QCOMPARE(object->property("s_width").toInt(), 1912);
386 QCOMPARE(object->property("s_height").toInt(), 1913);
387 QCOMPARE(object->property("copy"), QVariant(QSize(1912, 1913)));
388
389 delete object;
390 }
391
392 {
393 QQmlComponent component(&engine, testFileUrl(fileName: "sizereadonly_writeerror.qml"));
394 QVERIFY(component.isError());
395 QCOMPARE(component.errors().at(0).description(), QLatin1String("Invalid property assignment: \"sizereadonly\" is a read-only property"));
396 }
397
398 {
399 QQmlComponent component(&engine, testFileUrl(fileName: "sizereadonly_writeerror2.qml"));
400 QVERIFY(component.isError());
401 QCOMPARE(component.errors().at(0).description(), QLatin1String("Invalid property assignment: \"sizereadonly\" is a read-only property"));
402 }
403
404 {
405 QQmlComponent component(&engine, testFileUrl(fileName: "sizereadonly_writeerror3.qml"));
406 QVERIFY(component.isError());
407 QCOMPARE(component.errors().at(0).description(), QLatin1String("Invalid property assignment: \"sizereadonly\" is a read-only property"));
408 }
409
410 {
411 QQmlComponent component(&engine, testFileUrl(fileName: "sizereadonly_writeerror4.qml"));
412
413 QObject *object = component.create();
414 QVERIFY(object);
415
416 QCOMPARE(object->property("sizereadonly").toSize(), QSize(1912, 1913));
417
418 delete object;
419 }
420}
421
422void tst_qqmlvaluetypes::rect()
423{
424 {
425 QQmlComponent component(&engine, testFileUrl(fileName: "rect_read.qml"));
426 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
427 QVERIFY(object != nullptr);
428
429 QCOMPARE(object->property("r_x").toInt(), 2);
430 QCOMPARE(object->property("r_y").toInt(), 3);
431 QCOMPARE(object->property("r_width").toInt(), 109);
432 QCOMPARE(object->property("r_height").toInt(), 102);
433 QCOMPARE(object->property("r_left").toInt(), 2);
434 QCOMPARE(object->property("r_right").toInt(), 110);
435 QCOMPARE(object->property("r_top").toInt(), 3);
436 QCOMPARE(object->property("r_bottom").toInt(), 104);
437 QCOMPARE(object->property("copy"), QVariant(QRect(2, 3, 109, 102)));
438
439 delete object;
440 }
441
442 {
443 QQmlComponent component(&engine, testFileUrl(fileName: "rect_write.qml"));
444 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
445 QVERIFY(object != nullptr);
446
447 QCOMPARE(object->rect(), QRect(1234, 7, 56, 63));
448
449 delete object;
450 }
451
452 {
453 QQmlComponent component(&engine, testFileUrl(fileName: "rect_compare.qml"));
454 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
455 QVERIFY(object != nullptr);
456
457 QString tostring = QLatin1String("QRect(2, 3, 109, 102)");
458 QCOMPARE(object->property("tostring").toString(), tostring);
459 QCOMPARE(object->property("equalsString").toBool(), true);
460 QCOMPARE(object->property("equalsColor").toBool(), false);
461 QCOMPARE(object->property("equalsVector3d").toBool(), false);
462 QCOMPARE(object->property("equalsSize").toBool(), false);
463 QCOMPARE(object->property("equalsPoint").toBool(), false);
464 QCOMPARE(object->property("equalsRect").toBool(), true);
465 QCOMPARE(object->property("equalsSelf").toBool(), true);
466 QCOMPARE(object->property("equalsOther").toBool(), false);
467 QCOMPARE(object->property("rectEqualsRectf").toBool(), true);
468
469 delete object;
470 }
471}
472
473void tst_qqmlvaluetypes::rectf()
474{
475 {
476 QQmlComponent component(&engine, testFileUrl(fileName: "rectf_read.qml"));
477 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
478 QVERIFY(object != nullptr);
479
480 QCOMPARE(float(object->property("r_x").toDouble()), float(103.8));
481 QCOMPARE(float(object->property("r_y").toDouble()), float(99.2));
482 QCOMPARE(float(object->property("r_width").toDouble()), float(88.1));
483 QCOMPARE(float(object->property("r_height").toDouble()), float(77.6));
484 QCOMPARE(float(object->property("r_left").toDouble()), float(103.8));
485 QCOMPARE(float(object->property("r_right").toDouble()), float(191.9));
486 QCOMPARE(float(object->property("r_top").toDouble()), float(99.2));
487 QCOMPARE(float(object->property("r_bottom").toDouble()), float(176.8));
488 QCOMPARE(object->property("copy"), QVariant(QRectF(103.8, 99.2, 88.1, 77.6)));
489
490 delete object;
491 }
492
493 {
494 QQmlComponent component(&engine, testFileUrl(fileName: "rectf_write.qml"));
495 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
496 QVERIFY(object != nullptr);
497
498 QCOMPARE(object->rectf(), QRectF(70.1, -113.2, 80924.8, 99.2));
499
500 delete object;
501 }
502
503 {
504 QQmlComponent component(&engine, testFileUrl(fileName: "rectf_compare.qml"));
505 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
506 QVERIFY(object != nullptr);
507
508 QString tostring = QLatin1String("QRectF(103.8, 99.2, 88.1, 77.6)");
509 QCOMPARE(object->property("tostring").toString(), tostring);
510 QCOMPARE(object->property("equalsString").toBool(), true);
511 QCOMPARE(object->property("equalsColor").toBool(), false);
512 QCOMPARE(object->property("equalsVector3d").toBool(), false);
513 QCOMPARE(object->property("equalsSize").toBool(), false);
514 QCOMPARE(object->property("equalsPoint").toBool(), false);
515 QCOMPARE(object->property("equalsRect").toBool(), true);
516 QCOMPARE(object->property("equalsSelf").toBool(), true);
517 QCOMPARE(object->property("equalsOther").toBool(), false);
518 QCOMPARE(object->property("rectfEqualsRect").toBool(), true);
519
520 delete object;
521 }
522}
523
524void tst_qqmlvaluetypes::vector2d()
525{
526 {
527 QQmlComponent component(&engine, testFileUrl(fileName: "vector2d_read.qml"));
528 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
529 QVERIFY(object != nullptr);
530
531 QCOMPARE((float)object->property("v_x").toDouble(), (float)32.88);
532 QCOMPARE((float)object->property("v_y").toDouble(), (float)1.3);
533 QCOMPARE(object->property("copy"), QVariant(QVector2D(32.88f, 1.3f)));
534
535 delete object;
536 }
537
538 {
539 QQmlComponent component(&engine, testFileUrl(fileName: "vector2d_write.qml"));
540 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
541 QVERIFY(object != nullptr);
542
543 QCOMPARE(object->vector2(), QVector2D(-0.3f, -12.9f));
544
545 delete object;
546 }
547
548 {
549 QQmlComponent component(&engine, testFileUrl(fileName: "vector2d_compare.qml"));
550 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
551 QVERIFY(object != nullptr);
552
553 QString tostring = QLatin1String("QVector2D(32.88, 1.3)");
554 QCOMPARE(object->property("tostring").toString(), tostring);
555 QCOMPARE(object->property("equalsString").toBool(), true);
556 QCOMPARE(object->property("equalsColor").toBool(), false);
557 QCOMPARE(object->property("equalsVector3d").toBool(), false);
558 QCOMPARE(object->property("equalsSize").toBool(), false);
559 QCOMPARE(object->property("equalsPoint").toBool(), false);
560 QCOMPARE(object->property("equalsRect").toBool(), false);
561 QCOMPARE(object->property("equalsSelf").toBool(), true);
562
563 delete object;
564 }
565
566 {
567 QQmlComponent component(&engine, testFileUrl(fileName: "vector2d_invokables.qml"));
568 QObject *object = component.create();
569 QVERIFY(object != nullptr);
570 QVERIFY(object->property("success").toBool());
571 delete object;
572 }
573}
574
575void tst_qqmlvaluetypes::vector3d()
576{
577 {
578 QQmlComponent component(&engine, testFileUrl(fileName: "vector3d_read.qml"));
579 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
580 QVERIFY(object != nullptr);
581
582 QCOMPARE((float)object->property("v_x").toDouble(), (float)23.88);
583 QCOMPARE((float)object->property("v_y").toDouble(), (float)3.1);
584 QCOMPARE((float)object->property("v_z").toDouble(), (float)4.3);
585 QCOMPARE(object->property("copy"), QVariant(QVector3D(23.88f, 3.1f, 4.3f)));
586
587 delete object;
588 }
589
590 {
591 QQmlComponent component(&engine, testFileUrl(fileName: "vector3d_write.qml"));
592 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
593 QVERIFY(object != nullptr);
594
595 QCOMPARE(object->vector(), QVector3D(-0.3f, -12.9f, 907.4f));
596
597 delete object;
598 }
599
600 {
601 QQmlComponent component(&engine, testFileUrl(fileName: "vector3d_compare.qml"));
602 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
603 QVERIFY(object != nullptr);
604
605 QString tostring = QLatin1String("QVector3D(23.88, 3.1, 4.3)");
606 QCOMPARE(object->property("tostring").toString(), tostring);
607 QCOMPARE(object->property("equalsString").toBool(), true);
608 QCOMPARE(object->property("equalsColor").toBool(), false);
609 QCOMPARE(object->property("equalsVector3d").toBool(), true);
610 QCOMPARE(object->property("equalsSize").toBool(), false);
611 QCOMPARE(object->property("equalsPoint").toBool(), false);
612 QCOMPARE(object->property("equalsRect").toBool(), false);
613 QCOMPARE(object->property("equalsSelf").toBool(), true);
614 QCOMPARE(object->property("equalsOther").toBool(), false);
615
616 delete object;
617 }
618
619 {
620 QQmlComponent component(&engine, testFileUrl(fileName: "vector3d_invokables.qml"));
621 QObject *object = component.create();
622 QVERIFY(object != nullptr);
623 QVERIFY(object->property("success").toBool());
624 delete object;
625 }
626}
627
628void tst_qqmlvaluetypes::vector4d()
629{
630 {
631 QQmlComponent component(&engine, testFileUrl(fileName: "vector4d_read.qml"));
632 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
633 QVERIFY(object != nullptr);
634
635 QCOMPARE((float)object->property("v_x").toDouble(), (float)54.2);
636 QCOMPARE((float)object->property("v_y").toDouble(), (float)23.88);
637 QCOMPARE((float)object->property("v_z").toDouble(), (float)3.1);
638 QCOMPARE((float)object->property("v_w").toDouble(), (float)4.3);
639 QCOMPARE(object->property("copy"), QVariant(QVector4D(54.2f, 23.88f, 3.1f, 4.3f)));
640
641 delete object;
642 }
643
644 {
645 QQmlComponent component(&engine, testFileUrl(fileName: "vector4d_write.qml"));
646 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
647 QVERIFY(object != nullptr);
648
649 QCOMPARE(object->vector4(), QVector4D(-0.3f, -12.9f, 907.4f, 88.5f));
650
651 delete object;
652 }
653
654 {
655 QQmlComponent component(&engine, testFileUrl(fileName: "vector4d_compare.qml"));
656 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
657 QVERIFY(object != nullptr);
658
659 QString tostring = QLatin1String("QVector4D(54.2, 23.88, 3.1, 4.3)");
660 QCOMPARE(object->property("tostring").toString(), tostring);
661 QCOMPARE(object->property("equalsString").toBool(), true);
662 QCOMPARE(object->property("equalsColor").toBool(), false);
663 QCOMPARE(object->property("equalsVector3d").toBool(), false);
664 QCOMPARE(object->property("equalsSize").toBool(), false);
665 QCOMPARE(object->property("equalsPoint").toBool(), false);
666 QCOMPARE(object->property("equalsRect").toBool(), false);
667 QCOMPARE(object->property("equalsSelf").toBool(), true);
668
669 delete object;
670 }
671
672 {
673 QQmlComponent component(&engine, testFileUrl(fileName: "vector4d_invokables.qml"));
674 QObject *object = component.create();
675 QVERIFY(object != nullptr);
676 QVERIFY(object->property("success").toBool());
677 delete object;
678 }
679}
680
681void tst_qqmlvaluetypes::quaternion()
682{
683 {
684 QQmlComponent component(&engine, testFileUrl(fileName: "quaternion_read.qml"));
685 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
686 QVERIFY(object != nullptr);
687
688 QCOMPARE((float)object->property("v_scalar").toDouble(), (float)4.3);
689 QCOMPARE((float)object->property("v_x").toDouble(), (float)54.2);
690 QCOMPARE((float)object->property("v_y").toDouble(), (float)23.88);
691 QCOMPARE((float)object->property("v_z").toDouble(), (float)3.1);
692 QCOMPARE(object->property("copy"), QVariant(QQuaternion(4.3f, 54.2f, 23.88f, 3.1f)));
693
694 delete object;
695 }
696
697 {
698 QQmlComponent component(&engine, testFileUrl(fileName: "quaternion_write.qml"));
699 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
700 QVERIFY(object != nullptr);
701
702 QCOMPARE(object->quaternion(), QQuaternion(88.5f, -0.3f, -12.9f, 907.4f));
703
704 delete object;
705 }
706
707 {
708 QQmlComponent component(&engine, testFileUrl(fileName: "quaternion_compare.qml"));
709 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
710 QVERIFY(object != nullptr);
711
712 QString tostring = QLatin1String("QQuaternion(4.3, 54.2, 23.88, 3.1)");
713 QCOMPARE(object->property("tostring").toString(), tostring);
714 QCOMPARE(object->property("equalsString").toBool(), true);
715 QCOMPARE(object->property("equalsColor").toBool(), false);
716 QCOMPARE(object->property("equalsVector3d").toBool(), false);
717 QCOMPARE(object->property("equalsSize").toBool(), false);
718 QCOMPARE(object->property("equalsPoint").toBool(), false);
719 QCOMPARE(object->property("equalsRect").toBool(), false);
720 QCOMPARE(object->property("equalsSelf").toBool(), true);
721
722 delete object;
723 }
724}
725
726void tst_qqmlvaluetypes::matrix4x4()
727{
728 {
729 QQmlComponent component(&engine, testFileUrl(fileName: "matrix4x4_read.qml"));
730 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
731 QVERIFY(object != nullptr);
732
733 QCOMPARE((float)object->property("v_m11").toDouble(), (float)1);
734 QCOMPARE((float)object->property("v_m12").toDouble(), (float)2);
735 QCOMPARE((float)object->property("v_m13").toDouble(), (float)3);
736 QCOMPARE((float)object->property("v_m14").toDouble(), (float)4);
737 QCOMPARE((float)object->property("v_m21").toDouble(), (float)5);
738 QCOMPARE((float)object->property("v_m22").toDouble(), (float)6);
739 QCOMPARE((float)object->property("v_m23").toDouble(), (float)7);
740 QCOMPARE((float)object->property("v_m24").toDouble(), (float)8);
741 QCOMPARE((float)object->property("v_m31").toDouble(), (float)9);
742 QCOMPARE((float)object->property("v_m32").toDouble(), (float)10);
743 QCOMPARE((float)object->property("v_m33").toDouble(), (float)11);
744 QCOMPARE((float)object->property("v_m34").toDouble(), (float)12);
745 QCOMPARE((float)object->property("v_m41").toDouble(), (float)13);
746 QCOMPARE((float)object->property("v_m42").toDouble(), (float)14);
747 QCOMPARE((float)object->property("v_m43").toDouble(), (float)15);
748 QCOMPARE((float)object->property("v_m44").toDouble(), (float)16);
749 QCOMPARE(object->property("copy"),
750 QVariant(QMatrix4x4(1, 2, 3, 4,
751 5, 6, 7, 8,
752 9, 10, 11, 12,
753 13, 14, 15, 16)));
754
755 delete object;
756 }
757
758 {
759 QQmlComponent component(&engine, testFileUrl(fileName: "matrix4x4_write.qml"));
760 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
761 QVERIFY(object != nullptr);
762
763 QCOMPARE(object->matrix(), QMatrix4x4(11, 12, 13, 14,
764 21, 22, 23, 24,
765 31, 32, 33, 34,
766 41, 42, 43, 44));
767
768 delete object;
769 }
770
771 {
772 QQmlComponent component(&engine, testFileUrl(fileName: "matrix4x4_compare.qml"));
773 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
774 QVERIFY(object != nullptr);
775
776 QString tostring = QLatin1String("QMatrix4x4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)");
777 QCOMPARE(object->property("tostring").toString(), tostring);
778 QCOMPARE(object->property("equalsString").toBool(), true);
779 QCOMPARE(object->property("equalsColor").toBool(), false);
780 QCOMPARE(object->property("equalsVector3d").toBool(), false);
781 QCOMPARE(object->property("equalsSize").toBool(), false);
782 QCOMPARE(object->property("equalsPoint").toBool(), false);
783 QCOMPARE(object->property("equalsRect").toBool(), false);
784 QCOMPARE(object->property("equalsSelf").toBool(), true);
785
786 delete object;
787 }
788
789 {
790 QQmlComponent component(&engine, testFileUrl(fileName: "matrix4x4_invokables.qml"));
791 QObject *object = component.create();
792 QVERIFY(object != nullptr);
793 QCOMPARE(object->property("success").toBool(), true);
794 delete object;
795 }
796}
797
798void tst_qqmlvaluetypes::font()
799{
800 {
801 QQmlComponent component(&engine, testFileUrl(fileName: "font_read.qml"));
802 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
803 QVERIFY2(component.isReady(), qPrintable(component.errorString()));
804 QVERIFY(object != nullptr);
805
806 QCOMPARE(object->property("f_family").toString(), object->font().family());
807 QCOMPARE(object->property("f_bold").toBool(), object->font().bold());
808 QCOMPARE(object->property("f_weight").toInt(), object->font().weight());
809 QCOMPARE(object->property("f_italic").toBool(), object->font().italic());
810 QCOMPARE(object->property("f_underline").toBool(), object->font().underline());
811 QCOMPARE(object->property("f_overline").toBool(), object->font().overline());
812 QCOMPARE(object->property("f_strikeout").toBool(), object->font().strikeOut());
813
814 // If QFont::pixelSize() was set, QFont::pointSizeF() would return -1.
815 // If QFont::pointSizeF() was set, QFont::pixelSize() would return -1.
816 // QQuickFontValueType doesn't follow this semantic (if its -1 it calculates the value of
817 // the property from the other one)
818 double expectedPointSizeF = object->font().pointSizeF();
819 if (expectedPointSizeF == -1) expectedPointSizeF = object->font().pixelSize() * qreal(72.) / qreal(qt_defaultDpi());
820 int expectedPixelSize = object->font().pixelSize();
821 if (expectedPixelSize == -1) expectedPixelSize = int((object->font().pointSizeF() * qt_defaultDpi()) / qreal(72.));
822
823 QCOMPARE(object->property("f_pointSize").toDouble(), expectedPointSizeF);
824 QCOMPARE(object->property("f_pixelSize").toInt(), expectedPixelSize);
825
826 QCOMPARE(object->property("f_capitalization").toInt(), (int)object->font().capitalization());
827 QCOMPARE(object->property("f_letterSpacing").toDouble(), object->font().letterSpacing());
828 QCOMPARE(object->property("f_wordSpacing").toDouble(), object->font().wordSpacing());
829
830 QCOMPARE(object->property("copy"), QVariant(object->font()));
831
832 delete object;
833 }
834
835 {
836 QQmlComponent component(&engine, testFileUrl(fileName: "font_write.qml"));
837 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
838 QVERIFY(object != nullptr);
839
840 QFont font;
841 font.setFamily("Helvetica");
842 font.setBold(false);
843 font.setWeight(QFont::Normal);
844 font.setItalic(false);
845 font.setUnderline(false);
846 font.setStrikeOut(false);
847 font.setPointSize(15);
848 font.setCapitalization(QFont::AllLowercase);
849 font.setLetterSpacing(type: QFont::AbsoluteSpacing, spacing: 9.7);
850 font.setWordSpacing(11.2);
851
852 QFont f = object->font();
853 QCOMPARE(f.family(), font.family());
854 QCOMPARE(f.bold(), font.bold());
855 QCOMPARE(f.weight(), font.weight());
856 QCOMPARE(f.italic(), font.italic());
857 QCOMPARE(f.underline(), font.underline());
858 QCOMPARE(f.strikeOut(), font.strikeOut());
859 QCOMPARE(f.pointSize(), font.pointSize());
860 QCOMPARE(f.capitalization(), font.capitalization());
861 QCOMPARE(f.letterSpacing(), font.letterSpacing());
862 QCOMPARE(f.wordSpacing(), font.wordSpacing());
863
864 delete object;
865 }
866
867 // Test pixelSize
868 {
869 QQmlComponent component(&engine, testFileUrl(fileName: "font_write.2.qml"));
870 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
871 QVERIFY(object != nullptr);
872
873 QCOMPARE(object->font().pixelSize(), 10);
874
875 delete object;
876 }
877
878 // Test pixelSize and pointSize
879 {
880 QQmlComponent component(&engine, testFileUrl(fileName: "font_write.3.qml"));
881 QTest::ignoreMessage(type: QtWarningMsg, message: "Both point size and pixel size set. Using pixel size.");
882 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
883 QVERIFY(object != nullptr);
884
885 QCOMPARE(object->font().pixelSize(), 10);
886
887 delete object;
888 }
889 {
890 QQmlComponent component(&engine, testFileUrl(fileName: "font_write.4.qml"));
891 QTest::ignoreMessage(type: QtWarningMsg, message: "Both point size and pixel size set. Using pixel size.");
892 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
893 QVERIFY(object != nullptr);
894
895 QCOMPARE(object->font().pixelSize(), 10);
896
897 delete object;
898 }
899 {
900 QQmlComponent component(&engine, testFileUrl(fileName: "font_write.5.qml"));
901 QObject *object = qobject_cast<QObject *>(object: component.create());
902 QVERIFY(object != nullptr);
903 MyTypeObject *object1 = object->findChild<MyTypeObject *>(aName: "object1");
904 QVERIFY(object1 != nullptr);
905 MyTypeObject *object2 = object->findChild<MyTypeObject *>(aName: "object2");
906 QVERIFY(object2 != nullptr);
907
908 QCOMPARE(object1->font().pixelSize(), 19);
909 QCOMPARE(object2->font().pointSize(), 14);
910
911 delete object;
912 }
913
914 {
915 QQmlComponent component(&engine, testFileUrl(fileName: "font_compare.qml"));
916 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
917 QVERIFY(object != nullptr);
918
919 QString tostring = QLatin1String("QFont(") + object->font().toString() + QLatin1Char(')');
920 QCOMPARE(object->property("tostring").toString(), tostring);
921 QCOMPARE(object->property("equalsString").toBool(), true);
922 QCOMPARE(object->property("equalsColor").toBool(), false);
923 QCOMPARE(object->property("equalsVector3d").toBool(), false);
924 QCOMPARE(object->property("equalsSize").toBool(), false);
925 QCOMPARE(object->property("equalsPoint").toBool(), false);
926 QCOMPARE(object->property("equalsRect").toBool(), false);
927 QCOMPARE(object->property("equalsSelf").toBool(), true);
928
929 delete object;
930 }
931}
932
933void tst_qqmlvaluetypes::color()
934{
935 {
936 QQmlComponent component(&engine, testFileUrl(fileName: "color_read.qml"));
937 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
938 QVERIFY(object != nullptr);
939
940 QCOMPARE((float)object->property("v_r").toDouble(), (float)0.2);
941 QCOMPARE((float)object->property("v_g").toDouble(), (float)0.88);
942 QCOMPARE((float)object->property("v_b").toDouble(), (float)0.6);
943 QCOMPARE((float)object->property("v_a").toDouble(), (float)0.34);
944
945 QCOMPARE(qRound(object->property("hsv_h").toDouble() * 100), 43);
946 QCOMPARE(qRound(object->property("hsv_s").toDouble() * 100), 77);
947 QCOMPARE(qRound(object->property("hsv_v").toDouble() * 100), 88);
948
949 QCOMPARE(qRound(object->property("hsl_h").toDouble() * 100), 43);
950 QCOMPARE(qRound(object->property("hsl_s").toDouble() * 100), 74);
951 QCOMPARE(qRound(object->property("hsl_l").toDouble() * 100), 54);
952
953 QCOMPARE(object->property("valid").userType(), QMetaType::Bool);
954 QVERIFY(object->property("valid").toBool());
955 QCOMPARE(object->property("invalid").userType(), QMetaType::Bool);
956 QVERIFY(!object->property("invalid").toBool());
957
958 QColor comparison;
959 comparison.setRedF(0.2);
960 comparison.setGreenF(0.88);
961 comparison.setBlueF(0.6);
962 comparison.setAlphaF(0.34);
963 QCOMPARE(object->property("copy"), QVariant(comparison));
964
965 delete object;
966 }
967
968 {
969 QQmlComponent component(&engine, testFileUrl(fileName: "color_write.qml"));
970 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
971 QVERIFY(object != nullptr);
972
973 QColor newColor;
974 newColor.setRedF(0.5);
975 newColor.setGreenF(0.38);
976 newColor.setBlueF(0.3);
977 newColor.setAlphaF(0.7);
978 QCOMPARE(object->color(), newColor);
979
980 delete object;
981 }
982
983 {
984 QQmlComponent component(&engine, testFileUrl(fileName: "color_write_HSV.qml"));
985 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
986 QVERIFY(object != nullptr);
987
988 QColor newColor;
989 newColor.setHsvF(h: 0.43, s: 0.77, v: 0.88, a: 0.7);
990 QCOMPARE(object->color(), newColor);
991
992 delete object;
993 }
994
995 {
996 QQmlComponent component(&engine, testFileUrl(fileName: "color_write_HSL.qml"));
997 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
998 QVERIFY(object != nullptr);
999
1000 QColor newColor;
1001 newColor.setHslF(h: 0.43, s: 0.74, l: 0.54, a: 0.7);
1002 QCOMPARE(object->color(), newColor);
1003
1004 delete object;
1005 }
1006
1007 {
1008 QQmlComponent component(&engine, testFileUrl(fileName: "color_compare.qml"));
1009 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1010 QVERIFY(object != nullptr);
1011 QColor comparison;
1012 comparison.setRedF(0.2);
1013 comparison.setGreenF(0.88);
1014 comparison.setBlueF(0.6);
1015 comparison.setAlphaF(0.34);
1016 QString colorString = comparison.name(format: QColor::HexArgb);
1017 QCOMPARE(object->property("colorToString").toString(), colorString);
1018 QCOMPARE(object->property("colorEqualsIdenticalRgba").toBool(), true);
1019 QCOMPARE(object->property("colorEqualsDifferentAlpha").toBool(), false);
1020 QCOMPARE(object->property("colorEqualsDifferentRgba").toBool(), false);
1021 QCOMPARE(object->property("colorToStringEqualsColorString").toBool(), true);
1022 QCOMPARE(object->property("colorToStringEqualsDifferentAlphaString").toBool(), true);
1023 QCOMPARE(object->property("colorToStringEqualsDifferentRgbaString").toBool(), false);
1024 QCOMPARE(object->property("colorEqualsColorString").toBool(), true); // maintaining behaviour with QtQuick 1.0
1025 QCOMPARE(object->property("colorEqualsDifferentAlphaString").toBool(), true); // maintaining behaviour with QtQuick 1.0
1026 QCOMPARE(object->property("colorEqualsDifferentRgbaString").toBool(), false);
1027
1028 QCOMPARE(object->property("equalsColor").toBool(), true);
1029 QCOMPARE(object->property("equalsVector3d").toBool(), false);
1030 QCOMPARE(object->property("equalsSize").toBool(), false);
1031 QCOMPARE(object->property("equalsPoint").toBool(), false);
1032 QCOMPARE(object->property("equalsRect").toBool(), false);
1033
1034 // Color == Property and Property == Color should return the same result.
1035 QCOMPARE(object->property("equalsColorRHS").toBool(), object->property("equalsColor").toBool());
1036 QCOMPARE(object->property("colorEqualsCopy").toBool(), true);
1037 QCOMPARE(object->property("copyEqualsColor").toBool(), object->property("colorEqualsCopy").toBool());
1038
1039 delete object;
1040 }
1041}
1042
1043// Test bindings can write to value types
1044void tst_qqmlvaluetypes::bindingAssignment()
1045{
1046 // binding declaration
1047 {
1048 QQmlComponent component(&engine, testFileUrl(fileName: "bindingAssignment.qml"));
1049 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1050 QVERIFY(object != nullptr);
1051
1052 QCOMPARE(object->rect().x(), 10);
1053 QCOMPARE(object->rect().y(), 15);
1054
1055 object->setProperty(name: "value", value: QVariant(92));
1056
1057 QCOMPARE(object->rect().x(), 92);
1058 QCOMPARE(object->rect().y(), 97);
1059
1060 delete object;
1061 }
1062
1063 // function assignment should fail without crashing
1064 {
1065 QString warning1 = testFileUrl(fileName: "bindingAssignment.2.qml").toString() + QLatin1String(":6:5: Invalid use of Qt.binding() in a binding declaration.");
1066 QString warning2 = testFileUrl(fileName: "bindingAssignment.2.qml").toString() + QLatin1String(":10: Cannot assign JavaScript function to value-type property");
1067 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning1));
1068 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning2));
1069 QQmlComponent component(&engine, testFileUrl(fileName: "bindingAssignment.2.qml"));
1070 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1071 QVERIFY(object != nullptr);
1072 QCOMPARE(object->rect().x(), 5);
1073 object->setProperty(name: "value", value: QVariant(92));
1074 QCOMPARE(object->rect().x(), 5);
1075 delete object;
1076 }
1077}
1078
1079// Test bindings can read from value types
1080void tst_qqmlvaluetypes::bindingRead()
1081{
1082 QQmlComponent component(&engine, testFileUrl(fileName: "bindingRead.qml"));
1083 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1084 QVERIFY(object != nullptr);
1085
1086 QCOMPARE(object->property("value").toInt(), 2);
1087
1088 object->setRect(QRect(19, 3, 88, 2));
1089
1090 QCOMPARE(object->property("value").toInt(), 19);
1091
1092 delete object;
1093}
1094
1095// Test static values can assign to value types
1096void tst_qqmlvaluetypes::staticAssignment()
1097{
1098 QQmlComponent component(&engine, testFileUrl(fileName: "staticAssignment.qml"));
1099 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1100 QVERIFY(object != nullptr);
1101
1102 QCOMPARE(object->rect().x(), 9);
1103
1104 delete object;
1105}
1106
1107// Test scripts can read/write value types
1108void tst_qqmlvaluetypes::scriptAccess()
1109{
1110 QQmlComponent component(&engine, testFileUrl(fileName: "scriptAccess.qml"));
1111 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1112 QVERIFY(object != nullptr);
1113
1114 QCOMPARE(object->property("valuePre").toInt(), 2);
1115 QCOMPARE(object->rect().x(), 19);
1116 QCOMPARE(object->property("valuePost").toInt(), 19);
1117
1118 delete object;
1119}
1120
1121// Test that assigning a constant from script removes any binding
1122void tst_qqmlvaluetypes::autoBindingRemoval()
1123{
1124 {
1125 QQmlComponent component(&engine, testFileUrl(fileName: "autoBindingRemoval.qml"));
1126 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1127 QVERIFY(object != nullptr);
1128
1129 QCOMPARE(object->rect().x(), 10);
1130
1131 object->setProperty(name: "value", value: QVariant(13));
1132
1133 QCOMPARE(object->rect().x(), 13);
1134
1135 object->emitRunScript();
1136
1137 QCOMPARE(object->rect().x(), 42);
1138
1139 object->setProperty(name: "value", value: QVariant(92));
1140
1141 QCOMPARE(object->rect().x(), 42);
1142
1143 delete object;
1144 }
1145
1146 {
1147 QQmlComponent component(&engine, testFileUrl(fileName: "autoBindingRemoval.2.qml"));
1148 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1149 QVERIFY(object != nullptr);
1150
1151 QCOMPARE(object->rect().x(), 10);
1152
1153 object->setProperty(name: "value", value: QVariant(13));
1154
1155 QCOMPARE(object->rect().x(), 13);
1156
1157 object->emitRunScript();
1158
1159 QCOMPARE(object->rect(), QRect(10, 10, 10, 10));
1160
1161 object->setProperty(name: "value", value: QVariant(92));
1162
1163 QCOMPARE(object->rect(), QRect(10, 10, 10, 10));
1164
1165 delete object;
1166 }
1167
1168 {
1169 QQmlComponent component(&engine, testFileUrl(fileName: "autoBindingRemoval.3.qml"));
1170 QString warning = component.url().toString() + ":6:5: Unable to assign [undefined] to QRect";
1171 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
1172 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1173 QVERIFY(object != nullptr);
1174
1175 object->setProperty(name: "value", value: QVariant(QRect(9, 22, 33, 44)));
1176
1177 QCOMPARE(object->rect(), QRect(9, 22, 33, 44));
1178
1179 object->emitRunScript();
1180
1181 QCOMPARE(object->rect(), QRect(44, 22, 33, 44));
1182
1183 object->setProperty(name: "value", value: QVariant(QRect(19, 3, 4, 8)));
1184
1185 QCOMPARE(object->rect(), QRect(44, 22, 33, 44));
1186
1187 delete object;
1188 }
1189}
1190
1191// Test that property value sources assign to value types
1192void tst_qqmlvaluetypes::valueSources()
1193{
1194 QQmlComponent component(&engine, testFileUrl(fileName: "valueSources.qml"));
1195 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1196 QVERIFY(object != nullptr);
1197
1198 QCOMPARE(object->rect().x(), 3345);
1199
1200 delete object;
1201}
1202
1203static void checkNoErrors(QQmlComponent& component)
1204{
1205 QList<QQmlError> errors = component.errors();
1206 if (errors.isEmpty())
1207 return;
1208 for (int ii = 0; ii < errors.count(); ++ii) {
1209 const QQmlError &error = errors.at(i: ii);
1210 qWarning(msg: "%d:%d:%s",error.line(),error.column(),error.description().toUtf8().constData());
1211 }
1212}
1213
1214// Test that property value interceptors can be applied to value types
1215void tst_qqmlvaluetypes::valueInterceptors()
1216{
1217 QQmlComponent component(&engine, testFileUrl(fileName: "valueInterceptors.qml"));
1218 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1219 checkNoErrors(component);
1220 QVERIFY(object != nullptr);
1221
1222 QCOMPARE(object->rect().x(), 13);
1223
1224 object->setProperty(name: "value", value: 99);
1225
1226 QCOMPARE(object->rect().x(), 112);
1227
1228 delete object;
1229}
1230
1231// Test that you can't assign a binding to the "root" value type, and a sub-property
1232void tst_qqmlvaluetypes::bindingConflict()
1233{
1234 QQmlComponent component(&engine, testFileUrl(fileName: "bindingConflict.qml"));
1235 QCOMPARE(component.isError(), true);
1236}
1237
1238#define CPP_TEST(type, v) \
1239{ \
1240 type *t = new type; \
1241 QVariant value(v); \
1242 t->setValue(value); \
1243 QCOMPARE(t->value(), value); \
1244 delete t; \
1245}
1246
1247// Test that accessing a reference to a valuetype after the owning object is deleted
1248// doesn't crash
1249void tst_qqmlvaluetypes::deletedObject()
1250{
1251 QQmlComponent component(&engine, testFileUrl(fileName: "deletedObject.qml"));
1252 QTest::ignoreMessage(type: QtDebugMsg, message: "Test: 2");
1253 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1254 QVERIFY(object != nullptr);
1255
1256 QObject *dObject = qvariant_cast<QObject *>(v: object->property(name: "object"));
1257 QVERIFY(dObject != nullptr);
1258 delete dObject;
1259
1260 QTest::ignoreMessage(type: QtDebugMsg, message: "Test: undefined");
1261 object->emitRunScript();
1262
1263 delete object;
1264}
1265
1266// Test that value types can be assigned to another value type property in a binding
1267void tst_qqmlvaluetypes::bindingVariantCopy()
1268{
1269 QQmlComponent component(&engine, testFileUrl(fileName: "bindingVariantCopy.qml"));
1270 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1271 QVERIFY(object != nullptr);
1272
1273 QCOMPARE(object->rect(), QRect(19, 33, 5, 99));
1274
1275 delete object;
1276}
1277
1278// Test that value types can be assigned to another value type property in script
1279void tst_qqmlvaluetypes::scriptVariantCopy()
1280{
1281 QQmlComponent component(&engine, testFileUrl(fileName: "scriptVariantCopy.qml"));
1282 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1283 QVERIFY(object != nullptr);
1284
1285 QCOMPARE(object->rect(), QRect(2, 3, 109, 102));
1286
1287 object->emitRunScript();
1288
1289 QCOMPARE(object->rect(), QRect(19, 33, 5, 99));
1290
1291 delete object;
1292}
1293
1294void tst_qqmlvaluetypes::enums()
1295{
1296 {
1297 QQmlComponent component(&engine, testFileUrl(fileName: "enums.1.qml"));
1298 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1299 QVERIFY(object != nullptr);
1300 QCOMPARE(object->font().capitalization(), QFont::AllUppercase);
1301 delete object;
1302 }
1303
1304 {
1305 QQmlComponent component(&engine, testFileUrl(fileName: "enums.2.qml"));
1306 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1307 QVERIFY(object != nullptr);
1308 QCOMPARE(object->font().capitalization(), QFont::AllUppercase);
1309 delete object;
1310 }
1311
1312 {
1313 QQmlComponent component(&engine, testFileUrl(fileName: "enums.3.qml"));
1314 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1315 QVERIFY(object != nullptr);
1316 QCOMPARE(object->font().capitalization(), QFont::AllUppercase);
1317 delete object;
1318 }
1319
1320 {
1321 QQmlComponent component(&engine, testFileUrl(fileName: "enums.4.qml"));
1322 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1323 QVERIFY(object != nullptr);
1324 QCOMPARE(object->font().capitalization(), QFont::AllUppercase);
1325 delete object;
1326 }
1327
1328 {
1329 QQmlComponent component(&engine, testFileUrl(fileName: "enums.5.qml"));
1330 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
1331 QVERIFY(object != nullptr);
1332 QCOMPARE(object->font().capitalization(), QFont::AllUppercase);
1333 delete object;
1334 }
1335}
1336
1337// Tests switching between "conflicting" bindings (eg. a binding on the core
1338// property, to a binding on the value-type sub-property)
1339void tst_qqmlvaluetypes::conflictingBindings()
1340{
1341 {
1342 QQmlComponent component(&engine, testFileUrl(fileName: "conflicting.1.qml"));
1343 QObject *object = component.create();
1344 QVERIFY(object != nullptr);
1345
1346 QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12);
1347
1348 QMetaObject::invokeMethod(obj: object, member: "toggle");
1349
1350 QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 6);
1351
1352 QMetaObject::invokeMethod(obj: object, member: "toggle");
1353
1354 QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12);
1355
1356 delete object;
1357 }
1358
1359 {
1360 QQmlComponent component(&engine, testFileUrl(fileName: "conflicting.2.qml"));
1361 QObject *object = component.create();
1362 QVERIFY(object != nullptr);
1363
1364 QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 6);
1365
1366 QMetaObject::invokeMethod(obj: object, member: "toggle");
1367
1368 QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12);
1369
1370 QMetaObject::invokeMethod(obj: object, member: "toggle");
1371
1372 QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 6);
1373
1374 delete object;
1375 }
1376
1377 {
1378 QQmlComponent component(&engine, testFileUrl(fileName: "conflicting.3.qml"));
1379 QObject *object = component.create();
1380 QVERIFY(object != nullptr);
1381
1382 QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12);
1383
1384 QMetaObject::invokeMethod(obj: object, member: "toggle");
1385
1386 QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 24);
1387
1388 QMetaObject::invokeMethod(obj: object, member: "toggle");
1389
1390 QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12);
1391
1392 delete object;
1393 }
1394}
1395
1396void tst_qqmlvaluetypes::returnValues()
1397{
1398 QQmlComponent component(&engine, testFileUrl(fileName: "returnValues.qml"));
1399 QObject *object = component.create();
1400 QVERIFY(object != nullptr);
1401
1402 QCOMPARE(object->property("test1").toBool(), true);
1403 QCOMPARE(object->property("test2").toBool(), true);
1404 QCOMPARE(object->property("size").toSize(), QSize(13, 14));
1405
1406 delete object;
1407}
1408
1409void tst_qqmlvaluetypes::varAssignment()
1410{
1411 QQmlComponent component(&engine, testFileUrl(fileName: "varAssignment.qml"));
1412 QObject *object = component.create();
1413 QVERIFY(object != nullptr);
1414
1415 QCOMPARE(object->property("x").toInt(), 1);
1416 QCOMPARE(object->property("y").toInt(), 2);
1417 QCOMPARE(object->property("z").toInt(), 3);
1418
1419 delete object;
1420}
1421
1422// Test bindings splice together correctly
1423void tst_qqmlvaluetypes::bindingsSpliceCorrectly()
1424{
1425 {
1426 QQmlComponent component(&engine, testFileUrl(fileName: "bindingsSpliceCorrectly.1.qml"));
1427 QObject *object = component.create();
1428 QVERIFY(object != nullptr);
1429
1430 QCOMPARE(object->property("test").toBool(), true);
1431
1432 delete object;
1433 }
1434
1435 {
1436 QQmlComponent component(&engine, testFileUrl(fileName: "bindingsSpliceCorrectly.2.qml"));
1437 QObject *object = component.create();
1438 QVERIFY(object != nullptr);
1439
1440 QCOMPARE(object->property("test").toBool(), true);
1441
1442 delete object;
1443 }
1444
1445
1446 {
1447 QQmlComponent component(&engine, testFileUrl(fileName: "bindingsSpliceCorrectly.3.qml"));
1448 QObject *object = component.create();
1449 QVERIFY(object != nullptr);
1450
1451 QCOMPARE(object->property("test").toBool(), true);
1452
1453 delete object;
1454 }
1455
1456 {
1457 QQmlComponent component(&engine, testFileUrl(fileName: "bindingsSpliceCorrectly.4.qml"));
1458 QObject *object = component.create();
1459 QVERIFY(object != nullptr);
1460
1461 QCOMPARE(object->property("test").toBool(), true);
1462
1463 delete object;
1464 }
1465
1466 {
1467 QQmlComponent component(&engine, testFileUrl(fileName: "bindingsSpliceCorrectly.5.qml"));
1468 QObject *object = component.create();
1469 QVERIFY(object != nullptr);
1470
1471 QCOMPARE(object->property("test").toBool(), true);
1472
1473 delete object;
1474 }
1475}
1476
1477void tst_qqmlvaluetypes::nonValueTypeComparison()
1478{
1479 QQmlComponent component(&engine, testFileUrl(fileName: "nonValueTypeComparison.qml"));
1480 QObject *object = component.create();
1481 QVERIFY(object != nullptr);
1482
1483 QCOMPARE(object->property("test1").toBool(), true);
1484 QCOMPARE(object->property("test2").toBool(), true);
1485
1486 delete object;
1487}
1488
1489void tst_qqmlvaluetypes::initializeByWrite()
1490{
1491 QQmlComponent component(&engine, testFileUrl(fileName: "initializeByWrite.qml"));
1492 QObject *object = component.create();
1493 QVERIFY(object != nullptr);
1494
1495 QCOMPARE(object->property("test").toBool(), true);
1496
1497 delete object;
1498}
1499
1500void tst_qqmlvaluetypes::groupedInterceptors_data()
1501{
1502 QTest::addColumn<QString>(name: "qmlfile");
1503 QTest::addColumn<QColor>(name: "expectedInitialColor");
1504 QTest::addColumn<QColor>(name: "setColor");
1505 QTest::addColumn<QColor>(name: "expectedFinalColor");
1506
1507 QColor c0, c1, c2;
1508 c0.setRgbF(r: 0.1f, g: 0.2f, b: 0.3f, a: 0.4f);
1509 c1.setRgbF(r: 0.2f, g: 0.4f, b: 0.6f, a: 0.8f);
1510 c2.setRgbF(r: 0.8f, g: 0.6f, b: 0.4f, a: 0.2f);
1511
1512 QTest::newRow(dataTag: "value-interceptor") << QString::fromLatin1(str: "grouped_interceptors_value.qml") << c0 << c1 << c2;
1513 QTest::newRow(dataTag: "component-interceptor") << QString::fromLatin1(str: "grouped_interceptors_component.qml") << QColor(128, 0, 255) << QColor(50, 100, 200) << QColor(0, 100, 200);
1514 QTest::newRow(dataTag: "ignore-interceptor") << QString::fromLatin1(str: "grouped_interceptors_ignore.qml") << QColor(128, 0, 255) << QColor(50, 100, 200) << QColor(128, 100, 200);
1515}
1516
1517static bool fuzzyCompare(qreal a, qreal b)
1518{
1519 const qreal EPSILON = 0.0001;
1520 return (a + EPSILON > b) && (a - EPSILON < b);
1521}
1522
1523void tst_qqmlvaluetypes::groupedInterceptors()
1524{
1525 QFETCH(QString, qmlfile);
1526 QFETCH(QColor, expectedInitialColor);
1527 QFETCH(QColor, setColor);
1528 QFETCH(QColor, expectedFinalColor);
1529
1530 QQmlComponent component(&engine, testFileUrl(fileName: qmlfile));
1531 QObject *object = component.create();
1532 QVERIFY2(object != nullptr, qPrintable(component.errorString()));
1533
1534 QColor initialColor = object->property(name: "color").value<QColor>();
1535 QVERIFY(fuzzyCompare(initialColor.redF(), expectedInitialColor.redF()));
1536 QVERIFY(fuzzyCompare(initialColor.greenF(), expectedInitialColor.greenF()));
1537 QVERIFY(fuzzyCompare(initialColor.blueF(), expectedInitialColor.blueF()));
1538 QVERIFY(fuzzyCompare(initialColor.alphaF(), expectedInitialColor.alphaF()));
1539
1540 object->setProperty(name: "color", value: setColor);
1541
1542 QColor finalColor = object->property(name: "color").value<QColor>();
1543 QVERIFY(fuzzyCompare(finalColor.redF(), expectedFinalColor.redF()));
1544 QVERIFY(fuzzyCompare(finalColor.greenF(), expectedFinalColor.greenF()));
1545 QVERIFY(fuzzyCompare(finalColor.blueF(), expectedFinalColor.blueF()));
1546 QVERIFY(fuzzyCompare(finalColor.alphaF(), expectedFinalColor.alphaF()));
1547
1548 delete object;
1549}
1550
1551struct MyDesk
1552{
1553 Q_PROPERTY(int monitorCount MEMBER monitorCount)
1554 Q_GADGET
1555public:
1556 MyDesk() : monitorCount(1) {}
1557
1558 int monitorCount;
1559};
1560
1561bool operator==(const MyDesk &lhs, const MyDesk &rhs)
1562{ return lhs.monitorCount == rhs.monitorCount; }
1563bool operator!=(const MyDesk &lhs, const MyDesk &rhs)
1564{ return lhs.monitorCount != rhs.monitorCount; }
1565
1566Q_DECLARE_METATYPE(MyDesk)
1567
1568struct MyOffice
1569{
1570 Q_PROPERTY(int chairs MEMBER m_chairs)
1571 Q_PROPERTY(MyDesk desk READ desk WRITE setDesk)
1572 Q_PROPERTY(QVariant myThing READ myThing WRITE setMyThing)
1573 Q_GADGET
1574public:
1575 MyOffice() : m_chairs(0) {}
1576
1577 MyDesk desk() const { return m_desk; }
1578 void setDesk(const MyDesk &d) { m_desk = d; }
1579
1580 QVariant myThing() const { return m_myThing; }
1581 void setMyThing(const QVariant &thingy) { m_myThing = thingy; }
1582
1583 int m_chairs;
1584 MyDesk m_desk;
1585 QVariant m_myThing;
1586};
1587
1588Q_DECLARE_METATYPE(MyOffice)
1589
1590void tst_qqmlvaluetypes::customValueType()
1591{
1592 QJSEngine engine;
1593
1594 MyOffice cppOffice;
1595 cppOffice.m_chairs = 2;
1596
1597 QVariantMap m;
1598 m.insert(QStringLiteral("hasChair"), value: false);
1599 m.insert(QStringLiteral("textOnWhiteboard"), QStringLiteral("Blah blah"));
1600 cppOffice.m_myThing = m;
1601
1602 QJSValue office = engine.toScriptValue(value: cppOffice);
1603 QCOMPARE(office.property("chairs").toInt(), 2);
1604 office.setProperty(name: "chairs", value: 1);
1605 QCOMPARE(office.property("chairs").toInt(), 1);
1606 QCOMPARE(cppOffice.m_chairs, 2);
1607
1608 QJSValue jsDesk = office.property(name: "desk");
1609 QCOMPARE(jsDesk.property("monitorCount").toInt(), 1);
1610 jsDesk.setProperty(name: "monitorCount", value: 2);
1611 QCOMPARE(jsDesk.property("monitorCount").toInt(), 2);
1612
1613 QCOMPARE(cppOffice.desk().monitorCount, 1);
1614
1615 office.setProperty(name: "desk", value: jsDesk);
1616 cppOffice = engine.fromScriptValue<MyOffice>(value: office);
1617 QCOMPARE(cppOffice.m_chairs, 1);
1618 QCOMPARE(cppOffice.desk().monitorCount, 2);
1619
1620 QJSValue thingy = office.property(name: "myThing");
1621 QVERIFY(thingy.hasProperty("hasChair"));
1622 QVERIFY(thingy.property("hasChair").isBool());
1623 QCOMPARE(thingy.property("hasChair").toBool(), false);
1624 QVERIFY(thingy.property("textOnWhiteboard").isString());
1625 QVERIFY(thingy.hasProperty("textOnWhiteboard"));
1626 QCOMPARE(thingy.property("textOnWhiteboard").toString(), QStringLiteral("Blah blah"));
1627}
1628
1629struct BaseGadget
1630{
1631 Q_GADGET
1632 Q_PROPERTY(int baseProperty READ baseProperty WRITE setBaseProperty)
1633public:
1634 BaseGadget() : m_baseProperty(0) {}
1635 BaseGadget(int initValue) : m_baseProperty(initValue) {}
1636
1637 int baseProperty() const { return m_baseProperty; }
1638 void setBaseProperty(int value) { m_baseProperty = value; }
1639 int m_baseProperty;
1640
1641 Q_INVOKABLE void functionInBaseGadget(int value) { m_baseProperty = value; }
1642};
1643
1644Q_DECLARE_METATYPE(BaseGadget)
1645
1646struct DerivedGadget : public BaseGadget
1647{
1648 Q_GADGET
1649 Q_PROPERTY(int derivedProperty READ derivedProperty WRITE setDerivedProperty)
1650public:
1651 DerivedGadget() : m_derivedProperty(0) {}
1652
1653 int derivedProperty() const { return m_derivedProperty; }
1654 void setDerivedProperty(int value) { m_derivedProperty = value; }
1655 int m_derivedProperty;
1656
1657 Q_INVOKABLE void functionInDerivedGadget(int value) { m_derivedProperty = value; }
1658};
1659
1660// QTBUG-66744: we want a Q_GADGET giving us generic type safety in C++ and property access in Qml
1661template <typename T>
1662struct DerivedTypedGadget : public BaseGadget
1663{
1664 // cannot use Q_GADGET here
1665public:
1666 DerivedTypedGadget() {}
1667};
1668
1669class DerivedTypedGadgetDummyType {};
1670
1671Q_DECLARE_METATYPE(DerivedTypedGadget<DerivedTypedGadgetDummyType>)
1672
1673class TypeWithCustomValueType : public QObject
1674{
1675 Q_OBJECT
1676 Q_PROPERTY(MyDesk desk MEMBER m_desk)
1677 Q_PROPERTY(DerivedGadget derivedGadget READ derivedGadget WRITE setDerivedGadget)
1678public:
1679
1680 MyDesk m_desk;
1681
1682 DerivedGadget derivedGadget() const { return m_derivedGadget; }
1683 void setDerivedGadget(const DerivedGadget &value) { m_derivedGadget = value; }
1684 DerivedGadget m_derivedGadget;
1685};
1686
1687void tst_qqmlvaluetypes::customValueTypeInQml()
1688{
1689 qmlRegisterType<TypeWithCustomValueType>(uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "TypeWithCustomValueType");
1690 QQmlComponent component(&engine, testFileUrl(fileName: "customvaluetype.qml"));
1691 QScopedPointer<QObject> object(component.create());
1692 QVERIFY(!object.isNull());
1693
1694 TypeWithCustomValueType *t = qobject_cast<TypeWithCustomValueType*>(object: object.data());
1695 Q_ASSERT(t);
1696 QCOMPARE(t->m_desk.monitorCount, 3);
1697 QCOMPARE(t->m_derivedGadget.baseProperty(), 42);
1698}
1699
1700Q_DECLARE_METATYPE(DerivedGadget)
1701
1702void tst_qqmlvaluetypes::gadgetInheritance()
1703{
1704 QJSEngine engine;
1705
1706 QJSValue value = engine.toScriptValue(value: DerivedGadget());
1707
1708 QCOMPARE(value.property("baseProperty").toInt(), 0);
1709 value.setProperty(name: "baseProperty", value: 10);
1710 QCOMPARE(value.property("baseProperty").toInt(), 10);
1711
1712 QJSValue method = value.property(name: "functionInBaseGadget");
1713 method.call(args: QJSValueList() << QJSValue(42));
1714 QCOMPARE(value.property("baseProperty").toInt(), 42);
1715}
1716
1717void tst_qqmlvaluetypes::gadgetTemplateInheritance()
1718{
1719 QJSEngine engine;
1720
1721 QJSValue value = engine.toScriptValue(value: DerivedTypedGadget<DerivedTypedGadgetDummyType>());
1722
1723 QCOMPARE(value.property("baseProperty").toInt(), 0);
1724 value.setProperty(name: "baseProperty", value: 10);
1725 QCOMPARE(value.property("baseProperty").toInt(), 10);
1726
1727 QJSValue method = value.property(name: "functionInBaseGadget");
1728 method.call(args: QJSValueList() << QJSValue(42));
1729 QCOMPARE(value.property("baseProperty").toInt(), 42);
1730}
1731
1732void tst_qqmlvaluetypes::sequences()
1733{
1734 QJSEngine engine;
1735 {
1736 QList<BaseGadget> gadgetList{1, 4, 7, 8, 15};
1737 QJSValue value = engine.toScriptValue(value: gadgetList);
1738 QCOMPARE(value.property("length").toInt(), gadgetList.length());
1739 for (int i = 0; i < gadgetList.length(); ++i)
1740 QCOMPARE(value.property(i).property("baseProperty").toInt(), gadgetList.at(i).baseProperty());
1741 }
1742 {
1743 std::vector<BaseGadget> container{1, 4, 7, 8, 15};
1744 QJSValue value = engine.toScriptValue(value: container);
1745 QCOMPARE(value.property("length").toInt(), int(container.size()));
1746 for (size_t i = 0; i < container.size(); ++i)
1747 QCOMPARE(value.property(i).property("baseProperty").toInt(), container.at(i).baseProperty());
1748 }
1749 {
1750 QVector<QChar> qcharVector{1, 4, 42, 8, 15};
1751 QJSValue value = engine.toScriptValue(value: qcharVector);
1752 QCOMPARE(value.property("length").toInt(), qcharVector.length());
1753 for (int i = 0; i < qcharVector.length(); ++i)
1754 QCOMPARE(value.property(i).toString(), qcharVector.at(i));
1755 }
1756 {
1757 MyTypeObject a, b, c;
1758 QSet<QObject*> objSet{&a, &b, &c};
1759 QJSValue value = engine.toScriptValue(value: objSet);
1760 QCOMPARE(value.property("length").toInt(), objSet.size());
1761 for (int i = 0; i < objSet.size(); ++i)
1762 QCOMPARE(value.property(i).property("point").property("x").toInt(), a.point().x());
1763 }
1764 {
1765 MyTypeObject a, b, c;
1766 QSet<MyTypeObject*> container{&a, &b, &c};
1767 QJSValue value = engine.toScriptValue(value: container);
1768 QCOMPARE(value.property("length").toInt(), container.size());
1769 for (int i = 0; i < container.size(); ++i)
1770 QCOMPARE(value.property(i).property("point").property("x").toInt(), a.point().x());
1771 }
1772}
1773struct StringLessGadget {
1774 Q_GADGET
1775};
1776
1777Q_DECLARE_METATYPE(StringLessGadget)
1778
1779static QString StringLessGadget_to_QString(const StringLessGadget &)
1780{
1781 return QLatin1String("Surprise!");
1782}
1783
1784void tst_qqmlvaluetypes::toStringConversion()
1785{
1786 QJSEngine engine;
1787
1788 StringLessGadget g;
1789 QJSValue value = engine.toScriptValue(value: g);
1790
1791 QJSValue method = value.property(name: "toString");
1792 QJSValue stringConversion = method.callWithInstance(instance: value);
1793 QCOMPARE(stringConversion.toString(), QString("StringLessGadget()"));
1794
1795 QMetaType::registerConverter<StringLessGadget, QString>(function: StringLessGadget_to_QString);
1796
1797 stringConversion = method.callWithInstance(instance: value);
1798 QCOMPARE(stringConversion.toString(), StringLessGadget_to_QString(g));
1799}
1800
1801void tst_qqmlvaluetypes::enumerableProperties()
1802{
1803 QJSEngine engine;
1804 DerivedGadget g;
1805 QJSValue value = engine.toScriptValue(value: g);
1806 QSet<QString> names;
1807 QJSValueIterator it(value);
1808 while (it.hasNext()) {
1809 it.next();
1810 const QString name = it.name();
1811 QVERIFY(!names.contains(name));
1812 names.insert(value: name);
1813 }
1814
1815 QCOMPARE(names.count(), 2);
1816 QVERIFY(names.contains(QStringLiteral("baseProperty")));
1817 QVERIFY(names.contains(QStringLiteral("derivedProperty")));
1818}
1819
1820struct GadgetWithEnum
1821{
1822 Q_GADGET
1823public:
1824
1825 enum MyEnum { FirstValue, SecondValue };
1826
1827 Q_ENUM(MyEnum)
1828 Q_PROPERTY(MyEnum enumProperty READ enumProperty)
1829
1830 MyEnum enumProperty() const { return SecondValue; }
1831};
1832
1833void tst_qqmlvaluetypes::enumProperties()
1834{
1835 QJSEngine engine;
1836
1837 // When creating the property cache for the gadget when MyEnum is _not_ a registered
1838 // meta-type, then QMetaProperty::type() will return QMetaType::Int and consequently
1839 // property-read meta-calls will return an int (as expected in this test). However if we
1840 // explicitly register the gadget, then QMetaProperty::type() will return the user-type
1841 // and QQmlValueTypeWrapper should still handle that and return an integer/number for the
1842 // enum property when it is read.
1843 qRegisterMetaType<GadgetWithEnum::MyEnum>();
1844
1845 GadgetWithEnum g;
1846 QJSValue value = engine.toScriptValue(value: g);
1847
1848 QJSValue enumValue = value.property(name: "enumProperty");
1849 QVERIFY(enumValue.isNumber());
1850 QCOMPARE(enumValue.toInt(), int(g.enumProperty()));
1851}
1852
1853void tst_qqmlvaluetypes::scarceTypes()
1854{
1855 // These should not be treated as value types because we want the scarce resource
1856 // mechanism to clear them when going out of scope. The scarce resource mechanism
1857 // only works on QV4::VariantObject as that has an additional level of redirection.
1858 QVERIFY(!QQmlValueTypeFactory::isValueType(qMetaTypeId<QImage>()));
1859 QVERIFY(!QQmlValueTypeFactory::isValueType(qMetaTypeId<QPixmap>()));
1860
1861 QV4::ExecutionEngine engine;
1862 QV4::Scope scope(&engine);
1863
1864 QImage img(20, 20, QImage::Format_ARGB32);
1865 QV4::ScopedObject imgValue(scope, engine.fromVariant(QVariant::fromValue(value: img)));
1866 QCOMPARE(QByteArray(imgValue->vtable()->className), QByteArray("VariantObject"));
1867
1868 QPixmap pixmap;
1869 QV4::ScopedObject pixmapValue(scope, engine.fromVariant(QVariant::fromValue(value: img)));
1870 QCOMPARE(QByteArray(pixmapValue->vtable()->className), QByteArray("VariantObject"));
1871}
1872
1873#define CHECK_TYPE_IS_NOT_VALUETYPE(Type, typeId, cppType) \
1874 QVERIFY(!QQmlValueTypeFactory::isValueType(QMetaType::Type));
1875
1876void tst_qqmlvaluetypes::nonValueTypes()
1877{
1878 CHECK_TYPE_IS_NOT_VALUETYPE(UnknownType, 0, void)
1879 QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(CHECK_TYPE_IS_NOT_VALUETYPE);
1880}
1881
1882#undef CHECK_TYPE_IS_NOT_VALUETYPE
1883
1884QTEST_MAIN(tst_qqmlvaluetypes)
1885
1886#include "tst_qqmlvaluetypes.moc"
1887

source code of qtdeclarative/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp