1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
41 #include <QtTest/QtTest>
42 #include <QtQml/qqmlcomponent.h>
43 #include <QtQml/qqmlengine.h>
44 #include <QtQml/qqmlexpression.h>
45 #include <QtQml/qqmlcontext.h>
46 #include <QtCore/qfileinfo.h>
47 #include <QtCore/qdebug.h>
48 #include <QtQml/private/qqmlguard_p.h>
49 #include <QtCore/qdir.h>
50 #include <QtCore/qnumeric.h>
51 #include <private/qqmlengine_p.h>
52 #include <private/qqmlvmemetaobject_p.h>
53 #include <private/qv4compiler_p.h>
54 #include "testtypes.h"
55 #include "testhttpserver.h"
56 #include "../../shared/util.h"
59 This test covers evaluation of ECMAScript expressions and bindings from within
60 QML. This does not include static QML language issues.
62 Static QML language issues are covered in qmllanguage
65 class tst_qqmlecmascript : public QQmlDataTest
69 tst_qqmlecmascript() {}
73 void assignBasicTypes();
74 void assignDate_data();
76 void exportDate_data();
78 void idShortcutInvalidates();
79 void boolPropertiesEvaluateAsBool();
81 void signalAssignment();
83 void basicExpressions();
84 void basicExpressions_data();
85 void arrayExpressions();
86 void contextPropertiesTriggerReeval();
87 void objectPropertiesTriggerReeval();
88 void deferredProperties();
89 void deferredPropertiesErrors();
90 void extensionObjects();
91 void overrideExtensionProperties();
92 void attachedProperties();
94 void valueTypeFunctions();
95 void constantsOverrideBindings();
96 void outerBindingOverridesInnerBinding();
97 void aliasPropertyAndBinding();
98 void aliasPropertyReset();
99 void nonExistentAttachedObject();
102 void signalParameterTypes();
103 void objectsCompareAsEqual();
104 void componentCreation_data();
105 void componentCreation();
106 void dynamicCreation_data();
107 void dynamicCreation();
108 void dynamicDestruction();
109 void objectToString();
110 void objectHasOwnProperty();
111 void selfDeletingBinding();
112 void extendedObjectPropertyLookup();
113 void extendedObjectPropertyLookup2();
115 void functionErrors();
116 void propertyAssignmentErrors();
117 void signalTriggeredBindings();
118 void listProperties();
119 void exceptionClearsOnReeval();
120 void exceptionSlotProducesWarning();
121 void exceptionBindingProducesWarning();
122 void compileInvalidBinding();
123 void transientErrors();
124 void shutdownErrors();
125 void compositePropertyType();
127 void undefinedResetsProperty();
128 void listToVariant();
129 void listAssignment();
130 void multiEngineObject();
131 void deletedObject();
132 void attachedPropertyScope();
133 void scriptConnect();
134 void scriptDisconnect();
136 void cppOwnershipReturnValue();
137 void ownershipCustomReturnValue();
138 void ownershipRootObject();
139 void ownershipConsistency();
140 void ownershipQmlIncubated();
141 void qlistqobjectMethods();
142 void strictlyEquals();
144 void numberAssignment();
145 void propertySplicing();
146 void signalWithUnknownTypes();
147 void signalWithJSValueInVariant_data();
148 void signalWithJSValueInVariant();
149 void signalWithJSValueInVariant_twoEngines_data();
150 void signalWithJSValueInVariant_twoEngines();
151 void signalWithQJSValue_data();
152 void signalWithQJSValue();
153 void moduleApi_data();
155 void importScripts_data();
156 void importScripts();
157 void scarceResources();
158 void scarceResources_data();
159 void scarceResources_other();
160 void propertyChangeSlots();
161 void propertyVar_data();
163 void propertyQJSValue_data();
164 void propertyQJSValue();
165 void propertyVarCpp();
166 void propertyVarOwnership();
167 void propertyVarImplicitOwnership();
168 void propertyVarReparent();
169 void propertyVarReparentNullContext();
170 void propertyVarCircular();
171 void propertyVarCircular2();
172 void propertyVarInheritance();
173 void propertyVarInheritance2();
174 void elementAssign();
175 void objectPassThroughSignals();
176 void objectConversion();
177 void booleanConversion();
178 void handleReferenceManagement();
180 void readonlyDeclaration();
181 void sequenceConversionRead();
182 void sequenceConversionWrite();
183 void sequenceConversionArray();
184 void sequenceConversionIndexes();
185 void sequenceConversionThreads();
186 void sequenceConversionBindings();
187 void sequenceConversionCopy();
188 void assignSequenceTypes();
191 void singleV8BindingDestroyedDuringEvaluation();
193 #ifndef QT_NO_WIDGETS
196 void dynamicCreationCrash();
197 void dynamicCreationOwnership();
199 void nullObjectBinding();
200 void deletedEngine();
201 void libraryScriptAssert();
202 void variantsAssignedUndefined();
204 void qtcreatorbug_1289();
205 void noSpuriousWarningsAtShutdown();
206 void canAssignNullToQObject();
207 void functionAssignment_fromBinding();
208 void functionAssignment_fromJS();
209 void functionAssignment_fromJS_data();
210 void functionAssignmentfromJS_invalid();
211 void functionAssignment_afterBinding();
214 void functionException();
219 void qobjectConnectionListExceptionHandling();
220 void nonscriptable();
222 void objectNameChangedSignal();
223 void destroyedSignal();
227 void sharedAttachedObject();
229 void writeRemovesBinding();
230 void aliasBindingsAssignCorrectly();
231 void aliasBindingsOverrideTarget();
232 void aliasWritesOverrideBindings();
233 void aliasToCompositeElement();
236 void urlPropertyWithEncoding();
237 void urlListPropertyWithEncoding();
238 void dynamicString();
240 void signalHandlers();
241 void doubleEvaluate();
243 void nonNotifyable();
244 void deleteWhileBindingRunning();
245 void callQtInvokables();
246 void invokableObjectArg();
247 void invokableObjectRet();
248 void invokableEnumRet();
251 void qtbug_22843_data();
253 void rewriteMultiLineStrings();
254 void revisionErrors();
256 void invokableWithQObjectDerived();
257 void realTypePrecision();
258 void registeredFlagMethod();
259 void deleteLaterObjectMethodCall();
260 void automaticSemicolon();
261 void unaryExpression();
262 void switchStatement();
263 void withStatement();
265 void replaceBinding();
266 void deleteRootObjectInCreation();
267 void onDestruction();
268 void bindingSuppression();
269 void signalEmitted();
271 void qqmldataDestroyed();
274 void overrideDataAssert();
277 static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
281 void tst_qqmlecmascript::initTestCase()
283 QQmlDataTest::initTestCase();
286 QString dataDir(dataDirectory() + QLatin1Char('/') + QLatin1String("lib"));
287 engine.addImportPath(dataDir);
290 void tst_qqmlecmascript::assignBasicTypes()
293 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.qml"));
294 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
295 QVERIFY(object != 0);
296 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
297 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
298 QCOMPARE(object->stringProperty(), QString("Hello World!"));
299 QCOMPARE(object->uintProperty(), uint(10));
300 QCOMPARE(object->intProperty(), -19);
301 QCOMPARE((float)object->realProperty(), float(23.2));
302 QCOMPARE((float)object->doubleProperty(), float(-19.75));
303 QCOMPARE((float)object->floatProperty(), float(8.5));
304 QCOMPARE(object->colorProperty(), QColor("red"));
305 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
306 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
307 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
308 QCOMPARE(object->pointProperty(), QPoint(99,13));
309 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
310 QCOMPARE(object->sizeProperty(), QSize(99, 13));
311 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
312 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
313 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
314 QCOMPARE(object->boolProperty(), true);
315 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
316 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
317 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
321 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.2.qml"));
322 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
323 QVERIFY(object != 0);
324 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
325 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
326 QCOMPARE(object->stringProperty(), QString("Hello World!"));
327 QCOMPARE(object->uintProperty(), uint(10));
328 QCOMPARE(object->intProperty(), -19);
329 QCOMPARE((float)object->realProperty(), float(23.2));
330 QCOMPARE((float)object->doubleProperty(), float(-19.75));
331 QCOMPARE((float)object->floatProperty(), float(8.5));
332 QCOMPARE(object->colorProperty(), QColor("red"));
333 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
334 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
335 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
336 QCOMPARE(object->pointProperty(), QPoint(99,13));
337 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
338 QCOMPARE(object->sizeProperty(), QSize(99, 13));
339 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
340 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
341 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
342 QCOMPARE(object->boolProperty(), true);
343 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
344 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
345 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
350 void tst_qqmlecmascript::assignDate_data()
352 QTest::addColumn<QUrl>("source");
354 QTest::newRow("Component.onComplete JS Parse") << testFileUrl("assignDate.qml");
355 QTest::newRow("Component.onComplete JS") << testFileUrl("assignDate.1.qml");
356 QTest::newRow("Binding JS") << testFileUrl("assignDate.2.qml");
357 QTest::newRow("Binding UTC") << testFileUrl("assignDate.3.qml");
358 QTest::newRow("Binding JS UTC") << testFileUrl("assignDate.4.qml");
359 QTest::newRow("Binding UTC+2") << testFileUrl("assignDate.5.qml");
360 QTest::newRow("Binding JS UTC+2 ") << testFileUrl("assignDate.6.qml");
363 void tst_qqmlecmascript::assignDate()
365 QFETCH(QUrl, source);
367 QQmlComponent component(&engine, source);
368 QScopedPointer<QObject> obj(component.create());
369 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
370 QVERIFY(object != 0);
372 // Dates received from JS are automatically converted to local time
373 QDate expectedDate(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 0), Qt::UTC).toLocalTime().date());
374 QDateTime expectedDateTime(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 1), Qt::UTC).toLocalTime());
375 QDateTime expectedDateTime2(QDateTime(QDate(2009, 5, 12), QTime(23, 59, 59), Qt::UTC).toLocalTime());
377 QCOMPARE(object->dateProperty(), expectedDate);
378 QCOMPARE(object->dateTimeProperty(), expectedDateTime);
379 QCOMPARE(object->dateTimeProperty2(), expectedDateTime2);
380 QCOMPARE(object->boolProperty(), true);
383 void tst_qqmlecmascript::exportDate_data()
385 QTest::addColumn<QUrl>("source");
386 QTest::addColumn<QDateTime>("datetime");
388 // Verify that we can export datetime information to QML and that consumers can access
389 // the data correctly provided they know the TZ info associated with the value
391 const QDate date(2009, 5, 12);
392 const QTime early(0, 0, 1);
393 const QTime late(23, 59, 59);
394 const int offset(((11 * 60) + 30) * 60);
396 QTest::newRow("Localtime early") << testFileUrl("exportDate.qml") << QDateTime(date, early, Qt::LocalTime);
397 QTest::newRow("Localtime late") << testFileUrl("exportDate.2.qml") << QDateTime(date, late, Qt::LocalTime);
398 QTest::newRow("UTC early") << testFileUrl("exportDate.3.qml") << QDateTime(date, early, Qt::UTC);
399 QTest::newRow("UTC late") << testFileUrl("exportDate.4.qml") << QDateTime(date, late, Qt::UTC);
401 QDateTime dt(date, early, Qt::OffsetFromUTC);
402 dt.setUtcOffset(offset);
403 QTest::newRow("+11:30 early") << testFileUrl("exportDate.5.qml") << dt;
406 QDateTime dt(date, late, Qt::OffsetFromUTC);
407 dt.setUtcOffset(offset);
408 QTest::newRow("+11:30 late") << testFileUrl("exportDate.6.qml") << dt;
411 QDateTime dt(date, early, Qt::OffsetFromUTC);
412 dt.setUtcOffset(-offset);
413 QTest::newRow("-11:30 early") << testFileUrl("exportDate.7.qml") << dt;
416 QDateTime dt(date, late, Qt::OffsetFromUTC);
417 dt.setUtcOffset(-offset);
418 QTest::newRow("-11:30 late") << testFileUrl("exportDate.8.qml") << dt;
422 void tst_qqmlecmascript::exportDate()
424 QFETCH(QUrl, source);
425 QFETCH(QDateTime, datetime);
427 DateTimeExporter exporter(datetime);
430 e.rootContext()->setContextProperty("datetimeExporter", &exporter);
432 QQmlComponent component(&e, source);
433 QScopedPointer<QObject> obj(component.create());
434 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
435 QVERIFY(object != 0);
436 QCOMPARE(object->boolProperty(), true);
439 void tst_qqmlecmascript::idShortcutInvalidates()
442 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.qml"));
443 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
444 QVERIFY(object != 0);
445 QVERIFY(object->objectProperty() != 0);
446 delete object->objectProperty();
447 QVERIFY(object->objectProperty() == 0);
452 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.1.qml"));
453 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
454 QVERIFY(object != 0);
455 QVERIFY(object->objectProperty() != 0);
456 delete object->objectProperty();
457 QVERIFY(object->objectProperty() == 0);
462 void tst_qqmlecmascript::boolPropertiesEvaluateAsBool()
465 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.1.qml"));
466 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
467 QVERIFY(object != 0);
468 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
472 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.2.qml"));
473 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
474 QVERIFY(object != 0);
475 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
480 void tst_qqmlecmascript::signalAssignment()
483 QQmlComponent component(&engine, testFileUrl("signalAssignment.1.qml"));
484 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
485 QVERIFY(object != 0);
486 QCOMPARE(object->string(), QString());
487 emit object->basicSignal();
488 QCOMPARE(object->string(), QString("pass"));
493 QQmlComponent component(&engine, testFileUrl("signalAssignment.2.qml"));
494 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
495 QVERIFY(object != 0);
496 QCOMPARE(object->string(), QString());
497 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
498 QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
503 QQmlComponent component(&engine, testFileUrl("signalAssignment.3.qml"));
504 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
505 QVERIFY(object != 0);
506 QCOMPARE(object->string(), QString());
507 emit object->unnamedArgumentSignal(19, 10.25, "Hello world!");
508 QEXPECT_FAIL("", "QTBUG-24481", Continue);
509 QCOMPARE(object->string(), QString("pass 19 Hello world!"));
514 QQmlComponent component(&engine, testFileUrl("signalAssignment.4.qml"));
515 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
516 QVERIFY(object != 0);
517 QCOMPARE(object->string(), QString());
518 emit object->signalWithGlobalName(19);
519 QCOMPARE(object->string(), QString("pass 5"));
524 void tst_qqmlecmascript::methods()
527 QQmlComponent component(&engine, testFileUrl("methods.1.qml"));
528 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
529 QVERIFY(object != 0);
530 QCOMPARE(object->methodCalled(), false);
531 QCOMPARE(object->methodIntCalled(), false);
532 emit object->basicSignal();
533 QCOMPARE(object->methodCalled(), true);
534 QCOMPARE(object->methodIntCalled(), false);
539 QQmlComponent component(&engine, testFileUrl("methods.2.qml"));
540 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
541 QVERIFY(object != 0);
542 QCOMPARE(object->methodCalled(), false);
543 QCOMPARE(object->methodIntCalled(), false);
544 emit object->basicSignal();
545 QCOMPARE(object->methodCalled(), false);
546 QCOMPARE(object->methodIntCalled(), true);
551 QQmlComponent component(&engine, testFileUrl("methods.3.qml"));
552 QObject *object = component.create();
553 QVERIFY(object != 0);
554 QCOMPARE(object->property("test").toInt(), 19);
559 QQmlComponent component(&engine, testFileUrl("methods.4.qml"));
560 QObject *object = component.create();
561 QVERIFY(object != 0);
562 QCOMPARE(object->property("test").toInt(), 19);
563 QCOMPARE(object->property("test2").toInt(), 17);
564 QCOMPARE(object->property("test3").toInt(), 16);
569 QQmlComponent component(&engine, testFileUrl("methods.5.qml"));
570 QObject *object = component.create();
571 QVERIFY(object != 0);
572 QCOMPARE(object->property("test").toInt(), 9);
577 void tst_qqmlecmascript::bindingLoop()
579 QQmlComponent component(&engine, testFileUrl("bindingLoop.qml"));
580 QString warning = component.url().toString() + ":5:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\"";
581 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
582 QObject *object = component.create();
583 QVERIFY(object != 0);
587 void tst_qqmlecmascript::basicExpressions_data()
589 QTest::addColumn<QString>("expression");
590 QTest::addColumn<QVariant>("result");
591 QTest::addColumn<bool>("nest");
593 QTest::newRow("Syntax error (self test)") << "{console.log({'a':1'}.a)}" << QVariant() << false;
594 QTest::newRow("Context property") << "a" << QVariant(1944) << false;
595 QTest::newRow("Context property") << "a" << QVariant(1944) << true;
596 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << false;
597 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << true;
598 QTest::newRow("Overridden context property") << "b" << QVariant("Milk") << false;
599 QTest::newRow("Overridden context property") << "b" << QVariant("Cow") << true;
600 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << false;
601 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << true;
602 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object2") << false;
603 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object3") << true;
604 QTest::newRow("Default object property") << "horseLegs" << QVariant(4) << false;
605 QTest::newRow("Default object property") << "antLegs" << QVariant(6) << false;
606 QTest::newRow("Default object property") << "emuLegs" << QVariant(2) << false;
607 QTest::newRow("Nested default object property") << "horseLegs" << QVariant(4) << true;
608 QTest::newRow("Nested default object property") << "antLegs" << QVariant(7) << true;
609 QTest::newRow("Nested default object property") << "emuLegs" << QVariant(2) << true;
610 QTest::newRow("Nested default object property") << "humanLegs" << QVariant(2) << true;
611 QTest::newRow("Context property override default object property") << "millipedeLegs" << QVariant(100) << true;
614 void tst_qqmlecmascript::basicExpressions()
616 QFETCH(QString, expression);
617 QFETCH(QVariant, result);
623 MyDefaultObject1 default1;
624 MyDefaultObject3 default3;
625 object1.setStringProperty("Object1");
626 object2.setStringProperty("Object2");
627 object3.setStringProperty("Object3");
629 QQmlContext context(engine.rootContext());
630 QQmlContext nestedContext(&context);
632 context.setContextObject(&default1);
633 context.setContextProperty("a", QVariant(1944));
634 context.setContextProperty("b", QVariant("Milk"));
635 context.setContextProperty("object", &object1);
636 context.setContextProperty("objectOverride", &object2);
637 nestedContext.setContextObject(&default3);
638 nestedContext.setContextProperty("b", QVariant("Cow"));
639 nestedContext.setContextProperty("objectOverride", &object3);
640 nestedContext.setContextProperty("millipedeLegs", QVariant(100));
642 MyExpression expr(nest?&nestedContext:&context, expression);
643 QCOMPARE(expr.evaluate(), result);
646 void tst_qqmlecmascript::arrayExpressions()
652 QQmlContext context(engine.rootContext());
653 context.setContextProperty("a", &obj1);
654 context.setContextProperty("b", &obj2);
655 context.setContextProperty("c", &obj3);
657 MyExpression expr(&context, "[a, b, c, 10]");
658 QVariant result = expr.evaluate();
659 QCOMPARE(result.userType(), qMetaTypeId<QList<QObject *> >());
660 QList<QObject *> list = qvariant_cast<QList<QObject *> >(result);
661 QCOMPARE(list.count(), 4);
662 QCOMPARE(list.at(0), &obj1);
663 QCOMPARE(list.at(1), &obj2);
664 QCOMPARE(list.at(2), &obj3);
665 QCOMPARE(list.at(3), (QObject *)0);
668 // Tests that modifying a context property will reevaluate expressions
669 void tst_qqmlecmascript::contextPropertiesTriggerReeval()
671 QQmlContext context(engine.rootContext());
674 MyQmlObject *object3 = new MyQmlObject;
676 object1.setStringProperty("Hello");
677 object2.setStringProperty("World");
679 context.setContextProperty("testProp", QVariant(1));
680 context.setContextProperty("testObj", &object1);
681 context.setContextProperty("testObj2", object3);
684 MyExpression expr(&context, "testProp + 1");
685 QCOMPARE(expr.changed, false);
686 QCOMPARE(expr.evaluate(), QVariant(2));
688 context.setContextProperty("testProp", QVariant(2));
689 QCOMPARE(expr.changed, true);
690 QCOMPARE(expr.evaluate(), QVariant(3));
694 MyExpression expr(&context, "testProp + testProp + testProp");
695 QCOMPARE(expr.changed, false);
696 QCOMPARE(expr.evaluate(), QVariant(6));
698 context.setContextProperty("testProp", QVariant(4));
699 QCOMPARE(expr.changed, true);
700 QCOMPARE(expr.evaluate(), QVariant(12));
704 MyExpression expr(&context, "testObj.stringProperty");
705 QCOMPARE(expr.changed, false);
706 QCOMPARE(expr.evaluate(), QVariant("Hello"));
708 context.setContextProperty("testObj", &object2);
709 QCOMPARE(expr.changed, true);
710 QCOMPARE(expr.evaluate(), QVariant("World"));
714 MyExpression expr(&context, "testObj.stringProperty /**/");
715 QCOMPARE(expr.changed, false);
716 QCOMPARE(expr.evaluate(), QVariant("World"));
718 context.setContextProperty("testObj", &object1);
719 QCOMPARE(expr.changed, true);
720 QCOMPARE(expr.evaluate(), QVariant("Hello"));
724 MyExpression expr(&context, "testObj2");
725 QCOMPARE(expr.changed, false);
726 QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3));
732 void tst_qqmlecmascript::objectPropertiesTriggerReeval()
734 QQmlContext context(engine.rootContext());
738 context.setContextProperty("testObj", &object1);
740 object1.setStringProperty(QLatin1String("Hello"));
741 object2.setStringProperty(QLatin1String("Dog"));
742 object3.setStringProperty(QLatin1String("Cat"));
745 MyExpression expr(&context, "testObj.stringProperty");
746 QCOMPARE(expr.changed, false);
747 QCOMPARE(expr.evaluate(), QVariant("Hello"));
749 object1.setStringProperty(QLatin1String("World"));
750 QCOMPARE(expr.changed, true);
751 QCOMPARE(expr.evaluate(), QVariant("World"));
755 MyExpression expr(&context, "testObj.objectProperty.stringProperty");
756 QCOMPARE(expr.changed, false);
757 QCOMPARE(expr.evaluate(), QVariant());
759 object1.setObjectProperty(&object2);
760 QCOMPARE(expr.changed, true);
761 expr.changed = false;
762 QCOMPARE(expr.evaluate(), QVariant("Dog"));
764 object1.setObjectProperty(&object3);
765 QCOMPARE(expr.changed, true);
766 expr.changed = false;
767 QCOMPARE(expr.evaluate(), QVariant("Cat"));
769 object1.setObjectProperty(0);
770 QCOMPARE(expr.changed, true);
771 expr.changed = false;
772 QCOMPARE(expr.evaluate(), QVariant());
774 object1.setObjectProperty(&object3);
775 QCOMPARE(expr.changed, true);
776 expr.changed = false;
777 QCOMPARE(expr.evaluate(), QVariant("Cat"));
779 object3.setStringProperty("Donkey");
780 QCOMPARE(expr.changed, true);
781 expr.changed = false;
782 QCOMPARE(expr.evaluate(), QVariant("Donkey"));
786 void tst_qqmlecmascript::deferredProperties()
788 QQmlComponent component(&engine, testFileUrl("deferredProperties.qml"));
789 MyDeferredObject *object =
790 qobject_cast<MyDeferredObject *>(component.create());
791 QVERIFY(object != 0);
792 QCOMPARE(object->value(), 0);
793 QVERIFY(object->objectProperty() == 0);
794 QVERIFY(object->objectProperty2() != 0);
795 qmlExecuteDeferred(object);
796 QCOMPARE(object->value(), 10);
797 QVERIFY(object->objectProperty() != 0);
798 MyQmlObject *qmlObject =
799 qobject_cast<MyQmlObject *>(object->objectProperty());
800 QVERIFY(qmlObject != 0);
801 QCOMPARE(qmlObject->value(), 10);
802 object->setValue(19);
803 QCOMPARE(qmlObject->value(), 19);
808 // Check errors on deferred properties are correctly emitted
809 void tst_qqmlecmascript::deferredPropertiesErrors()
811 QQmlComponent component(&engine, testFileUrl("deferredPropertiesErrors.qml"));
812 MyDeferredObject *object =
813 qobject_cast<MyDeferredObject *>(component.create());
814 QVERIFY(object != 0);
815 QCOMPARE(object->value(), 0);
816 QVERIFY(object->objectProperty() == 0);
817 QVERIFY(object->objectProperty2() == 0);
819 QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject*";
820 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
822 qmlExecuteDeferred(object);
827 void tst_qqmlecmascript::extensionObjects()
829 QQmlComponent component(&engine, testFileUrl("extensionObjects.qml"));
830 MyExtendedObject *object =
831 qobject_cast<MyExtendedObject *>(component.create());
832 QVERIFY(object != 0);
833 QCOMPARE(object->baseProperty(), 13);
834 QCOMPARE(object->coreProperty(), 9);
835 object->setProperty("extendedProperty", QVariant(11));
836 object->setProperty("baseExtendedProperty", QVariant(92));
837 QCOMPARE(object->coreProperty(), 11);
838 QCOMPARE(object->baseProperty(), 92);
840 MyExtendedObject *nested = qobject_cast<MyExtendedObject*>(qvariant_cast<QObject *>(object->property("nested")));
842 QCOMPARE(nested->baseProperty(), 13);
843 QCOMPARE(nested->coreProperty(), 9);
844 nested->setProperty("extendedProperty", QVariant(11));
845 nested->setProperty("baseExtendedProperty", QVariant(92));
846 QCOMPARE(nested->coreProperty(), 11);
847 QCOMPARE(nested->baseProperty(), 92);
852 void tst_qqmlecmascript::overrideExtensionProperties()
854 QQmlComponent component(&engine, testFileUrl("extensionObjectsPropertyOverride.qml"));
855 OverrideDefaultPropertyObject *object =
856 qobject_cast<OverrideDefaultPropertyObject *>(component.create());
857 QVERIFY(object != 0);
858 QVERIFY(object->secondProperty() != 0);
859 QVERIFY(object->firstProperty() == 0);
864 void tst_qqmlecmascript::attachedProperties()
867 QQmlComponent component(&engine, testFileUrl("attachedProperty.qml"));
868 QObject *object = component.create();
869 QVERIFY(object != 0);
870 QCOMPARE(object->property("a").toInt(), 19);
871 QCOMPARE(object->property("b").toInt(), 19);
872 QCOMPARE(object->property("c").toInt(), 19);
873 QCOMPARE(object->property("d").toInt(), 19);
878 QQmlComponent component(&engine, testFileUrl("attachedProperty.2.qml"));
879 QObject *object = component.create();
880 QVERIFY(object != 0);
881 QCOMPARE(object->property("a").toInt(), 26);
882 QCOMPARE(object->property("b").toInt(), 26);
883 QCOMPARE(object->property("c").toInt(), 26);
884 QCOMPARE(object->property("d").toInt(), 26);
890 QQmlComponent component(&engine, testFileUrl("writeAttachedProperty.qml"));
891 QObject *object = component.create();
892 QVERIFY(object != 0);
894 QMetaObject::invokeMethod(object, "writeValue2");
896 MyQmlAttachedObject *attached =
897 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
898 QVERIFY(attached != 0);
900 QCOMPARE(attached->value2(), 9);
905 void tst_qqmlecmascript::enums()
909 QQmlComponent component(&engine, testFileUrl("enums.1.qml"));
910 QObject *object = component.create();
911 QVERIFY(object != 0);
913 QCOMPARE(object->property("a").toInt(), 0);
914 QCOMPARE(object->property("b").toInt(), 1);
915 QCOMPARE(object->property("c").toInt(), 2);
916 QCOMPARE(object->property("d").toInt(), 3);
917 QCOMPARE(object->property("e").toInt(), 0);
918 QCOMPARE(object->property("f").toInt(), 1);
919 QCOMPARE(object->property("g").toInt(), 2);
920 QCOMPARE(object->property("h").toInt(), 3);
921 QCOMPARE(object->property("i").toInt(), 19);
922 QCOMPARE(object->property("j").toInt(), 19);
926 // Non-existent enums
928 QUrl file = testFileUrl("enums.2.qml");
929 QString w1 = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'MyEnum' for property 'MyUnregisteredEnumTypeObject::enumProperty'");
930 QString w2 = QLatin1String("QQmlExpression: Expression ") + testFileUrl("enums.2.qml").toString() + QLatin1String(":9 depends on non-NOTIFYable properties:");
931 QString w3 = QLatin1String(" MyUnregisteredEnumTypeObject::enumProperty");
932 QString w4 = file.toString() + ":7: Unable to assign [undefined] to int";
933 QString w5 = file.toString() + ":8: Unable to assign [undefined] to int";
934 QString w6 = file.toString() + ":9: Unable to assign [undefined] to int";
935 QString w7 = file.toString() + ":13: Unable to assign [undefined] to [unknown property type]";
936 QString w8 = file.toString() + ":31: Unable to assign int to [unknown property type]";
937 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
938 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
939 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
940 QTest::ignoreMessage(QtWarningMsg, qPrintable(w4));
941 QTest::ignoreMessage(QtWarningMsg, qPrintable(w5));
942 QTest::ignoreMessage(QtWarningMsg, qPrintable(w6));
943 QTest::ignoreMessage(QtWarningMsg, qPrintable(w7));
944 QTest::ignoreMessage(QtWarningMsg, qPrintable(w8));
946 QQmlComponent component(&engine, testFileUrl("enums.2.qml"));
947 QObject *object = component.create();
948 QVERIFY(object != 0);
949 QCOMPARE(object->property("a").toInt(), 0);
950 QCOMPARE(object->property("b").toInt(), 0);
951 QCOMPARE(object->property("c").toInt(), 0);
953 QString w9 = file.toString() + ":18: Error: Cannot assign JavaScript function to [unknown property type]";
954 QTest::ignoreMessage(QtWarningMsg, qPrintable(w9));
955 QMetaObject::invokeMethod(object, "testAssignmentOne");
957 QString w10 = file.toString() + ":21: Error: Cannot assign [undefined] to [unknown property type]";
958 QTest::ignoreMessage(QtWarningMsg, qPrintable(w10));
959 QMetaObject::invokeMethod(object, "testAssignmentTwo");
961 QString w11 = file.toString() + ":24: Error: Cannot assign [undefined] to [unknown property type]";
962 QTest::ignoreMessage(QtWarningMsg, qPrintable(w11));
963 QMetaObject::invokeMethod(object, "testAssignmentThree");
965 QString w12 = file.toString() + ":34: Error: Cannot assign int to an unregistered type";
966 QTest::ignoreMessage(QtWarningMsg, qPrintable(w12));
967 QMetaObject::invokeMethod(object, "testAssignmentFour");
973 QQmlComponent component(&engine, testFileUrl("enums.3.qml"));
974 QObject *object = component.create();
975 QVERIFY(object != 0);
977 // check the values are what we expect
978 QCOMPARE(object->property("a").toInt(), 4);
979 QCOMPARE(object->property("b").toInt(), 5);
980 QCOMPARE(object->property("c").toInt(), 9);
981 QCOMPARE(object->property("d").toInt(), 13);
982 QCOMPARE(object->property("e").toInt(), 2);
983 QCOMPARE(object->property("f").toInt(), 3);
984 QCOMPARE(object->property("h").toInt(), 2);
985 QCOMPARE(object->property("i").toInt(), 3);
987 // count of change signals
988 QCOMPARE(object->property("ac").toInt(), 0);
989 QCOMPARE(object->property("bc").toInt(), 0);
990 QCOMPARE(object->property("cc").toInt(), 0);
991 QCOMPARE(object->property("dc").toInt(), 0);
992 QCOMPARE(object->property("ec").toInt(), 0);
993 QCOMPARE(object->property("fc").toInt(), 0);
994 QCOMPARE(object->property("hc").toInt(), 1); // namespace -> binding
995 QCOMPARE(object->property("ic").toInt(), 1); // namespace -> binding
1001 void tst_qqmlecmascript::valueTypeFunctions()
1003 QQmlComponent component(&engine, testFileUrl("valueTypeFunctions.qml"));
1004 MyTypeObject *obj = qobject_cast<MyTypeObject*>(component.create());
1006 QCOMPARE(obj->rectProperty(), QRect(0,0,100,100));
1007 QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5));
1013 Tests that writing a constant to a property with a binding on it disables the
1016 void tst_qqmlecmascript::constantsOverrideBindings()
1020 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.1.qml"));
1021 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1022 QVERIFY(object != 0);
1024 QCOMPARE(object->property("c2").toInt(), 0);
1025 object->setProperty("c1", QVariant(9));
1026 QCOMPARE(object->property("c2").toInt(), 9);
1028 emit object->basicSignal();
1030 QCOMPARE(object->property("c2").toInt(), 13);
1031 object->setProperty("c1", QVariant(8));
1032 QCOMPARE(object->property("c2").toInt(), 13);
1037 // During construction
1039 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.2.qml"));
1040 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1041 QVERIFY(object != 0);
1043 QCOMPARE(object->property("c1").toInt(), 0);
1044 QCOMPARE(object->property("c2").toInt(), 10);
1045 object->setProperty("c1", QVariant(9));
1046 QCOMPARE(object->property("c1").toInt(), 9);
1047 QCOMPARE(object->property("c2").toInt(), 10);
1055 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.3.qml"));
1056 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1057 QVERIFY(object != 0);
1059 QCOMPARE(object->property("c2").toInt(), 0);
1060 object->setProperty("c1", QVariant(9));
1061 QCOMPARE(object->property("c2").toInt(), 9);
1063 object->setProperty("c2", QVariant(13));
1064 QCOMPARE(object->property("c2").toInt(), 13);
1065 object->setProperty("c1", QVariant(7));
1066 QCOMPARE(object->property("c1").toInt(), 7);
1067 QCOMPARE(object->property("c2").toInt(), 13);
1075 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.4.qml"));
1076 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1077 QVERIFY(object != 0);
1079 QCOMPARE(object->property("c1").toInt(), 0);
1080 QCOMPARE(object->property("c3").toInt(), 10);
1081 object->setProperty("c1", QVariant(9));
1082 QCOMPARE(object->property("c1").toInt(), 9);
1083 QCOMPARE(object->property("c3").toInt(), 10);
1090 Tests that assigning a binding to a property that already has a binding causes
1091 the original binding to be disabled.
1093 void tst_qqmlecmascript::outerBindingOverridesInnerBinding()
1095 QQmlComponent component(&engine,
1096 testFileUrl("outerBindingOverridesInnerBinding.qml"));
1097 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1098 QVERIFY(object != 0);
1100 QCOMPARE(object->property("c1").toInt(), 0);
1101 QCOMPARE(object->property("c2").toInt(), 0);
1102 QCOMPARE(object->property("c3").toInt(), 0);
1104 object->setProperty("c1", QVariant(9));
1105 QCOMPARE(object->property("c1").toInt(), 9);
1106 QCOMPARE(object->property("c2").toInt(), 0);
1107 QCOMPARE(object->property("c3").toInt(), 0);
1109 object->setProperty("c3", QVariant(8));
1110 QCOMPARE(object->property("c1").toInt(), 9);
1111 QCOMPARE(object->property("c2").toInt(), 8);
1112 QCOMPARE(object->property("c3").toInt(), 8);
1118 Access a non-existent attached object.
1120 Tests for a regression where this used to crash.
1122 void tst_qqmlecmascript::nonExistentAttachedObject()
1124 QQmlComponent component(&engine, testFileUrl("nonExistentAttachedObject.qml"));
1126 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString";
1127 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
1129 QObject *object = component.create();
1130 QVERIFY(object != 0);
1135 void tst_qqmlecmascript::scope()
1138 QQmlComponent component(&engine, testFileUrl("scope.qml"));
1139 QObject *object = component.create();
1140 QVERIFY(object != 0);
1142 QCOMPARE(object->property("test1").toInt(), 1);
1143 QCOMPARE(object->property("test2").toInt(), 2);
1144 QCOMPARE(object->property("test3").toString(), QString("1Test"));
1145 QCOMPARE(object->property("test4").toString(), QString("2Test"));
1146 QCOMPARE(object->property("test5").toInt(), 1);
1147 QCOMPARE(object->property("test6").toInt(), 1);
1148 QCOMPARE(object->property("test7").toInt(), 2);
1149 QCOMPARE(object->property("test8").toInt(), 2);
1150 QCOMPARE(object->property("test9").toInt(), 1);
1151 QCOMPARE(object->property("test10").toInt(), 3);
1157 QQmlComponent component(&engine, testFileUrl("scope.2.qml"));
1158 QObject *object = component.create();
1159 QVERIFY(object != 0);
1161 QCOMPARE(object->property("test1").toInt(), 19);
1162 QCOMPARE(object->property("test2").toInt(), 19);
1163 QCOMPARE(object->property("test3").toInt(), 14);
1164 QCOMPARE(object->property("test4").toInt(), 14);
1165 QCOMPARE(object->property("test5").toInt(), 24);
1166 QCOMPARE(object->property("test6").toInt(), 24);
1172 QQmlComponent component(&engine, testFileUrl("scope.3.qml"));
1173 QObject *object = component.create();
1174 QVERIFY(object != 0);
1176 QCOMPARE(object->property("test1").toBool(), true);
1177 QCOMPARE(object->property("test2").toBool(), true);
1178 QCOMPARE(object->property("test3").toBool(), true);
1183 // Signal argument scope
1185 QQmlComponent component(&engine, testFileUrl("scope.4.qml"));
1186 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1187 QVERIFY(object != 0);
1189 QCOMPARE(object->property("test").toInt(), 0);
1190 QCOMPARE(object->property("test2").toString(), QString());
1192 emit object->argumentSignal(13, "Argument Scope", 9, MyQmlObject::EnumValue4, Qt::RightButton);
1194 QCOMPARE(object->property("test").toInt(), 13);
1195 QCOMPARE(object->property("test2").toString(), QString("Argument Scope"));
1201 QQmlComponent component(&engine, testFileUrl("scope.5.qml"));
1202 QObject *object = component.create();
1203 QVERIFY(object != 0);
1205 QCOMPARE(object->property("test1").toBool(), true);
1206 QCOMPARE(object->property("test2").toBool(), true);
1212 QQmlComponent component(&engine, testFileUrl("scope.6.qml"));
1213 QObject *object = component.create();
1214 QVERIFY(object != 0);
1216 QCOMPARE(object->property("test").toBool(), true);
1222 // In 4.7, non-library javascript files that had no imports shared the imports of their
1223 // importing context
1224 void tst_qqmlecmascript::importScope()
1226 QQmlComponent component(&engine, testFileUrl("importScope.qml"));
1227 QObject *o = component.create();
1230 QCOMPARE(o->property("test").toInt(), 240);
1236 Tests that "any" type passes through a synthesized signal parameter. This
1237 is essentially a test of QQmlMetaType::copy()
1239 void tst_qqmlecmascript::signalParameterTypes()
1241 QQmlComponent component(&engine, testFileUrl("signalParameterTypes.qml"));
1242 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1243 QVERIFY(object != 0);
1245 emit object->basicSignal();
1247 QCOMPARE(object->property("intProperty").toInt(), 10);
1248 QCOMPARE(object->property("realProperty").toReal(), 19.2);
1249 QVERIFY(object->property("colorProperty").value<QColor>() == QColor(255, 255, 0, 255));
1250 QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255)));
1251 QVERIFY(object->property("enumProperty") == MyQmlObject::EnumValue3);
1252 QVERIFY(object->property("qtEnumProperty") == Qt::LeftButton);
1258 Test that two JS objects for the same QObject compare as equal.
1260 void tst_qqmlecmascript::objectsCompareAsEqual()
1262 QQmlComponent component(&engine, testFileUrl("objectsCompareAsEqual.qml"));
1263 QObject *object = component.create();
1264 QVERIFY(object != 0);
1266 QCOMPARE(object->property("test1").toBool(), true);
1267 QCOMPARE(object->property("test2").toBool(), true);
1268 QCOMPARE(object->property("test3").toBool(), true);
1269 QCOMPARE(object->property("test4").toBool(), true);
1270 QCOMPARE(object->property("test5").toBool(), true);
1276 Confirm bindings and alias properties can coexist.
1278 Tests for a regression where the binding would not reevaluate.
1280 void tst_qqmlecmascript::aliasPropertyAndBinding()
1282 QQmlComponent component(&engine, testFileUrl("aliasPropertyAndBinding.qml"));
1283 QObject *object = component.create();
1284 QVERIFY(object != 0);
1286 QCOMPARE(object->property("c2").toInt(), 3);
1287 QCOMPARE(object->property("c3").toInt(), 3);
1289 object->setProperty("c2", QVariant(19));
1291 QCOMPARE(object->property("c2").toInt(), 19);
1292 QCOMPARE(object->property("c3").toInt(), 19);
1298 Ensure that we can write undefined value to an alias property,
1299 and that the aliased property is reset correctly if possible.
1301 void tst_qqmlecmascript::aliasPropertyReset()
1303 QObject *object = 0;
1305 // test that a manual write (of undefined) to a resettable aliased property succeeds
1306 QQmlComponent c1(&engine, testFileUrl("aliasreset/aliasPropertyReset.1.qml"));
1307 object = c1.create();
1308 QVERIFY(object != 0);
1309 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1310 QCOMPARE(object->property("aliasIsUndefined"), QVariant(false));
1311 QMetaObject::invokeMethod(object, "resetAliased");
1312 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1313 QCOMPARE(object->property("aliasIsUndefined"), QVariant(true));
1316 // test that a manual write (of undefined) to a resettable alias property succeeds
1317 QQmlComponent c2(&engine, testFileUrl("aliasreset/aliasPropertyReset.2.qml"));
1318 object = c2.create();
1319 QVERIFY(object != 0);
1320 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1321 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false));
1322 QMetaObject::invokeMethod(object, "resetAlias");
1323 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1324 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(true));
1327 // test that an alias to a bound property works correctly
1328 QQmlComponent c3(&engine, testFileUrl("aliasreset/aliasPropertyReset.3.qml"));
1329 object = c3.create();
1330 QVERIFY(object != 0);
1331 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1332 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false));
1333 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1334 QMetaObject::invokeMethod(object, "resetAlias");
1335 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1336 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(true));
1337 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1340 // test that a manual write (of undefined) to a resettable alias property
1341 // whose aliased property's object has been deleted, does not crash.
1342 QQmlComponent c4(&engine, testFileUrl("aliasreset/aliasPropertyReset.4.qml"));
1343 object = c4.create();
1344 QVERIFY(object != 0);
1345 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1346 QObject *loader = object->findChild<QObject*>("loader");
1347 QVERIFY(loader != 0);
1349 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // deletion should have caused value unset.
1350 QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash.
1351 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1352 QMetaObject::invokeMethod(object, "setAlias"); // shouldn't crash, and shouldn't change value (since it's no longer referencing anything).
1353 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1356 // test that binding an alias property to an undefined value works correctly
1357 QQmlComponent c5(&engine, testFileUrl("aliasreset/aliasPropertyReset.5.qml"));
1358 object = c5.create();
1359 QVERIFY(object != 0);
1360 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // bound to undefined value.
1363 // test that a manual write (of undefined) to a non-resettable property fails properly
1364 QUrl url = testFileUrl("aliasreset/aliasPropertyReset.error.1.qml");
1365 QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int");
1366 QQmlComponent e1(&engine, url);
1367 object = e1.create();
1368 QVERIFY(object != 0);
1369 QCOMPARE(object->property("intAlias").value<int>(), 12);
1370 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1371 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1372 QMetaObject::invokeMethod(object, "resetAlias");
1373 QCOMPARE(object->property("intAlias").value<int>(), 12);
1374 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1378 void tst_qqmlecmascript::componentCreation_data()
1380 QTest::addColumn<QString>("method");
1381 QTest::addColumn<QString>("creationError");
1382 QTest::addColumn<QString>("createdParent");
1384 QTest::newRow("url")
1388 QTest::newRow("urlMode")
1392 QTest::newRow("urlParent")
1396 QTest::newRow("urlNullParent")
1400 QTest::newRow("urlModeParent")
1404 QTest::newRow("urlModeNullParent")
1405 << "urlModeNullParent"
1408 QTest::newRow("invalidSecondArg")
1409 << "invalidSecondArg"
1410 << ":40: Error: Qt.createComponent(): Invalid arguments"
1412 QTest::newRow("invalidThirdArg")
1413 << "invalidThirdArg"
1414 << ":45: Error: Qt.createComponent(): Invalid parent object"
1416 QTest::newRow("invalidMode")
1418 << ":50: Error: Qt.createComponent(): Invalid arguments"
1423 Test using createComponent to dynamically generate a component.
1425 void tst_qqmlecmascript::componentCreation()
1427 QFETCH(QString, method);
1428 QFETCH(QString, creationError);
1429 QFETCH(QString, createdParent);
1431 QUrl testUrl(testFileUrl("componentCreation.qml"));
1433 if (!creationError.isEmpty()) {
1434 QString warning = testUrl.toString() + creationError;
1435 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1438 QQmlComponent component(&engine, testUrl);
1439 MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create());
1440 QVERIFY(object != 0);
1442 QMetaObject::invokeMethod(object, method.toUtf8());
1443 QQmlComponent *created = object->componentProperty();
1445 if (creationError.isEmpty()) {
1448 QObject *expectedParent;
1449 if (createdParent == QLatin1String("obj")) {
1450 expectedParent = object;
1451 } else if ((createdParent == QLatin1String("null")) || createdParent.isEmpty()) {
1454 QCOMPARE(created->parent(), expectedParent);
1458 void tst_qqmlecmascript::dynamicCreation_data()
1460 QTest::addColumn<QString>("method");
1461 QTest::addColumn<QString>("createdName");
1463 QTest::newRow("One") << "createOne" << "objectOne";
1464 QTest::newRow("Two") << "createTwo" << "objectTwo";
1465 QTest::newRow("Three") << "createThree" << "objectThree";
1469 Test using createQmlObject to dynamically generate an item
1470 Also using createComponent is tested.
1472 void tst_qqmlecmascript::dynamicCreation()
1474 QFETCH(QString, method);
1475 QFETCH(QString, createdName);
1477 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
1478 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1479 QVERIFY(object != 0);
1481 QMetaObject::invokeMethod(object, method.toUtf8());
1482 QObject *created = object->objectProperty();
1484 QCOMPARE(created->objectName(), createdName);
1490 Tests the destroy function
1492 void tst_qqmlecmascript::dynamicDestruction()
1495 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.qml"));
1496 QQmlGuard<MyQmlObject> object = qobject_cast<MyQmlObject*>(component.create());
1497 QVERIFY(object != 0);
1498 QQmlGuard<QObject> createdQmlObject = 0;
1500 QMetaObject::invokeMethod(object, "create");
1501 createdQmlObject = object->objectProperty();
1502 QVERIFY(createdQmlObject);
1503 QCOMPARE(createdQmlObject->objectName(), QString("emptyObject"));
1505 QMetaObject::invokeMethod(object, "killOther");
1506 QVERIFY(createdQmlObject);
1508 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1509 QCoreApplication::processEvents();
1510 QVERIFY(createdQmlObject);
1511 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1512 if (createdQmlObject) {
1514 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1515 QCoreApplication::processEvents();
1518 QVERIFY(!createdQmlObject);
1520 QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
1521 QMetaObject::invokeMethod(object, "killMe");
1523 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1524 QCoreApplication::processEvents();
1529 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.2.qml"));
1530 QObject *o = component.create();
1533 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1535 QMetaObject::invokeMethod(o, "create");
1537 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) != 0);
1539 QMetaObject::invokeMethod(o, "destroy");
1541 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1542 QCoreApplication::processEvents();
1544 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1551 QQmlGuard<QObject> createdQmlObject = 0;
1552 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.3.qml"));
1553 QObject *o = component.create();
1555 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1556 QMetaObject::invokeMethod(o, "create");
1557 createdQmlObject = qvariant_cast<QObject*>(o->property("objectProperty"));
1558 QVERIFY(createdQmlObject);
1559 QMetaObject::invokeMethod(o, "destroy");
1560 QVERIFY(qvariant_cast<bool>(o->property("test")) == false);
1561 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1563 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1564 QCoreApplication::processEvents();
1566 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1567 QVERIFY(qvariant_cast<bool>(o->property("test")) == true);
1573 tests that id.toString() works
1575 void tst_qqmlecmascript::objectToString()
1577 QQmlComponent component(&engine, testFileUrl("qmlToString.qml"));
1578 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1579 QVERIFY(object != 0);
1580 QMetaObject::invokeMethod(object, "testToString");
1581 QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_"));
1582 QVERIFY(object->stringProperty().endsWith(", \"objName\")"));
1588 tests that id.hasOwnProperty() works
1590 void tst_qqmlecmascript::objectHasOwnProperty()
1592 QUrl url = testFileUrl("qmlHasOwnProperty.qml");
1593 QString warning1 = url.toString() + ":59: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1594 QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1595 QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1597 QQmlComponent component(&engine, url);
1598 QObject *object = component.create();
1599 QVERIFY(object != 0);
1601 // test QObjects in QML
1602 QMetaObject::invokeMethod(object, "testHasOwnPropertySuccess");
1603 QVERIFY(object->property("result").value<bool>() == true);
1604 QMetaObject::invokeMethod(object, "testHasOwnPropertyFailure");
1605 QVERIFY(object->property("result").value<bool>() == false);
1607 // now test other types in QML
1608 QObject *child = object->findChild<QObject*>("typeObj");
1609 QVERIFY(child != 0);
1610 QMetaObject::invokeMethod(child, "testHasOwnPropertySuccess");
1611 QCOMPARE(child->property("valueTypeHasOwnProperty").toBool(), true);
1612 QCOMPARE(child->property("valueTypeHasOwnProperty2").toBool(), true);
1613 QCOMPARE(child->property("variantTypeHasOwnProperty").toBool(), true);
1614 QCOMPARE(child->property("stringTypeHasOwnProperty").toBool(), true);
1615 QCOMPARE(child->property("listTypeHasOwnProperty").toBool(), true);
1616 QCOMPARE(child->property("emptyListTypeHasOwnProperty").toBool(), true);
1617 QCOMPARE(child->property("enumTypeHasOwnProperty").toBool(), true);
1618 QCOMPARE(child->property("typenameHasOwnProperty").toBool(), true);
1619 QCOMPARE(child->property("typenameHasOwnProperty2").toBool(), true);
1620 QCOMPARE(child->property("moduleApiTypeHasOwnProperty").toBool(), true);
1621 QCOMPARE(child->property("moduleApiPropertyTypeHasOwnProperty").toBool(), true);
1623 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1624 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureOne");
1625 QCOMPARE(child->property("enumNonValueHasOwnProperty").toBool(), false);
1626 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1627 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureTwo");
1628 QCOMPARE(child->property("moduleApiNonPropertyHasOwnProperty").toBool(), false);
1629 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1630 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureThree");
1631 QCOMPARE(child->property("listAtInvalidHasOwnProperty").toBool(), false);
1637 Tests bindings that indirectly cause their own deletion work.
1639 This test is best run under valgrind to ensure no invalid memory access occur.
1641 void tst_qqmlecmascript::selfDeletingBinding()
1644 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.qml"));
1645 QObject *object = component.create();
1646 QVERIFY(object != 0);
1647 object->setProperty("triggerDelete", true);
1652 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.2.qml"));
1653 QObject *object = component.create();
1654 QVERIFY(object != 0);
1655 object->setProperty("triggerDelete", true);
1661 Test that extended object properties can be accessed.
1663 This test a regression where this used to crash. The issue was specificially
1664 for extended objects that did not include a synthesized meta object (so non-root
1665 and no synthesiszed properties).
1667 void tst_qqmlecmascript::extendedObjectPropertyLookup()
1669 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup.qml"));
1670 QObject *object = component.create();
1671 QVERIFY(object != 0);
1676 Test that extended object properties can be accessed correctly.
1678 void tst_qqmlecmascript::extendedObjectPropertyLookup2()
1680 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup2.qml"));
1681 QObject *object = component.create();
1682 QVERIFY(object != 0);
1684 QVariant returnValue;
1685 QVERIFY(QMetaObject::invokeMethod(object, "getValue", Q_RETURN_ARG(QVariant, returnValue)));
1686 QCOMPARE(returnValue.toInt(), 42);
1691 Test file/lineNumbers for binding/Script errors.
1693 void tst_qqmlecmascript::scriptErrors()
1695 QQmlComponent component(&engine, testFileUrl("scriptErrors.qml"));
1696 QString url = component.url().toString();
1698 QString warning1 = url.left(url.length() - 3) + "js:2: Error: Invalid write to global property \"a\"";
1699 QString warning2 = url + ":5: ReferenceError: a is not defined";
1700 QString warning3 = url.left(url.length() - 3) + "js:4: Error: Invalid write to global property \"a\"";
1701 QString warning4 = url + ":13: ReferenceError: a is not defined";
1702 QString warning5 = url + ":11: ReferenceError: a is not defined";
1703 QString warning6 = url + ":10: Unable to assign [undefined] to int";
1704 QString warning7 = url + ":15: Error: Cannot assign to read-only property \"trueProperty\"";
1705 QString warning8 = url + ":16: Error: Cannot assign to non-existent property \"fakeProperty\"";
1707 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1708 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1709 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1710 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
1711 QTest::ignoreMessage(QtWarningMsg, warning6.toLatin1().constData());
1712 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1713 QVERIFY(object != 0);
1715 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
1716 emit object->basicSignal();
1718 QTest::ignoreMessage(QtWarningMsg, warning7.toLatin1().constData());
1719 emit object->anotherBasicSignal();
1721 QTest::ignoreMessage(QtWarningMsg, warning8.toLatin1().constData());
1722 emit object->thirdBasicSignal();
1728 Test file/lineNumbers for inline functions.
1730 void tst_qqmlecmascript::functionErrors()
1732 QQmlComponent component(&engine, testFileUrl("functionErrors.qml"));
1733 QString url = component.url().toString();
1735 QString warning = url + ":5: Error: Invalid write to global property \"a\"";
1737 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1739 QObject *object = component.create();
1740 QVERIFY(object != 0);
1743 // test that if an exception occurs while invoking js function from cpp, it is reported as expected.
1744 QQmlComponent componentTwo(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
1745 url = componentTwo.url().toString();
1746 object = componentTwo.create();
1747 QVERIFY(object != 0);
1749 QString srpname = object->property("srp_name").toString();
1751 warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srpname
1752 + QLatin1String(" is not a function");
1753 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed.
1754 QMetaObject::invokeMethod(object, "retrieveScarceResource");
1759 Test various errors that can occur when assigning a property from script
1761 void tst_qqmlecmascript::propertyAssignmentErrors()
1763 QQmlComponent component(&engine, testFileUrl("propertyAssignmentErrors.qml"));
1765 QString url = component.url().toString();
1767 QObject *object = component.create();
1768 QVERIFY(object != 0);
1770 QCOMPARE(object->property("test1").toBool(), true);
1771 QCOMPARE(object->property("test2").toBool(), true);
1777 Test bindings still work when the reeval is triggered from within
1780 void tst_qqmlecmascript::signalTriggeredBindings()
1782 QQmlComponent component(&engine, testFileUrl("signalTriggeredBindings.qml"));
1783 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1784 QVERIFY(object != 0);
1786 QCOMPARE(object->property("base").toReal(), 50.);
1787 QCOMPARE(object->property("test1").toReal(), 50.);
1788 QCOMPARE(object->property("test2").toReal(), 50.);
1790 object->basicSignal();
1792 QCOMPARE(object->property("base").toReal(), 200.);
1793 QCOMPARE(object->property("test1").toReal(), 200.);
1794 QCOMPARE(object->property("test2").toReal(), 200.);
1796 object->argumentSignal(10, QString(), 10, MyQmlObject::EnumValue4, Qt::RightButton);
1798 QCOMPARE(object->property("base").toReal(), 400.);
1799 QCOMPARE(object->property("test1").toReal(), 400.);
1800 QCOMPARE(object->property("test2").toReal(), 400.);
1806 Test that list properties can be iterated from ECMAScript
1808 void tst_qqmlecmascript::listProperties()
1810 QQmlComponent component(&engine, testFileUrl("listProperties.qml"));
1811 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1812 QVERIFY(object != 0);
1814 QCOMPARE(object->property("test1").toInt(), 21);
1815 QCOMPARE(object->property("test2").toInt(), 2);
1816 QCOMPARE(object->property("test3").toBool(), true);
1817 QCOMPARE(object->property("test4").toBool(), true);
1822 void tst_qqmlecmascript::exceptionClearsOnReeval()
1824 QQmlComponent component(&engine, testFileUrl("exceptionClearsOnReeval.qml"));
1825 QString url = component.url().toString();
1827 QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null";
1829 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1830 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1831 QVERIFY(object != 0);
1833 QCOMPARE(object->property("test").toBool(), false);
1835 MyQmlObject object2;
1836 MyQmlObject object3;
1837 object2.setObjectProperty(&object3);
1838 object->setObjectProperty(&object2);
1840 QCOMPARE(object->property("test").toBool(), true);
1845 void tst_qqmlecmascript::exceptionSlotProducesWarning()
1847 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning.qml"));
1848 QString url = component.url().toString();
1850 QString warning = component.url().toString() + ":6: Error: JS exception";
1852 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1853 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1854 QVERIFY(object != 0);
1858 void tst_qqmlecmascript::exceptionBindingProducesWarning()
1860 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning2.qml"));
1861 QString url = component.url().toString();
1863 QString warning = component.url().toString() + ":5: Error: JS exception";
1865 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1866 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1867 QVERIFY(object != 0);
1871 void tst_qqmlecmascript::compileInvalidBinding()
1873 // QTBUG-23387: ensure that invalid bindings don't cause a crash.
1874 QQmlComponent component(&engine, testFileUrl("v8bindingException.qml"));
1875 QObject *object = component.create();
1876 QVERIFY(object != 0);
1880 static int transientErrorsMsgCount = 0;
1881 static void transientErrorsMsgHandler(QtMsgType, const char *)
1883 ++transientErrorsMsgCount;
1886 // Check that transient binding errors are not displayed
1887 void tst_qqmlecmascript::transientErrors()
1890 QQmlComponent component(&engine, testFileUrl("transientErrors.qml"));
1892 transientErrorsMsgCount = 0;
1893 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1895 QObject *object = component.create();
1896 QVERIFY(object != 0);
1898 qInstallMsgHandler(old);
1900 QCOMPARE(transientErrorsMsgCount, 0);
1905 // One binding erroring multiple times, but then resolving
1907 QQmlComponent component(&engine, testFileUrl("transientErrors.2.qml"));
1909 transientErrorsMsgCount = 0;
1910 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1912 QObject *object = component.create();
1913 QVERIFY(object != 0);
1915 qInstallMsgHandler(old);
1917 QCOMPARE(transientErrorsMsgCount, 0);
1923 // Check that errors during shutdown are minimized
1924 void tst_qqmlecmascript::shutdownErrors()
1926 QQmlComponent component(&engine, testFileUrl("shutdownErrors.qml"));
1927 QObject *object = component.create();
1928 QVERIFY(object != 0);
1930 transientErrorsMsgCount = 0;
1931 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1935 qInstallMsgHandler(old);
1936 QCOMPARE(transientErrorsMsgCount, 0);
1939 void tst_qqmlecmascript::compositePropertyType()
1941 QQmlComponent component(&engine, testFileUrl("compositePropertyType.qml"));
1943 QTest::ignoreMessage(QtDebugMsg, "hello world");
1944 QObject *object = qobject_cast<QObject *>(component.create());
1949 void tst_qqmlecmascript::jsObject()
1951 QQmlComponent component(&engine, testFileUrl("jsObject.qml"));
1952 QObject *object = component.create();
1953 QVERIFY(object != 0);
1955 QCOMPARE(object->property("test").toInt(), 92);
1960 void tst_qqmlecmascript::undefinedResetsProperty()
1963 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.qml"));
1964 QObject *object = component.create();
1965 QVERIFY(object != 0);
1967 QCOMPARE(object->property("resettableProperty").toInt(), 92);
1969 object->setProperty("setUndefined", true);
1971 QCOMPARE(object->property("resettableProperty").toInt(), 13);
1973 object->setProperty("setUndefined", false);
1975 QCOMPARE(object->property("resettableProperty").toInt(), 92);
1980 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.2.qml"));
1981 QObject *object = component.create();
1982 QVERIFY(object != 0);
1984 QCOMPARE(object->property("resettableProperty").toInt(), 19);
1986 QMetaObject::invokeMethod(object, "doReset");
1988 QCOMPARE(object->property("resettableProperty").toInt(), 13);
1994 // Aliases to variant properties should work
1995 void tst_qqmlecmascript::qtbug_22464()
1997 QQmlComponent component(&engine, testFileUrl("qtbug_22464.qml"));
1998 QObject *object = component.create();
1999 QVERIFY(object != 0);
2001 QCOMPARE(object->property("test").toBool(), true);
2006 void tst_qqmlecmascript::qtbug_21580()
2008 QQmlComponent component(&engine, testFileUrl("qtbug_21580.qml"));
2010 QObject *object = component.create();
2011 QVERIFY(object != 0);
2013 QCOMPARE(object->property("test").toBool(), true);
2018 // Causes a v8 binding, but not all v8 bindings to be destroyed during evaluation
2019 void tst_qqmlecmascript::singleV8BindingDestroyedDuringEvaluation()
2021 QQmlComponent component(&engine, testFileUrl("singleV8BindingDestroyedDuringEvaluation.qml"));
2023 QObject *object = component.create();
2024 QVERIFY(object != 0);
2029 void tst_qqmlecmascript::bug1()
2031 QQmlComponent component(&engine, testFileUrl("bug.1.qml"));
2032 QObject *object = component.create();
2033 QVERIFY(object != 0);
2035 QCOMPARE(object->property("test").toInt(), 14);
2037 object->setProperty("a", 11);
2039 QCOMPARE(object->property("test").toInt(), 3);
2041 object->setProperty("b", true);
2043 QCOMPARE(object->property("test").toInt(), 9);
2048 #ifndef QT_NO_WIDGETS
2049 void tst_qqmlecmascript::bug2()
2051 QQmlComponent component(&engine);
2052 component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl());
2054 QObject *object = component.create();
2055 QVERIFY(object != 0);
2061 // Don't crash in createObject when the component has errors.
2062 void tst_qqmlecmascript::dynamicCreationCrash()
2064 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
2065 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2066 QVERIFY(object != 0);
2068 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2069 QMetaObject::invokeMethod(object, "dontCrash");
2070 QObject *created = object->objectProperty();
2071 QVERIFY(created == 0);
2076 // ownership transferred to JS, ensure that GC runs the dtor
2077 void tst_qqmlecmascript::dynamicCreationOwnership()
2080 int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
2082 // allow the engine to go out of scope too.
2084 QQmlEngine dcoEngine;
2085 QQmlComponent component(&dcoEngine, testFileUrl("dynamicCreationOwnership.qml"));
2086 QObject *object = component.create();
2087 QVERIFY(object != 0);
2088 MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo");
2089 QVERIFY(mdcdo != 0);
2090 mdcdo->setDtorCount(&dtorCount);
2092 for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
2093 QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
2095 // we do this once manually, but it should be done automatically
2096 // when the engine goes out of scope (since it should gc in dtor)
2097 QMetaObject::invokeMethod(object, "performGc");
2100 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2101 QCoreApplication::processEvents();
2107 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2108 QCoreApplication::processEvents();
2109 QCOMPARE(dtorCount, expectedDtorCount);
2112 void tst_qqmlecmascript::regExpBug()
2116 QQmlComponent component(&engine, testFileUrl("regExp.qml"));
2117 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2118 QVERIFY(object != 0);
2119 QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]"));
2125 QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString());
2126 QQmlComponent component(&engine, testFileUrl("regExp.2.qml"));
2127 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2128 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2130 QCOMPARE(component.errorString(), err);
2134 static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source)
2136 QString functionSource = QLatin1String("(function(object) { return ") +
2137 QLatin1String(source) + QLatin1String(" })");
2139 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2142 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2143 if (function.IsEmpty())
2145 v8::Handle<v8::Value> args[] = { o };
2146 function->Call(engine->global(), 1, args);
2147 return tc.HasCaught();
2150 static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o,
2151 const char *source, v8::Handle<v8::Value> result)
2153 QString functionSource = QLatin1String("(function(object) { return ") +
2154 QLatin1String(source) + QLatin1String(" })");
2156 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2159 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2160 if (function.IsEmpty())
2162 v8::Handle<v8::Value> args[] = { o };
2164 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2169 return value->StrictEquals(result);
2172 static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o,
2175 QString functionSource = QLatin1String("(function(object) { return ") +
2176 QLatin1String(source) + QLatin1String(" })");
2178 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2180 return v8::Handle<v8::Value>();
2181 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2182 if (function.IsEmpty())
2183 return v8::Handle<v8::Value>();
2184 v8::Handle<v8::Value> args[] = { o };
2186 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2189 return v8::Handle<v8::Value>();
2193 #define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
2194 #define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result)
2195 #define EVALUATE(source) evaluate(engine, object, source)
2197 void tst_qqmlecmascript::callQtInvokables()
2199 MyInvokableObject o;
2201 QQmlEngine qmlengine;
2202 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine);
2204 QV8Engine *engine = ep->v8engine();
2206 v8::HandleScope handle_scope;
2207 v8::Context::Scope scope(engine->context());
2209 v8::Local<v8::Object> object = engine->newQObject(&o)->ToObject();
2211 // Non-existent methods
2213 QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
2214 QCOMPARE(o.error(), false);
2215 QCOMPARE(o.invoked(), -1);
2216 QCOMPARE(o.actuals().count(), 0);
2219 QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)"));
2220 QCOMPARE(o.error(), false);
2221 QCOMPARE(o.invoked(), -1);
2222 QCOMPARE(o.actuals().count(), 0);
2224 // Insufficient arguments
2226 QVERIFY(EVALUATE_ERROR("object.method_int()"));
2227 QCOMPARE(o.error(), false);
2228 QCOMPARE(o.invoked(), -1);
2229 QCOMPARE(o.actuals().count(), 0);
2232 QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
2233 QCOMPARE(o.error(), false);
2234 QCOMPARE(o.invoked(), -1);
2235 QCOMPARE(o.actuals().count(), 0);
2237 // Excessive arguments
2239 QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", v8::Undefined()));
2240 QCOMPARE(o.error(), false);
2241 QCOMPARE(o.invoked(), 8);
2242 QCOMPARE(o.actuals().count(), 1);
2243 QCOMPARE(o.actuals().at(0), QVariant(10));
2246 QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", v8::Undefined()));
2247 QCOMPARE(o.error(), false);
2248 QCOMPARE(o.invoked(), 9);
2249 QCOMPARE(o.actuals().count(), 2);
2250 QCOMPARE(o.actuals().at(0), QVariant(10));
2251 QCOMPARE(o.actuals().at(1), QVariant(11));
2253 // Test return types
2255 QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", v8::Undefined()));
2256 QCOMPARE(o.error(), false);
2257 QCOMPARE(o.invoked(), 0);
2258 QCOMPARE(o.actuals().count(), 0);
2261 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", v8::Integer::New(6)));
2262 QCOMPARE(o.error(), false);
2263 QCOMPARE(o.invoked(), 1);
2264 QCOMPARE(o.actuals().count(), 0);
2267 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", v8::Number::New(19.75)));
2268 QCOMPARE(o.error(), false);
2269 QCOMPARE(o.invoked(), 2);
2270 QCOMPARE(o.actuals().count(), 0);
2274 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QPointF()");
2275 QVERIFY(!ret.IsEmpty());
2276 QCOMPARE(engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5)));
2277 QCOMPARE(o.error(), false);
2278 QCOMPARE(o.invoked(), 3);
2279 QCOMPARE(o.actuals().count(), 0);
2284 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QObject()");
2285 QCOMPARE(engine->toQObject(ret), (QObject *)&o);
2286 QCOMPARE(o.error(), false);
2287 QCOMPARE(o.invoked(), 4);
2288 QCOMPARE(o.actuals().count(), 0);
2292 QVERIFY(EVALUATE_ERROR("object.method_NoArgs_unknown()"));
2293 QCOMPARE(o.error(), false);
2294 QCOMPARE(o.invoked(), -1);
2295 QCOMPARE(o.actuals().count(), 0);
2299 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QScriptValue()");
2300 QVERIFY(ret->IsString());
2301 QCOMPARE(engine->toString(ret), QString("Hello world"));
2302 QCOMPARE(o.error(), false);
2303 QCOMPARE(o.invoked(), 6);
2304 QCOMPARE(o.actuals().count(), 0);
2308 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks")));
2309 QCOMPARE(o.error(), false);
2310 QCOMPARE(o.invoked(), 7);
2311 QCOMPARE(o.actuals().count(), 0);
2315 QVERIFY(EVALUATE_VALUE("object.method_int(94)", v8::Undefined()));
2316 QCOMPARE(o.error(), false);
2317 QCOMPARE(o.invoked(), 8);
2318 QCOMPARE(o.actuals().count(), 1);
2319 QCOMPARE(o.actuals().at(0), QVariant(94));
2322 QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", v8::Undefined()));
2323 QCOMPARE(o.error(), false);
2324 QCOMPARE(o.invoked(), 8);
2325 QCOMPARE(o.actuals().count(), 1);
2326 QCOMPARE(o.actuals().at(0), QVariant(94));
2329 QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", v8::Undefined()));
2330 QCOMPARE(o.error(), false);
2331 QCOMPARE(o.invoked(), 8);
2332 QCOMPARE(o.actuals().count(), 1);
2333 QCOMPARE(o.actuals().at(0), QVariant(0));
2336 QVERIFY(EVALUATE_VALUE("object.method_int(null)", v8::Undefined()));
2337 QCOMPARE(o.error(), false);
2338 QCOMPARE(o.invoked(), 8);
2339 QCOMPARE(o.actuals().count(), 1);
2340 QCOMPARE(o.actuals().at(0), QVariant(0));
2343 QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", v8::Undefined()));
2344 QCOMPARE(o.error(), false);
2345 QCOMPARE(o.invoked(), 8);
2346 QCOMPARE(o.actuals().count(), 1);
2347 QCOMPARE(o.actuals().at(0), QVariant(0));
2350 QVERIFY(EVALUATE_VALUE("object.method_int(object)", v8::Undefined()));
2351 QCOMPARE(o.error(), false);
2352 QCOMPARE(o.invoked(), 8);
2353 QCOMPARE(o.actuals().count(), 1);
2354 QCOMPARE(o.actuals().at(0), QVariant(0));
2357 QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", v8::Undefined()));
2358 QCOMPARE(o.error(), false);
2359 QCOMPARE(o.invoked(), 9);
2360 QCOMPARE(o.actuals().count(), 2);
2361 QCOMPARE(o.actuals().at(0), QVariant(122));
2362 QCOMPARE(o.actuals().at(1), QVariant(9));
2365 QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", v8::Undefined()));
2366 QCOMPARE(o.error(), false);
2367 QCOMPARE(o.invoked(), 10);
2368 QCOMPARE(o.actuals().count(), 1);
2369 QCOMPARE(o.actuals().at(0), QVariant(94.3));
2372 QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", v8::Undefined()));
2373 QCOMPARE(o.error(), false);
2374 QCOMPARE(o.invoked(), 10);
2375 QCOMPARE(o.actuals().count(), 1);
2376 QCOMPARE(o.actuals().at(0), QVariant(94.3));
2379 QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", v8::Undefined()));
2380 QCOMPARE(o.error(), false);
2381 QCOMPARE(o.invoked(), 10);
2382 QCOMPARE(o.actuals().count(), 1);
2383 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2386 QVERIFY(EVALUATE_VALUE("object.method_real(null)", v8::Undefined()));
2387 QCOMPARE(o.error(), false);
2388 QCOMPARE(o.invoked(), 10);
2389 QCOMPARE(o.actuals().count(), 1);
2390 QCOMPARE(o.actuals().at(0), QVariant(0));
2393 QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", v8::Undefined()));
2394 QCOMPARE(o.error(), false);
2395 QCOMPARE(o.invoked(), 10);
2396 QCOMPARE(o.actuals().count(), 1);
2397 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2400 QVERIFY(EVALUATE_VALUE("object.method_real(object)", v8::Undefined()));
2401 QCOMPARE(o.error(), false);
2402 QCOMPARE(o.invoked(), 10);
2403 QCOMPARE(o.actuals().count(), 1);
2404 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2407 QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", v8::Undefined()));
2408 QCOMPARE(o.error(), false);
2409 QCOMPARE(o.invoked(), 11);
2410 QCOMPARE(o.actuals().count(), 1);
2411 QCOMPARE(o.actuals().at(0), QVariant("Hello world"));
2414 QVERIFY(EVALUATE_VALUE("object.method_QString(19)", v8::Undefined()));
2415 QCOMPARE(o.error(), false);
2416 QCOMPARE(o.invoked(), 11);
2417 QCOMPARE(o.actuals().count(), 1);
2418 QCOMPARE(o.actuals().at(0), QVariant("19"));
2422 QString expected = "MyInvokableObject(0x" + QString::number((quintptr)&o, 16) + ")";
2423 QVERIFY(EVALUATE_VALUE("object.method_QString(object)", v8::Undefined()));
2424 QCOMPARE(o.error(), false);
2425 QCOMPARE(o.invoked(), 11);
2426 QCOMPARE(o.actuals().count(), 1);
2427 QCOMPARE(o.actuals().at(0), QVariant(expected));
2431 QVERIFY(EVALUATE_VALUE("object.method_QString(null)", v8::Undefined()));
2432 QCOMPARE(o.error(), false);
2433 QCOMPARE(o.invoked(), 11);
2434 QCOMPARE(o.actuals().count(), 1);
2435 QCOMPARE(o.actuals().at(0), QVariant(QString()));
2438 QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", v8::Undefined()));
2439 QCOMPARE(o.error(), false);
2440 QCOMPARE(o.invoked(), 11);
2441 QCOMPARE(o.actuals().count(), 1);
2442 QCOMPARE(o.actuals().at(0), QVariant(QString()));
2445 QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", v8::Undefined()));
2446 QCOMPARE(o.error(), false);
2447 QCOMPARE(o.invoked(), 12);
2448 QCOMPARE(o.actuals().count(), 1);
2449 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2452 QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", v8::Undefined()));
2453 QCOMPARE(o.error(), false);
2454 QCOMPARE(o.invoked(), 12);
2455 QCOMPARE(o.actuals().count(), 1);
2456 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2459 QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", v8::Undefined()));
2460 QCOMPARE(o.error(), false);
2461 QCOMPARE(o.invoked(), 12);
2462 QCOMPARE(o.actuals().count(), 1);
2463 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2466 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", v8::Undefined()));
2467 QCOMPARE(o.error(), false);
2468 QCOMPARE(o.invoked(), 12);
2469 QCOMPARE(o.actuals().count(), 1);
2470 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2473 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", v8::Undefined()));
2474 QCOMPARE(o.error(), false);
2475 QCOMPARE(o.invoked(), 12);
2476 QCOMPARE(o.actuals().count(), 1);
2477 QCOMPARE(o.actuals().at(0), QVariant(QPointF(99.3, -10.2)));
2480 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", v8::Undefined()));
2481 QCOMPARE(o.error(), false);
2482 QCOMPARE(o.invoked(), 12);
2483 QCOMPARE(o.actuals().count(), 1);
2484 QCOMPARE(o.actuals().at(0), QVariant(QPointF(9, 12)));
2487 QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", v8::Undefined()));
2488 QCOMPARE(o.error(), false);
2489 QCOMPARE(o.invoked(), 13);
2490 QCOMPARE(o.actuals().count(), 1);
2491 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2494 QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", v8::Undefined()));
2495 QCOMPARE(o.error(), false);
2496 QCOMPARE(o.invoked(), 13);
2497 QCOMPARE(o.actuals().count(), 1);
2498 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2501 QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", v8::Undefined()));
2502 QCOMPARE(o.error(), false);
2503 QCOMPARE(o.invoked(), 13);
2504 QCOMPARE(o.actuals().count(), 1);
2505 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2508 QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", v8::Undefined()));
2509 QCOMPARE(o.error(), false);
2510 QCOMPARE(o.invoked(), 13);
2511 QCOMPARE(o.actuals().count(), 1);
2512 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2515 QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", v8::Undefined()));
2516 QCOMPARE(o.error(), false);
2517 QCOMPARE(o.invoked(), 13);
2518 QCOMPARE(o.actuals().count(), 1);
2519 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)&o));
2522 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined()));
2523 QCOMPARE(o.error(), false);
2524 QCOMPARE(o.invoked(), 14);
2525 QCOMPARE(o.actuals().count(), 1);
2526 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isNull());
2529 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined()));
2530 QCOMPARE(o.error(), false);
2531 QCOMPARE(o.invoked(), 14);
2532 QCOMPARE(o.actuals().count(), 1);
2533 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isUndefined());
2536 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined()));
2537 QCOMPARE(o.error(), false);
2538 QCOMPARE(o.invoked(), 14);
2539 QCOMPARE(o.actuals().count(), 1);
2540 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).strictlyEquals(QJSValue(19)));
2543 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined()));
2544 QCOMPARE(o.error(), false);
2545 QCOMPARE(o.invoked(), 14);
2546 QCOMPARE(o.actuals().count(), 1);
2547 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isArray());
2550 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined()));
2551 QCOMPARE(o.error(), false);
2552 QCOMPARE(o.invoked(), 15);
2553 QCOMPARE(o.actuals().count(), 2);
2554 QCOMPARE(o.actuals().at(0), QVariant(4));
2555 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isNull());
2558 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined()));
2559 QCOMPARE(o.error(), false);
2560 QCOMPARE(o.invoked(), 15);
2561 QCOMPARE(o.actuals().count(), 2);
2562 QCOMPARE(o.actuals().at(0), QVariant(8));
2563 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isUndefined());
2566 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined()));
2567 QCOMPARE(o.error(), false);
2568 QCOMPARE(o.invoked(), 15);
2569 QCOMPARE(o.actuals().count(), 2);
2570 QCOMPARE(o.actuals().at(0), QVariant(3));
2571 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).strictlyEquals(QJSValue(19)));
2574 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined()));
2575 QCOMPARE(o.error(), false);
2576 QCOMPARE(o.invoked(), 15);
2577 QCOMPARE(o.actuals().count(), 2);
2578 QCOMPARE(o.actuals().at(0), QVariant(44));
2579 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isArray());
2582 QVERIFY(EVALUATE_ERROR("object.method_overload()"));
2583 QCOMPARE(o.error(), false);
2584 QCOMPARE(o.invoked(), -1);
2585 QCOMPARE(o.actuals().count(), 0);
2588 QVERIFY(EVALUATE_VALUE("object.method_overload(10)", v8::Undefined()));
2589 QCOMPARE(o.error(), false);
2590 QCOMPARE(o.invoked(), 16);
2591 QCOMPARE(o.actuals().count(), 1);
2592 QCOMPARE(o.actuals().at(0), QVariant(10));
2595 QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", v8::Undefined()));
2596 QCOMPARE(o.error(), false);
2597 QCOMPARE(o.invoked(), 17);
2598 QCOMPARE(o.actuals().count(), 2);
2599 QCOMPARE(o.actuals().at(0), QVariant(10));
2600 QCOMPARE(o.actuals().at(1), QVariant(11));
2603 QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", v8::Undefined()));
2604 QCOMPARE(o.error(), false);
2605 QCOMPARE(o.invoked(), 18);
2606 QCOMPARE(o.actuals().count(), 1);
2607 QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2610 QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", v8::Undefined()));
2611 QCOMPARE(o.error(), false);
2612 QCOMPARE(o.invoked(), 19);
2613 QCOMPARE(o.actuals().count(), 1);
2614 QCOMPARE(o.actuals().at(0), QVariant(9));
2617 QVERIFY(EVALUATE_VALUE("object.method_default(10)", v8::Integer::New(19)));
2618 QCOMPARE(o.error(), false);
2619 QCOMPARE(o.invoked(), 20);
2620 QCOMPARE(o.actuals().count(), 2);
2621 QCOMPARE(o.actuals().at(0), QVariant(10));
2622 QCOMPARE(o.actuals().at(1), QVariant(19));
2625 QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", v8::Integer::New(13)));
2626 QCOMPARE(o.error(), false);
2627 QCOMPARE(o.invoked(), 20);
2628 QCOMPARE(o.actuals().count(), 2);
2629 QCOMPARE(o.actuals().at(0), QVariant(10));
2630 QCOMPARE(o.actuals().at(1), QVariant(13));
2633 QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", v8::Undefined()));
2634 QCOMPARE(o.error(), false);
2635 QCOMPARE(o.invoked(), -3);
2636 QCOMPARE(o.actuals().count(), 1);
2637 QCOMPARE(o.actuals().at(0), QVariant(9));
2640 QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", v8::Undefined()));
2641 QCOMPARE(o.error(), false);
2642 QCOMPARE(o.invoked(), 21);
2643 QCOMPARE(o.actuals().count(), 2);
2644 QCOMPARE(o.actuals().at(0), QVariant(9));
2645 QCOMPARE(o.actuals().at(1), QVariant());
2648 QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", v8::Undefined()));
2649 QCOMPARE(o.error(), false);
2650 QCOMPARE(o.invoked(), 21);
2651 QCOMPARE(o.actuals().count(), 2);
2652 QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2653 QCOMPARE(o.actuals().at(1), QVariant(QString("World")));
2656 QVERIFY(EVALUATE_VALUE("object.method_QJsonObject({foo:123})", v8::Undefined()));
2657 QCOMPARE(o.error(), false);
2658 QCOMPARE(o.invoked(), 22);
2659 QCOMPARE(o.actuals().count(), 1);
2660 QCOMPARE(qvariant_cast<QJsonObject>(o.actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2663 QVERIFY(EVALUATE_VALUE("object.method_QJsonArray([123])", v8::Undefined()));
2664 QCOMPARE(o.error(), false);
2665 QCOMPARE(o.invoked(), 23);
2666 QCOMPARE(o.actuals().count(), 1);
2667 QCOMPARE(qvariant_cast<QJsonArray>(o.actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2670 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(123)", v8::Undefined()));
2671 QCOMPARE(o.error(), false);
2672 QCOMPARE(o.invoked(), 24);
2673 QCOMPARE(o.actuals().count(), 1);
2674 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(123));
2677 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(42.35)", v8::Undefined()));
2678 QCOMPARE(o.error(), false);
2679 QCOMPARE(o.invoked(), 24);
2680 QCOMPARE(o.actuals().count(), 1);
2681 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(42.35));
2684 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue('ciao')", v8::Undefined()));
2685 QCOMPARE(o.error(), false);
2686 QCOMPARE(o.invoked(), 24);
2687 QCOMPARE(o.actuals().count(), 1);
2688 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QStringLiteral("ciao")));
2691 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(true)", v8::Undefined()));
2692 QCOMPARE(o.error(), false);
2693 QCOMPARE(o.invoked(), 24);
2694 QCOMPARE(o.actuals().count(), 1);
2695 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(true));
2698 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(false)", v8::Undefined()));
2699 QCOMPARE(o.error(), false);
2700 QCOMPARE(o.invoked(), 24);
2701 QCOMPARE(o.actuals().count(), 1);
2702 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(false));
2705 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(null)", v8::Undefined()));
2706 QCOMPARE(o.error(), false);
2707 QCOMPARE(o.invoked(), 24);
2708 QCOMPARE(o.actuals().count(), 1);
2709 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Null));
2712 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(undefined)", v8::Undefined()));
2713 QCOMPARE(o.error(), false);
2714 QCOMPARE(o.invoked(), 24);
2715 QCOMPARE(o.actuals().count(), 1);
2716 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2719 QVERIFY(EVALUATE_VALUE("object.method_overload({foo:123})", v8::Undefined()));
2720 QCOMPARE(o.error(), false);
2721 QCOMPARE(o.invoked(), 25);
2722 QCOMPARE(o.actuals().count(), 1);
2723 QCOMPARE(qvariant_cast<QJsonObject>(o.actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2726 QVERIFY(EVALUATE_VALUE("object.method_overload([123])", v8::Undefined()));
2727 QCOMPARE(o.error(), false);
2728 QCOMPARE(o.invoked(), 26);
2729 QCOMPARE(o.actuals().count(), 1);
2730 QCOMPARE(qvariant_cast<QJsonArray>(o.actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2733 QVERIFY(EVALUATE_VALUE("object.method_overload(null)", v8::Undefined()));
2734 QCOMPARE(o.error(), false);
2735 QCOMPARE(o.invoked(), 27);
2736 QCOMPARE(o.actuals().count(), 1);
2737 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Null));
2740 QVERIFY(EVALUATE_VALUE("object.method_overload(undefined)", v8::Undefined()));
2741 QCOMPARE(o.error(), false);
2742 QCOMPARE(o.invoked(), 27);
2743 QCOMPARE(o.actuals().count(), 1);
2744 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2747 QVERIFY(EVALUATE_ERROR("object.method_unknown(null)"));
2748 QCOMPARE(o.error(), false);
2749 QCOMPARE(o.invoked(), -1);
2750 QCOMPARE(o.actuals().count(), 0);
2753 // QTBUG-13047 (check that you can pass registered object types as args)
2754 void tst_qqmlecmascript::invokableObjectArg()
2756 QQmlComponent component(&engine, testFileUrl("invokableObjectArg.qml"));
2758 QObject *o = component.create();
2760 MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o);
2762 QCOMPARE(qmlobject->myinvokableObject, qmlobject);
2767 // QTBUG-13047 (check that you can return registered object types from methods)
2768 void tst_qqmlecmascript::invokableObjectRet()
2770 QQmlComponent component(&engine, testFileUrl("invokableObjectRet.qml"));
2772 QObject *o = component.create();
2774 QCOMPARE(o->property("test").toBool(), true);
2778 void tst_qqmlecmascript::invokableEnumRet()
2780 QQmlComponent component(&engine, testFileUrl("invokableEnumRet.qml"));
2782 QObject *o = component.create();
2784 QCOMPARE(o->property("test").toBool(), true);
2789 void tst_qqmlecmascript::listToVariant()
2791 QQmlComponent component(&engine, testFileUrl("listToVariant.qml"));
2793 MyQmlContainer container;
2795 QQmlContext context(engine.rootContext());
2796 context.setContextObject(&container);
2798 QObject *object = component.create(&context);
2799 QVERIFY(object != 0);
2801 QVariant v = object->property("test");
2802 QCOMPARE(v.userType(), qMetaTypeId<QQmlListReference>());
2803 QVERIFY(qvariant_cast<QQmlListReference>(v).object() == &container);
2809 Q_DECLARE_METATYPE(QQmlListProperty<MyQmlObject>)
2810 void tst_qqmlecmascript::listAssignment()
2812 QQmlComponent component(&engine, testFileUrl("listAssignment.qml"));
2813 QObject *obj = component.create();
2814 QCOMPARE(obj->property("list1length").toInt(), 2);
2815 QQmlListProperty<MyQmlObject> list1 = obj->property("list1").value<QQmlListProperty<MyQmlObject> >();
2816 QQmlListProperty<MyQmlObject> list2 = obj->property("list2").value<QQmlListProperty<MyQmlObject> >();
2817 QCOMPARE(list1.count(&list1), list2.count(&list2));
2818 QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0));
2819 QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1));
2824 void tst_qqmlecmascript::multiEngineObject()
2827 obj.setStringProperty("Howdy planet");
2830 e1.rootContext()->setContextProperty("thing", &obj);
2831 QQmlComponent c1(&e1, testFileUrl("multiEngineObject.qml"));
2834 e2.rootContext()->setContextProperty("thing", &obj);
2835 QQmlComponent c2(&e2, testFileUrl("multiEngineObject.qml"));
2837 QObject *o1 = c1.create();
2838 QObject *o2 = c2.create();
2840 QCOMPARE(o1->property("test").toString(), QString("Howdy planet"));
2841 QCOMPARE(o2->property("test").toString(), QString("Howdy planet"));
2847 // Test that references to QObjects are cleanup when the object is destroyed
2848 void tst_qqmlecmascript::deletedObject()
2850 QQmlComponent component(&engine, testFileUrl("deletedObject.qml"));
2852 QObject *object = component.create();
2854 QCOMPARE(object->property("test1").toBool(), true);
2855 QCOMPARE(object->property("test2").toBool(), true);
2856 QCOMPARE(object->property("test3").toBool(), true);
2857 QCOMPARE(object->property("test4").toBool(), true);
2862 void tst_qqmlecmascript::attachedPropertyScope()
2864 QQmlComponent component(&engine, testFileUrl("attachedPropertyScope.qml"));
2866 QObject *object = component.create();
2867 QVERIFY(object != 0);
2869 MyQmlAttachedObject *attached =
2870 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
2871 QVERIFY(attached != 0);
2873 QCOMPARE(object->property("value2").toInt(), 0);
2875 attached->emitMySignal();
2877 QCOMPARE(object->property("value2").toInt(), 9);
2882 void tst_qqmlecmascript::scriptConnect()
2885 QQmlComponent component(&engine, testFileUrl("scriptConnect.1.qml"));
2887 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2888 QVERIFY(object != 0);
2890 QCOMPARE(object->property("test").toBool(), false);
2891 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2892 QCOMPARE(object->property("test").toBool(), true);
2898 QQmlComponent component(&engine, testFileUrl("scriptConnect.2.qml"));
2900 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2901 QVERIFY(object != 0);
2903 QCOMPARE(object->property("test").toBool(), false);
2904 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2905 QCOMPARE(object->property("test").toBool(), true);
2911 QQmlComponent component(&engine, testFileUrl("scriptConnect.3.qml"));
2913 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2914 QVERIFY(object != 0);
2916 QCOMPARE(object->property("test").toBool(), false);
2917 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2918 QCOMPARE(object->property("test").toBool(), true);
2924 QQmlComponent component(&engine, testFileUrl("scriptConnect.4.qml"));
2926 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2927 QVERIFY(object != 0);
2929 QCOMPARE(object->methodCalled(), false);
2930 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2931 QCOMPARE(object->methodCalled(), true);
2937 QQmlComponent component(&engine, testFileUrl("scriptConnect.5.qml"));
2939 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2940 QVERIFY(object != 0);
2942 QCOMPARE(object->methodCalled(), false);
2943 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2944 QCOMPARE(object->methodCalled(), true);
2950 QQmlComponent component(&engine, testFileUrl("scriptConnect.6.qml"));
2952 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2953 QVERIFY(object != 0);
2955 QCOMPARE(object->property("test").toInt(), 0);
2956 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2957 QCOMPARE(object->property("test").toInt(), 2);
2963 void tst_qqmlecmascript::scriptDisconnect()
2966 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.1.qml"));
2968 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2969 QVERIFY(object != 0);
2971 QCOMPARE(object->property("test").toInt(), 0);
2972 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2973 QCOMPARE(object->property("test").toInt(), 1);
2974 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2975 QCOMPARE(object->property("test").toInt(), 2);
2976 emit object->basicSignal();
2977 QCOMPARE(object->property("test").toInt(), 2);
2978 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2979 QCOMPARE(object->property("test").toInt(), 2);
2985 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.2.qml"));
2987 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2988 QVERIFY(object != 0);
2990 QCOMPARE(object->property("test").toInt(), 0);
2991 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2992 QCOMPARE(object->property("test").toInt(), 1);
2993 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2994 QCOMPARE(object->property("test").toInt(), 2);
2995 emit object->basicSignal();
2996 QCOMPARE(object->property("test").toInt(), 2);
2997 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2998 QCOMPARE(object->property("test").toInt(), 2);
3004 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.3.qml"));
3006 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3007 QVERIFY(object != 0);
3009 QCOMPARE(object->property("test").toInt(), 0);
3010 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3011 QCOMPARE(object->property("test").toInt(), 1);
3012 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3013 QCOMPARE(object->property("test").toInt(), 2);
3014 emit object->basicSignal();
3015 QCOMPARE(object->property("test").toInt(), 2);
3016 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3017 QCOMPARE(object->property("test").toInt(), 3);
3022 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.4.qml"));
3024 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3025 QVERIFY(object != 0);
3027 QCOMPARE(object->property("test").toInt(), 0);
3028 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3029 QCOMPARE(object->property("test").toInt(), 1);
3030 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3031 QCOMPARE(object->property("test").toInt(), 2);
3032 emit object->basicSignal();
3033 QCOMPARE(object->property("test").toInt(), 2);
3034 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3035 QCOMPARE(object->property("test").toInt(), 3);
3041 class OwnershipObject : public QObject
3045 OwnershipObject() { object = new QObject; }
3047 QPointer<QObject> object;
3050 QObject *getObject() { return object; }
3053 void tst_qqmlecmascript::ownership()
3055 OwnershipObject own;
3056 QQmlContext *context = new QQmlContext(engine.rootContext());
3057 context->setContextObject(&own);
3060 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3062 QVERIFY(own.object != 0);
3064 QObject *object = component.create(context);
3066 engine.collectGarbage();
3068 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3069 QCoreApplication::processEvents();
3071 QVERIFY(own.object == 0);
3076 own.object = new QObject(&own);
3079 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3081 QVERIFY(own.object != 0);
3083 QObject *object = component.create(context);
3085 engine.collectGarbage();
3087 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3088 QCoreApplication::processEvents();
3090 QVERIFY(own.object != 0);
3098 class CppOwnershipReturnValue : public QObject
3102 CppOwnershipReturnValue() : value(0) {}
3103 ~CppOwnershipReturnValue() { delete value; }
3105 Q_INVOKABLE QObject *create() {
3106 value = new QObject;
3107 QQmlEngine::setObjectOwnership(value, QQmlEngine::CppOwnership);
3111 Q_INVOKABLE MyQmlObject *createQmlObject() {
3112 MyQmlObject *rv = new MyQmlObject;
3117 QPointer<QObject> value;
3121 // Test setObjectOwnership(CppOwnership) works even when there is no QQmlData
3122 void tst_qqmlecmascript::cppOwnershipReturnValue()
3124 CppOwnershipReturnValue source;
3128 engine.rootContext()->setContextProperty("source", &source);
3130 QVERIFY(source.value == 0);
3132 QQmlComponent component(&engine);
3133 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl());
3135 QObject *object = component.create();
3137 QVERIFY(object != 0);
3138 QVERIFY(source.value != 0);
3143 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3144 QCoreApplication::processEvents();
3146 QVERIFY(source.value != 0);
3150 void tst_qqmlecmascript::ownershipCustomReturnValue()
3152 CppOwnershipReturnValue source;
3156 engine.rootContext()->setContextProperty("source", &source);
3158 QVERIFY(source.value == 0);
3160 QQmlComponent component(&engine);
3161 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl());
3163 QObject *object = component.create();
3165 QVERIFY(object != 0);
3166 QVERIFY(source.value != 0);
3171 engine.collectGarbage();
3172 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3173 QCoreApplication::processEvents();
3175 QVERIFY(source.value == 0);
3178 //the return value from getObject will be JS ownership,
3179 //unless strong Cpp ownership has been set
3180 class OwnershipChangingObject : public QObject
3184 OwnershipChangingObject(): object(0) { }
3186 QPointer<QObject> object;
3189 QObject *getObject() { return object; }
3190 void setObject(QObject *obj) { object = obj; }
3193 void tst_qqmlecmascript::ownershipRootObject()
3195 OwnershipChangingObject own;
3196 QQmlContext *context = new QQmlContext(engine.rootContext());
3197 context->setContextObject(&own);
3199 QQmlComponent component(&engine, testFileUrl("ownershipRootObject.qml"));
3200 QQmlGuard<QObject> object = component.create(context);
3203 engine.collectGarbage();
3205 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3206 QCoreApplication::processEvents();
3208 QVERIFY(own.object != 0);
3214 void tst_qqmlecmascript::ownershipConsistency()
3216 OwnershipChangingObject own;
3217 QQmlContext *context = new QQmlContext(engine.rootContext());
3218 context->setContextObject(&own);
3220 QString expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":19: Error: Invalid attempt to destroy() an indestructible object");
3221 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3222 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":15: Error: Invalid attempt to destroy() an indestructible object");
3223 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3224 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":6: Error: Invalid attempt to destroy() an indestructible object");
3225 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3226 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":10: Error: Invalid attempt to destroy() an indestructible object");
3227 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3229 QQmlComponent component(&engine, testFileUrl("ownershipConsistency.qml"));
3230 QQmlGuard<QObject> object = component.create(context);
3233 engine.collectGarbage();
3235 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3236 QCoreApplication::processEvents();
3238 QVERIFY(own.object != 0);
3244 void tst_qqmlecmascript::ownershipQmlIncubated()
3246 QQmlComponent component(&engine, testFileUrl("ownershipQmlIncubated.qml"));
3247 QObject *object = component.create();
3250 QTRY_VERIFY(object->property("incubatedItem").value<QObject*>() != 0);
3252 QMetaObject::invokeMethod(object, "deleteIncubatedItem");
3254 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3255 QCoreApplication::processEvents();
3257 QVERIFY(object->property("incubatedItem").value<QObject*>() == 0);
3262 class QListQObjectMethodsObject : public QObject
3266 QListQObjectMethodsObject() {
3267 m_objects.append(new MyQmlObject());
3268 m_objects.append(new MyQmlObject());
3271 ~QListQObjectMethodsObject() {
3272 qDeleteAll(m_objects);
3276 QList<QObject *> getObjects() { return m_objects; }
3279 QList<QObject *> m_objects;
3282 // Tests that returning a QList<QObject*> from a method works
3283 void tst_qqmlecmascript::qlistqobjectMethods()
3285 QListQObjectMethodsObject obj;
3286 QQmlContext *context = new QQmlContext(engine.rootContext());
3287 context->setContextObject(&obj);
3289 QQmlComponent component(&engine, testFileUrl("qlistqobjectMethods.qml"));
3291 QObject *object = component.create(context);
3293 QCOMPARE(object->property("test").toInt(), 2);
3294 QCOMPARE(object->property("test2").toBool(), true);
3301 void tst_qqmlecmascript::strictlyEquals()
3303 QQmlComponent component(&engine, testFileUrl("strictlyEquals.qml"));
3305 QObject *object = component.create();
3306 QVERIFY(object != 0);
3308 QCOMPARE(object->property("test1").toBool(), true);
3309 QCOMPARE(object->property("test2").toBool(), true);
3310 QCOMPARE(object->property("test3").toBool(), true);
3311 QCOMPARE(object->property("test4").toBool(), true);
3312 QCOMPARE(object->property("test5").toBool(), true);
3313 QCOMPARE(object->property("test6").toBool(), true);
3314 QCOMPARE(object->property("test7").toBool(), true);
3315 QCOMPARE(object->property("test8").toBool(), true);
3320 void tst_qqmlecmascript::compiled()
3322 QQmlComponent component(&engine, testFileUrl("compiled.qml"));
3324 QObject *object = component.create();
3325 QVERIFY(object != 0);
3327 QCOMPARE(object->property("test1").toReal(), qreal(15.7));
3328 QCOMPARE(object->property("test2").toReal(), qreal(-6.7));
3329 QCOMPARE(object->property("test3").toBool(), true);
3330 QCOMPARE(object->property("test4").toBool(), false);
3331 QCOMPARE(object->property("test5").toBool(), false);
3332 QCOMPARE(object->property("test6").toBool(), true);
3334 QCOMPARE(object->property("test7").toInt(), 185);
3335 QCOMPARE(object->property("test8").toInt(), 167);
3336 QCOMPARE(object->property("test9").toBool(), true);
3337 QCOMPARE(object->property("test10").toBool(), false);
3338 QCOMPARE(object->property("test11").toBool(), false);
3339 QCOMPARE(object->property("test12").toBool(), true);
3341 QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld"));
3342 QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World"));
3343 QCOMPARE(object->property("test15").toBool(), false);
3344 QCOMPARE(object->property("test16").toBool(), true);
3346 QCOMPARE(object->property("test17").toInt(), 5);
3347 QCOMPARE(object->property("test18").toReal(), qreal(176));
3348 QCOMPARE(object->property("test19").toInt(), 7);
3349 QCOMPARE(object->property("test20").toReal(), qreal(6.7));
3350 QCOMPARE(object->property("test21").toString(), QLatin1String("6.7"));
3351 QCOMPARE(object->property("test22").toString(), QLatin1String("!"));
3352 QCOMPARE(object->property("test23").toBool(), true);
3353 QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33));
3354 QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA));
3359 // Test that numbers assigned in bindings as strings work consistently
3360 void tst_qqmlecmascript::numberAssignment()
3362 QQmlComponent component(&engine, testFileUrl("numberAssignment.qml"));
3364 QObject *object = component.create();
3365 QVERIFY(object != 0);
3367 QCOMPARE(object->property("test1"), QVariant((qreal)6.7));
3368 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3369 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3370 QCOMPARE(object->property("test3"), QVariant((qreal)6));
3371 QCOMPARE(object->property("test4"), QVariant((qreal)6));
3373 QCOMPARE(object->property("test5"), QVariant((int)7));
3374 QCOMPARE(object->property("test6"), QVariant((int)7));
3375 QCOMPARE(object->property("test7"), QVariant((int)6));
3376 QCOMPARE(object->property("test8"), QVariant((int)6));
3378 QCOMPARE(object->property("test9"), QVariant((unsigned int)7));
3379 QCOMPARE(object->property("test10"), QVariant((unsigned int)7));
3380 QCOMPARE(object->property("test11"), QVariant((unsigned int)6));
3381 QCOMPARE(object->property("test12"), QVariant((unsigned int)6));
3386 void tst_qqmlecmascript::propertySplicing()
3388 QQmlComponent component(&engine, testFileUrl("propertySplicing.qml"));
3390 QObject *object = component.create();
3391 QVERIFY(object != 0);
3393 QCOMPARE(object->property("test").toBool(), true);
3399 void tst_qqmlecmascript::signalWithUnknownTypes()
3401 QQmlComponent component(&engine, testFileUrl("signalWithUnknownTypes.qml"));
3403 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3404 QVERIFY(object != 0);
3406 MyQmlObject::MyType type;
3407 type.value = 0x8971123;
3408 emit object->signalWithUnknownType(type);
3410 MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant());
3412 QCOMPARE(result.value, type.value);
3418 void tst_qqmlecmascript::signalWithJSValueInVariant_data()
3420 QTest::addColumn<QString>("expression");
3421 QTest::addColumn<QString>("compare");
3423 QString compareStrict("(function(a, b) { return a === b; })");
3424 QTest::newRow("true") << "true" << compareStrict;
3425 QTest::newRow("undefined") << "undefined" << compareStrict;
3426 QTest::newRow("null") << "null" << compareStrict;
3427 QTest::newRow("123") << "123" << compareStrict;
3428 QTest::newRow("'ciao'") << "'ciao'" << compareStrict;
3430 QString comparePropertiesStrict(
3432 " if (typeof b != 'object')"
3434 " var props = Object.getOwnPropertyNames(b);"
3435 " for (var i = 0; i < props.length; ++i) {"
3436 " var p = props[i];"
3437 " return arguments.callee(a[p], b[p]);"
3440 QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })" << comparePropertiesStrict;
3441 QTest::newRow("[10,20,30]") << "[10,20,30]" << comparePropertiesStrict;
3444 void tst_qqmlecmascript::signalWithJSValueInVariant()
3446 QFETCH(QString, expression);
3447 QFETCH(QString, compare);
3449 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3450 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3451 QVERIFY(object != 0);
3453 QJSValue value = engine.evaluate(expression);
3454 QVERIFY(!value.isError());
3455 object->setProperty("expression", expression);
3456 object->setProperty("compare", compare);
3457 object->setProperty("pass", false);
3459 emit object->signalWithVariant(QVariant::fromValue(value));
3460 QVERIFY(object->property("pass").toBool());
3463 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines_data()
3465 signalWithJSValueInVariant_data();
3468 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines()
3470 QFETCH(QString, expression);
3471 QFETCH(QString, compare);
3473 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3474 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3475 QVERIFY(object != 0);
3478 QJSValue value = engine2.evaluate(expression);
3479 QVERIFY(!value.isError());
3480 object->setProperty("expression", expression);
3481 object->setProperty("compare", compare);
3482 object->setProperty("pass", false);
3484 QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine.");
3485 emit object->signalWithVariant(QVariant::fromValue(value));
3486 QVERIFY(!object->property("pass").toBool());
3489 void tst_qqmlecmascript::signalWithQJSValue_data()
3491 signalWithJSValueInVariant_data();
3494 void tst_qqmlecmascript::signalWithQJSValue()
3496 QFETCH(QString, expression);
3497 QFETCH(QString, compare);
3499 QQmlComponent component(&engine, testFileUrl("signalWithQJSValue.qml"));
3500 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3501 QVERIFY(object != 0);
3503 QJSValue value = engine.evaluate(expression);
3504 QVERIFY(!value.isError());
3505 object->setProperty("expression", expression);
3506 object->setProperty("compare", compare);
3507 object->setProperty("pass", false);
3509 emit object->signalWithQJSValue(value);
3511 QVERIFY(object->property("pass").toBool());
3512 QVERIFY(object->qjsvalue().strictlyEquals(value));
3515 void tst_qqmlecmascript::moduleApi_data()
3517 QTest::addColumn<QUrl>("testfile");
3518 QTest::addColumn<QString>("errorMessage");
3519 QTest::addColumn<QStringList>("warningMessages");
3520 QTest::addColumn<QStringList>("readProperties");
3521 QTest::addColumn<QVariantList>("readExpectedValues");
3522 QTest::addColumn<QStringList>("writeProperties");
3523 QTest::addColumn<QVariantList>("writeValues");
3524 QTest::addColumn<QStringList>("readBackProperties");
3525 QTest::addColumn<QVariantList>("readBackExpectedValues");
3527 QTest::newRow("qobject, register + read + method")
3528 << testFileUrl("moduleapi/qobjectModuleApi.qml")
3531 << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
3532 << "qobjectMinorVersionTest" << "qobjectMajorVersionTest" << "qobjectParentedTest")
3533 << (QVariantList() << 20 << 20 << 1 << 20 << 20 << 26)
3539 QTest::newRow("script, register + read")
3540 << testFileUrl("moduleapi/scriptModuleApi.qml")
3543 << (QStringList() << "scriptTest")
3544 << (QVariantList() << 13)
3550 QTest::newRow("qobject, caching + read")
3551 << testFileUrl("moduleapi/qobjectModuleApiCaching.qml")
3554 << (QStringList() << "existingUriTest" << "qobjectParentedTest")
3555 << (QVariantList() << 20 << 26) // 26, shouldn't have incremented to 27.
3561 QTest::newRow("script, caching + read")
3562 << testFileUrl("moduleapi/scriptModuleApiCaching.qml")
3565 << (QStringList() << "scriptTest")
3566 << (QVariantList() << 13) // 13, shouldn't have incremented to 14.
3572 QTest::newRow("qobject, writing + readonly constraints")
3573 << testFileUrl("moduleapi/qobjectModuleApiWriting.qml")
3575 << (QStringList() << QString(testFileUrl("moduleapi/qobjectModuleApiWriting.qml").toString() + QLatin1String(":15: Error: Cannot assign to read-only property \"qobjectTestProperty\"")))
3576 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3577 << (QVariantList() << 20 << 50 << 10)
3578 << (QStringList() << "firstProperty" << "secondProperty")
3579 << (QVariantList() << 30 << 30)
3580 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3581 << (QVariantList() << 20 << 30 << 30);
3583 QTest::newRow("script, writing + readonly constraints")
3584 << testFileUrl("moduleapi/scriptModuleApiWriting.qml")
3586 << (QStringList() << QString(testFileUrl("moduleapi/scriptModuleApiWriting.qml").toString() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\"")))
3587 << (QStringList() << "readBack" << "unchanged")
3588 << (QVariantList() << 13 << 42)
3589 << (QStringList() << "firstProperty" << "secondProperty")
3590 << (QVariantList() << 30 << 30)
3591 << (QStringList() << "readBack" << "unchanged")
3592 << (QVariantList() << 30 << 42);
3594 QTest::newRow("qobject module API enum values in JS")
3595 << testFileUrl("moduleapi/qobjectModuleApiEnums.qml")
3598 << (QStringList() << "enumValue" << "enumMethod")
3599 << (QVariantList() << 42 << 30)
3605 QTest::newRow("qobject, invalid major version fail")
3606 << testFileUrl("moduleapi/moduleApiMajorVersionFail.qml")
3607 << QString("QQmlComponent: Component is not ready")
3616 QTest::newRow("qobject, invalid minor version fail")
3617 << testFileUrl("moduleapi/moduleApiMinorVersionFail.qml")
3618 << QString("QQmlComponent: Component is not ready")
3628 void tst_qqmlecmascript::moduleApi()
3630 QFETCH(QUrl, testfile);
3631 QFETCH(QString, errorMessage);
3632 QFETCH(QStringList, warningMessages);
3633 QFETCH(QStringList, readProperties);
3634 QFETCH(QVariantList, readExpectedValues);
3635 QFETCH(QStringList, writeProperties);
3636 QFETCH(QVariantList, writeValues);
3637 QFETCH(QStringList, readBackProperties);
3638 QFETCH(QVariantList, readBackExpectedValues);
3640 QQmlComponent component(&engine, testfile);
3642 if (!errorMessage.isEmpty())
3643 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3645 if (warningMessages.size())
3646 foreach (const QString &warning, warningMessages)
3647 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3649 QObject *object = component.create();
3650 if (!errorMessage.isEmpty()) {
3651 QVERIFY(object == 0);
3653 QVERIFY(object != 0);
3654 for (int i = 0; i < readProperties.size(); ++i)
3655 QCOMPARE(object->property(readProperties.at(i).toLatin1().constData()), readExpectedValues.at(i));
3656 for (int i = 0; i < writeProperties.size(); ++i)
3657 QVERIFY(object->setProperty(writeProperties.at(i).toLatin1().constData(), writeValues.at(i)));
3658 for (int i = 0; i < readBackProperties.size(); ++i)
3659 QCOMPARE(object->property(readBackProperties.at(i).toLatin1().constData()), readBackExpectedValues.at(i));
3664 void tst_qqmlecmascript::importScripts_data()
3666 QTest::addColumn<QUrl>("testfile");
3667 QTest::addColumn<QString>("errorMessage");
3668 QTest::addColumn<QStringList>("warningMessages");
3669 QTest::addColumn<QStringList>("propertyNames");
3670 QTest::addColumn<QVariantList>("propertyValues");
3672 QTest::newRow("basic functionality")
3673 << testFileUrl("jsimport/testImport.qml")
3676 << (QStringList() << QLatin1String("importedScriptStringValue")
3677 << QLatin1String("importedScriptFunctionValue")
3678 << QLatin1String("importedModuleAttachedPropertyValue")
3679 << QLatin1String("importedModuleEnumValue"))
3680 << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3685 QTest::newRow("import scoping")
3686 << testFileUrl("jsimport/testImportScoping.qml")
3689 << (QStringList() << QLatin1String("componentError"))
3690 << (QVariantList() << QVariant(5));
3692 QTest::newRow("parent scope shouldn't be inherited by import with imports")
3693 << testFileUrl("jsimportfail/failOne.qml")
3695 << (QStringList() << QString(testFileUrl("jsimportfail/failOne.qml").toString() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
3696 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3697 << (QVariantList() << QVariant(QString()));
3699 QTest::newRow("javascript imports in an import should be private to the import scope")
3700 << testFileUrl("jsimportfail/failTwo.qml")
3702 << (QStringList() << QString(testFileUrl("jsimportfail/failTwo.qml").toString() + QLatin1String(":6: ReferenceError: ImportOneJs is not defined")))
3703 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3704 << (QVariantList() << QVariant(QString()));
3706 QTest::newRow("module imports in an import should be private to the import scope")
3707 << testFileUrl("jsimportfail/failThree.qml")
3709 << (QStringList() << QString(testFileUrl("jsimportfail/failThree.qml").toString() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
3710 << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
3711 << (QVariantList() << QVariant(false));
3713 QTest::newRow("typenames in an import should be private to the import scope")
3714 << testFileUrl("jsimportfail/failFour.qml")
3716 << (QStringList() << QString(testFileUrl("jsimportfail/failFour.qml").toString() + QLatin1String(":6: ReferenceError: JsQtTest is not defined")))
3717 << (QStringList() << QLatin1String("importedModuleEnumValue"))
3718 << (QVariantList() << QVariant(0));
3720 QTest::newRow("import with imports has it's own activation scope")
3721 << testFileUrl("jsimportfail/failFive.qml")
3723 << (QStringList() << QString(testFileUrl("jsimportfail/importWithImports.js").toString() + QLatin1String(":8: ReferenceError: Component is not defined")))
3724 << (QStringList() << QLatin1String("componentError"))
3725 << (QVariantList() << QVariant(0));
3727 QTest::newRow("import pragma library script")
3728 << testFileUrl("jsimport/testImportPragmaLibrary.qml")
3731 << (QStringList() << QLatin1String("testValue"))
3732 << (QVariantList() << QVariant(31));
3734 QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3735 << testFileUrl("jsimportfail/testImportPragmaLibrary.qml")
3737 << (QStringList() << QString(testFileUrl("jsimportfail/importPragmaLibrary.js").toString() + QLatin1String(":6: ReferenceError: Component is not defined")))
3738 << (QStringList() << QLatin1String("testValue"))
3739 << (QVariantList() << QVariant(0));
3741 QTest::newRow("import pragma library script which has an import")
3742 << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
3745 << (QStringList() << QLatin1String("testValue"))
3746 << (QVariantList() << QVariant(55));
3748 QTest::newRow("import pragma library script which has a pragma library import")
3749 << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3752 << (QStringList() << QLatin1String("testValue"))
3753 << (QVariantList() << QVariant(18));
3755 QTest::newRow("import module api into js import")
3756 << testFileUrl("jsimport/testImportModuleApi.qml")
3759 << (QStringList() << QLatin1String("testValue"))
3760 << (QVariantList() << QVariant(20));
3762 QTest::newRow("import module which exports a script")
3763 << testFileUrl("jsimport/testJsImport.qml")
3766 << (QStringList() << QLatin1String("importedScriptStringValue")
3767 << QLatin1String("renamedScriptStringValue")
3768 << QLatin1String("reimportedScriptStringValue"))
3769 << (QVariantList() << QVariant(QString("Hello"))
3770 << QVariant(QString("Hello"))
3771 << QVariant(QString("Hello")));
3774 void tst_qqmlecmascript::importScripts()
3776 QFETCH(QUrl, testfile);
3777 QFETCH(QString, errorMessage);
3778 QFETCH(QStringList, warningMessages);
3779 QFETCH(QStringList, propertyNames);
3780 QFETCH(QVariantList, propertyValues);
3782 QQmlComponent component(&engine, testfile);
3784 if (!errorMessage.isEmpty())
3785 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3787 if (warningMessages.size())
3788 foreach (const QString &warning, warningMessages)
3789 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3791 QObject *object = component.create();
3792 if (!errorMessage.isEmpty()) {
3793 QVERIFY(object == 0);
3795 QVERIFY(object != 0);
3796 for (int i = 0; i < propertyNames.size(); ++i)
3797 QCOMPARE(object->property(propertyNames.at(i).toLatin1().constData()), propertyValues.at(i));
3802 void tst_qqmlecmascript::scarceResources_other()
3804 /* These tests require knowledge of state, since we test values after
3805 performing signal or function invocation. */
3807 QPixmap origPixmap(100, 100);
3808 origPixmap.fill(Qt::blue);
3809 QString srp_name, expectedWarning;
3810 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
3811 ScarceResourceObject *eo = 0;
3813 QObject *object = 0;
3815 /* property var semantics */
3817 // test that scarce resources are handled properly in signal invocation
3818 QQmlComponent varComponentTen(&engine, testFileUrl("scarceResourceSignal.var.qml"));
3819 object = varComponentTen.create();
3820 srsc = object->findChild<QObject*>("srsc");
3822 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3823 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3824 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3825 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3826 QMetaObject::invokeMethod(srsc, "testSignal");
3827 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3828 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3829 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3830 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3831 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3832 QVERIFY(srsc->property("scarceResourceCopy").isValid());
3833 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3834 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3835 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3836 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3839 // test that scarce resources are handled properly from js functions in qml files
3840 QQmlComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml"));
3841 object = varComponentEleven.create();
3842 QVERIFY(object != 0);
3843 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3844 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3845 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3846 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3847 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3848 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3849 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3850 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3851 QMetaObject::invokeMethod(object, "releaseScarceResource");
3852 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3853 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3854 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3855 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3858 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3859 QQmlComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
3860 object = varComponentTwelve.create();
3861 QVERIFY(object != 0);
3862 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3863 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3864 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3865 srp_name = object->property("srp_name").toString();
3866 expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3867 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3868 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3869 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3870 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3871 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3872 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3875 // test that if an Item which has JS ownership but has a scarce resource property is garbage collected,
3876 // that the scarce resource is removed from the engine's list of scarce resources to clean up.
3877 QQmlComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml"));
3878 object = varComponentThirteen.create();
3879 QVERIFY(object != 0);
3880 QVERIFY(!object->property("varProperty").isValid()); // not assigned yet
3881 QMetaObject::invokeMethod(object, "assignVarProperty");
3882 QVERIFY(ep->scarceResources.isEmpty()); // the scarce resource is a VME property.
3883 QMetaObject::invokeMethod(object, "deassignVarProperty");
3884 QVERIFY(ep->scarceResources.isEmpty()); // should still be empty; the resource should have been released on gc.
3887 /* property variant semantics */
3889 // test that scarce resources are handled properly in signal invocation
3890 QQmlComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml"));
3891 object = variantComponentTen.create();
3892 QVERIFY(object != 0);
3893 srsc = object->findChild<QObject*>("srsc");
3895 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3896 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3897 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3898 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3899 QMetaObject::invokeMethod(srsc, "testSignal");
3900 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3901 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3902 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3903 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3904 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3905 QVERIFY(srsc->property("scarceResourceCopy").isValid());
3906 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3907 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3908 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3909 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3912 // test that scarce resources are handled properly from js functions in qml files
3913 QQmlComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml"));
3914 object = variantComponentEleven.create();
3915 QVERIFY(object != 0);
3916 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3917 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3918 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3919 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3920 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3921 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3922 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3923 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3924 QMetaObject::invokeMethod(object, "releaseScarceResource");
3925 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3926 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3927 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3928 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3931 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3932 QQmlComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml"));
3933 object = variantComponentTwelve.create();
3934 QVERIFY(object != 0);
3935 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3936 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3937 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3938 srp_name = object->property("srp_name").toString();
3939 expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3940 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3941 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3942 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3943 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3944 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3945 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3949 void tst_qqmlecmascript::scarceResources_data()
3951 QTest::addColumn<QUrl>("qmlFile");
3952 QTest::addColumn<bool>("readDetachStatus");
3953 QTest::addColumn<bool>("expectedDetachStatus");
3954 QTest::addColumn<QStringList>("propertyNames");
3955 QTest::addColumn<QVariantList>("expectedValidity");
3956 QTest::addColumn<QVariantList>("expectedValues");
3957 QTest::addColumn<QStringList>("expectedErrors");
3959 QPixmap origPixmap(100, 100);
3960 origPixmap.fill(Qt::blue);
3962 /* property var semantics */
3964 // in the following three cases, the instance created from the component
3965 // has a property which is a copy of the scarce resource; hence, the
3966 // resource should NOT be detached prior to deletion of the object instance,
3967 // unless the resource is destroyed explicitly.
3968 QTest::newRow("var: import scarce resource copy directly")
3969 << testFileUrl("scarceResourceCopy.var.qml")
3971 << false // won't be detached, because assigned to property and not explicitly released
3972 << (QStringList() << QLatin1String("scarceResourceCopy"))
3973 << (QList<QVariant>() << true)
3974 << (QList<QVariant>() << origPixmap)
3977 QTest::newRow("var: import scarce resource copy from JS")
3978 << testFileUrl("scarceResourceCopyFromJs.var.qml")
3980 << false // won't be detached, because assigned to property and not explicitly released
3981 << (QStringList() << QLatin1String("scarceResourceCopy"))
3982 << (QList<QVariant>() << true)
3983 << (QList<QVariant>() << origPixmap)
3986 QTest::newRow("var: import released scarce resource copy from JS")
3987 << testFileUrl("scarceResourceDestroyedCopy.var.qml")
3989 << true // explicitly released, so it will be detached
3990 << (QStringList() << QLatin1String("scarceResourceCopy"))
3991 << (QList<QVariant>() << false)
3992 << (QList<QVariant>() << QVariant())
3995 // in the following three cases, no other copy should exist in memory,
3996 // and so it should be detached (unless explicitly preserved).
3997 QTest::newRow("var: import auto-release SR from JS in binding side-effect")
3998 << testFileUrl("scarceResourceTest.var.qml")
4000 << true // auto released, so it will be detached
4001 << (QStringList() << QLatin1String("scarceResourceTest"))
4002 << (QList<QVariant>() << true)
4003 << (QList<QVariant>() << QVariant(100))
4005 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4006 << testFileUrl("scarceResourceTestPreserve.var.qml")
4008 << false // won't be detached because we explicitly preserve it
4009 << (QStringList() << QLatin1String("scarceResourceTest"))
4010 << (QList<QVariant>() << true)
4011 << (QList<QVariant>() << QVariant(100))
4013 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4014 << testFileUrl("scarceResourceTestMultiple.var.qml")
4016 << true // will be detached because all resources were released manually or automatically.
4017 << (QStringList() << QLatin1String("scarceResourceTest"))
4018 << (QList<QVariant>() << true)
4019 << (QList<QVariant>() << QVariant(100))
4022 // In the following three cases, test that scarce resources are handled
4023 // correctly for imports.
4024 QTest::newRow("var: import with no binding")
4025 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4026 << false // cannot check detach status.
4029 << QList<QVariant>()
4030 << QList<QVariant>()
4032 QTest::newRow("var: import with binding without explicit preserve")
4033 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4036 << (QStringList() << QLatin1String("scarceResourceCopy"))
4037 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4038 << (QList<QVariant>() << QVariant())
4040 QTest::newRow("var: import with explicit release after binding evaluation")
4041 << testFileUrl("scarceResourceCopyImport.var.qml")
4044 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4045 << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated.
4046 << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true))
4048 QTest::newRow("var: import with different js objects")
4049 << testFileUrl("scarceResourceCopyImportDifferent.var.qml")
4052 << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4053 << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4054 << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false))
4056 QTest::newRow("var: import with different js objects and explicit release")
4057 << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml")
4060 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4061 << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4062 << (QList<QVariant>() << QVariant(origPixmap) << QVariant())
4064 QTest::newRow("var: import with same js objects and explicit release")
4065 << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml")
4068 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4069 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4070 << (QList<QVariant>() << QVariant() << QVariant())
4072 QTest::newRow("var: binding with same js objects and explicit release")
4073 << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml")
4076 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4077 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4078 << (QList<QVariant>() << QVariant() << QVariant())
4082 /* property variant semantics */
4084 // in the following three cases, the instance created from the component
4085 // has a property which is a copy of the scarce resource; hence, the
4086 // resource should NOT be detached prior to deletion of the object instance,
4087 // unless the resource is destroyed explicitly.
4088 QTest::newRow("variant: import scarce resource copy directly")
4089 << testFileUrl("scarceResourceCopy.variant.qml")
4091 << false // won't be detached, because assigned to property and not explicitly released
4092 << (QStringList() << QLatin1String("scarceResourceCopy"))
4093 << (QList<QVariant>() << true)
4094 << (QList<QVariant>() << origPixmap)
4097 QTest::newRow("variant: import scarce resource copy from JS")
4098 << testFileUrl("scarceResourceCopyFromJs.variant.qml")
4100 << false // won't be detached, because assigned to property and not explicitly released
4101 << (QStringList() << QLatin1String("scarceResourceCopy"))
4102 << (QList<QVariant>() << true)
4103 << (QList<QVariant>() << origPixmap)
4106 QTest::newRow("variant: import released scarce resource copy from JS")
4107 << testFileUrl("scarceResourceDestroyedCopy.variant.qml")
4109 << true // explicitly released, so it will be detached
4110 << (QStringList() << QLatin1String("scarceResourceCopy"))
4111 << (QList<QVariant>() << false)
4112 << (QList<QVariant>() << QVariant())
4115 // in the following three cases, no other copy should exist in memory,
4116 // and so it should be detached (unless explicitly preserved).
4117 QTest::newRow("variant: import auto-release SR from JS in binding side-effect")
4118 << testFileUrl("scarceResourceTest.variant.qml")
4120 << true // auto released, so it will be detached
4121 << (QStringList() << QLatin1String("scarceResourceTest"))
4122 << (QList<QVariant>() << true)
4123 << (QList<QVariant>() << QVariant(100))
4125 QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect")
4126 << testFileUrl("scarceResourceTestPreserve.variant.qml")
4128 << false // won't be detached because we explicitly preserve it
4129 << (QStringList() << QLatin1String("scarceResourceTest"))
4130 << (QList<QVariant>() << true)
4131 << (QList<QVariant>() << QVariant(100))
4133 QTest::newRow("variant: import multiple scarce resources")
4134 << testFileUrl("scarceResourceTestMultiple.variant.qml")
4136 << true // will be detached because all resources were released manually or automatically.
4137 << (QStringList() << QLatin1String("scarceResourceTest"))
4138 << (QList<QVariant>() << true)
4139 << (QList<QVariant>() << QVariant(100))
4142 // In the following three cases, test that scarce resources are handled
4143 // correctly for imports.
4144 QTest::newRow("variant: import with no binding")
4145 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4146 << false // cannot check detach status.
4149 << QList<QVariant>()
4150 << QList<QVariant>()
4152 QTest::newRow("variant: import with binding without explicit preserve")
4153 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4156 << (QStringList() << QLatin1String("scarceResourceCopy"))
4157 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4158 << (QList<QVariant>() << QVariant())
4160 QTest::newRow("variant: import with explicit release after binding evaluation")
4161 << testFileUrl("scarceResourceCopyImport.variant.qml")
4164 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo"))
4165 << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies.
4166 << (QList<QVariant>() << origPixmap << origPixmap << QVariant())
4170 void tst_qqmlecmascript::scarceResources()
4172 QFETCH(QUrl, qmlFile);
4173 QFETCH(bool, readDetachStatus);
4174 QFETCH(bool, expectedDetachStatus);
4175 QFETCH(QStringList, propertyNames);
4176 QFETCH(QVariantList, expectedValidity);
4177 QFETCH(QVariantList, expectedValues);
4178 QFETCH(QStringList, expectedErrors);
4180 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4181 ScarceResourceObject *eo = 0;
4182 QObject *object = 0;
4184 QQmlComponent c(&engine, qmlFile);
4185 object = c.create();
4186 QVERIFY(object != 0);
4187 for (int i = 0; i < propertyNames.size(); ++i) {
4188 QString prop = propertyNames.at(i);
4189 bool validity = expectedValidity.at(i).toBool();
4190 QVariant value = expectedValues.at(i);
4192 QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity);
4193 if (value.type() == QVariant::Int) {
4194 QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt());
4195 } else if (value.type() == QVariant::Pixmap) {
4196 QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>());
4200 if (readDetachStatus) {
4201 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4202 QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
4205 QVERIFY(ep->scarceResources.isEmpty());
4209 void tst_qqmlecmascript::propertyChangeSlots()
4211 // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
4212 QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml"));
4213 QObject *object = component.create();
4214 QVERIFY(object != 0);
4217 // ensure that invalid property names fail properly.
4218 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4219 QQmlComponent e1(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.1.qml"));
4220 QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
4221 QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
4222 object = e1.create();
4223 QVERIFY(object == 0);
4226 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4227 QQmlComponent e2(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.2.qml"));
4228 expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
4229 QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
4230 object = e2.create();
4231 QVERIFY(object == 0);
4234 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4235 QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml"));
4236 expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
4237 QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
4238 object = e3.create();
4239 QVERIFY(object == 0);
4242 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4243 QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml"));
4244 expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
4245 QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
4246 object = e4.create();
4247 QVERIFY(object == 0);
4251 void tst_qqmlecmascript::propertyVar_data()
4253 QTest::addColumn<QUrl>("qmlFile");
4256 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyVar.1.qml");
4257 QTest::newRow("non-bindable object changed") << testFileUrl("propertyVar.2.qml");
4258 QTest::newRow("primitive changed") << testFileUrl("propertyVar.3.qml");
4259 QTest::newRow("javascript array modification") << testFileUrl("propertyVar.4.qml");
4260 QTest::newRow("javascript map modification") << testFileUrl("propertyVar.5.qml");
4261 QTest::newRow("javascript array assignment") << testFileUrl("propertyVar.6.qml");
4262 QTest::newRow("javascript map assignment") << testFileUrl("propertyVar.7.qml");
4263 QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml");
4264 QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml");
4265 QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml");
4266 QTest::newRow("javascript function assignment") << testFileUrl("propertyVar.11.qml");
4267 QTest::newRow("javascript special assignment") << testFileUrl("propertyVar.12.qml");
4268 QTest::newRow("declarative binding assignment") << testFileUrl("propertyVar.13.qml");
4269 QTest::newRow("imperative binding assignment") << testFileUrl("propertyVar.14.qml");
4270 QTest::newRow("stored binding assignment") << testFileUrl("propertyVar.15.qml");
4271 QTest::newRow("function expression binding assignment") << testFileUrl("propertyVar.16.qml");
4274 void tst_qqmlecmascript::propertyVar()
4276 QFETCH(QUrl, qmlFile);
4278 QQmlComponent component(&engine, qmlFile);
4279 QObject *object = component.create();
4280 QVERIFY(object != 0);
4282 QCOMPARE(object->property("test").toBool(), true);
4287 void tst_qqmlecmascript::propertyQJSValue_data()
4289 QTest::addColumn<QUrl>("qmlFile");
4292 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyQJSValue.1.qml");
4293 QTest::newRow("non-bindable object changed") << testFileUrl("propertyQJSValue.2.qml");
4294 QTest::newRow("primitive changed") << testFileUrl("propertyQJSValue.3.qml");
4295 QTest::newRow("javascript array modification") << testFileUrl("propertyQJSValue.4.qml");
4296 QTest::newRow("javascript map modification") << testFileUrl("propertyQJSValue.5.qml");
4297 QTest::newRow("javascript array assignment") << testFileUrl("propertyQJSValue.6.qml");
4298 QTest::newRow("javascript map assignment") << testFileUrl("propertyQJSValue.7.qml");
4299 QTest::newRow("literal property assignment") << testFileUrl("propertyQJSValue.8.qml");
4300 QTest::newRow("qobject property assignment") << testFileUrl("propertyQJSValue.9.qml");
4301 QTest::newRow("base class var property assignment") << testFileUrl("propertyQJSValue.10.qml");
4302 QTest::newRow("javascript function assignment") << testFileUrl("propertyQJSValue.11.qml");
4303 QTest::newRow("javascript special assignment") << testFileUrl("propertyQJSValue.12.qml");
4304 QTest::newRow("declarative binding assignment") << testFileUrl("propertyQJSValue.13.qml");
4305 QTest::newRow("imperative binding assignment") << testFileUrl("propertyQJSValue.14.qml");
4306 QTest::newRow("stored binding assignment") << testFileUrl("propertyQJSValue.15.qml");
4307 QTest::newRow("javascript function binding") << testFileUrl("propertyQJSValue.16.qml");
4309 QTest::newRow("reset property") << testFileUrl("propertyQJSValue.reset.qml");
4310 QTest::newRow("reset property in binding") << testFileUrl("propertyQJSValue.bindingreset.qml");
4313 void tst_qqmlecmascript::propertyQJSValue()
4315 QFETCH(QUrl, qmlFile);
4317 QQmlComponent component(&engine, qmlFile);
4318 QObject *object = component.create();
4319 QVERIFY(object != 0);
4321 QCOMPARE(object->property("test").toBool(), true);
4326 // Tests that we can write QVariant values to var properties from C++
4327 void tst_qqmlecmascript::propertyVarCpp()
4329 QObject *object = 0;
4331 // ensure that writing to and reading from a var property from cpp works as required.
4332 // Literal values stored in var properties can be read and written as QVariants
4333 // of a specific type, whereas object values are read as QVariantMaps.
4334 QQmlComponent component(&engine, testFileUrl("propertyVarCpp.qml"));
4335 object = component.create();
4336 QVERIFY(object != 0);
4337 // assign int to property var that currently has int assigned
4338 QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
4339 QCOMPARE(object->property("varBound"), QVariant(15));
4340 QCOMPARE(object->property("intBound"), QVariant(15));
4341 QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
4342 QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
4343 // assign string to property var that current has bool assigned
4344 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
4345 QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
4346 QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
4347 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
4348 // now enforce behaviour when accessing JavaScript objects from cpp.
4349 QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
4353 static void gc(QQmlEngine &engine)
4355 engine.collectGarbage();
4356 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4357 QCoreApplication::processEvents();
4360 void tst_qqmlecmascript::propertyVarOwnership()
4362 // Referenced JS objects are not collected
4364 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.qml"));
4365 QObject *object = component.create();
4366 QVERIFY(object != 0);
4367 QCOMPARE(object->property("test").toBool(), false);
4368 QMetaObject::invokeMethod(object, "runTest");
4369 QCOMPARE(object->property("test").toBool(), true);
4372 // Referenced JS objects are not collected
4374 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml"));
4375 QObject *object = component.create();
4376 QVERIFY(object != 0);
4377 QCOMPARE(object->property("test").toBool(), false);
4378 QMetaObject::invokeMethod(object, "runTest");
4379 QCOMPARE(object->property("test").toBool(), true);
4382 // Qt objects are not collected until they've been dereferenced
4384 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml"));
4385 QObject *object = component.create();
4386 QVERIFY(object != 0);
4388 QCOMPARE(object->property("test2").toBool(), false);
4389 QCOMPARE(object->property("test2").toBool(), false);
4391 QMetaObject::invokeMethod(object, "runTest");
4392 QCOMPARE(object->property("test1").toBool(), true);
4394 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4395 QVERIFY(!referencedObject.isNull());
4397 QVERIFY(!referencedObject.isNull());
4399 QMetaObject::invokeMethod(object, "runTest2");
4400 QCOMPARE(object->property("test2").toBool(), true);
4402 QVERIFY(referencedObject.isNull());
4406 // Self reference does not prevent Qt object collection
4408 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml"));
4409 QObject *object = component.create();
4410 QVERIFY(object != 0);
4412 QCOMPARE(object->property("test").toBool(), true);
4414 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4415 QVERIFY(!referencedObject.isNull());
4417 QVERIFY(!referencedObject.isNull());
4419 QMetaObject::invokeMethod(object, "runTest");
4421 QVERIFY(referencedObject.isNull());
4425 // Garbage collection cannot result in attempted dereference of empty handle
4427 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.5.qml"));
4428 QObject *object = component.create();
4429 QVERIFY(object != 0);
4430 QMetaObject::invokeMethod(object, "runTest");
4431 QCOMPARE(object->property("test").toBool(), true);
4436 void tst_qqmlecmascript::propertyVarImplicitOwnership()
4438 // The childObject has a reference to a different QObject. We want to ensure
4439 // that the different item will not be cleaned up until required. IE, the childObject
4440 // has implicit ownership of the constructed QObject.
4441 QQmlComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml"));
4442 QObject *object = component.create();
4443 QVERIFY(object != 0);
4444 QMetaObject::invokeMethod(object, "assignCircular");
4445 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4446 QCoreApplication::processEvents();
4447 QObject *rootObject = object->property("vp").value<QObject*>();
4448 QVERIFY(rootObject != 0);
4449 QObject *childObject = rootObject->findChild<QObject*>("text");
4450 QVERIFY(childObject != 0);
4451 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4452 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4453 QMetaObject::invokeMethod(childObject, "constructQObject"); // creates a reference to a constructed QObject.
4454 QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
4455 QVERIFY(!qobjectGuard.isNull());
4456 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4457 QCoreApplication::processEvents();
4458 QVERIFY(!qobjectGuard.isNull());
4459 QMetaObject::invokeMethod(object, "deassignCircular");
4460 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4461 QCoreApplication::processEvents();
4462 QVERIFY(qobjectGuard.isNull()); // should have been collected now.
4466 void tst_qqmlecmascript::propertyVarReparent()
4468 // ensure that nothing breaks if we re-parent objects
4469 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4470 QObject *object = component.create();
4471 QVERIFY(object != 0);
4472 QMetaObject::invokeMethod(object, "assignVarProp");
4473 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4474 QCoreApplication::processEvents();
4475 QObject *rect = object->property("vp").value<QObject*>();
4476 QObject *text = rect->findChild<QObject*>("textOne");
4477 QObject *text2 = rect->findChild<QObject*>("textTwo");
4478 QWeakPointer<QObject> rectGuard(rect);
4479 QWeakPointer<QObject> textGuard(text);
4480 QWeakPointer<QObject> text2Guard(text2);
4481 QVERIFY(!rectGuard.isNull());
4482 QVERIFY(!textGuard.isNull());
4483 QVERIFY(!text2Guard.isNull());
4484 QCOMPARE(text->property("textCanary").toInt(), 11);
4485 QCOMPARE(text2->property("textCanary").toInt(), 12);
4486 // now construct an image which we will reparent.
4487 QMetaObject::invokeMethod(text2, "constructQObject");
4488 QObject *image = text2->property("vp").value<QObject*>();
4489 QWeakPointer<QObject> imageGuard(image);
4490 QVERIFY(!imageGuard.isNull());
4491 QCOMPARE(image->property("imageCanary").toInt(), 13);
4492 // now reparent the "Image" object (currently, it has JS ownership)
4493 image->setParent(text); // shouldn't be collected after deassignVp now, since has a parent.
4494 QMetaObject::invokeMethod(text2, "deassignVp");
4495 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4496 QCoreApplication::processEvents();
4497 QCOMPARE(text->property("textCanary").toInt(), 11);
4498 QCOMPARE(text2->property("textCanary").toInt(), 22);
4499 QVERIFY(!imageGuard.isNull()); // should still be alive.
4500 QCOMPARE(image->property("imageCanary").toInt(), 13); // still able to access var properties
4501 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4502 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4503 QCoreApplication::processEvents();
4504 QVERIFY(imageGuard.isNull()); // should now have been deleted, due to parent being deleted.
4508 void tst_qqmlecmascript::propertyVarReparentNullContext()
4510 // sometimes reparenting can cause problems
4511 // (eg, if the ctxt is collected, varproperties are no longer available)
4512 // this test ensures that no crash occurs in that situation.
4513 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4514 QObject *object = component.create();
4515 QVERIFY(object != 0);
4516 QMetaObject::invokeMethod(object, "assignVarProp");
4517 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4518 QCoreApplication::processEvents();
4519 QObject *rect = object->property("vp").value<QObject*>();
4520 QObject *text = rect->findChild<QObject*>("textOne");
4521 QObject *text2 = rect->findChild<QObject*>("textTwo");
4522 QWeakPointer<QObject> rectGuard(rect);
4523 QWeakPointer<QObject> textGuard(text);
4524 QWeakPointer<QObject> text2Guard(text2);
4525 QVERIFY(!rectGuard.isNull());
4526 QVERIFY(!textGuard.isNull());
4527 QVERIFY(!text2Guard.isNull());
4528 QCOMPARE(text->property("textCanary").toInt(), 11);
4529 QCOMPARE(text2->property("textCanary").toInt(), 12);
4530 // now construct an image which we will reparent.
4531 QMetaObject::invokeMethod(text2, "constructQObject");
4532 QObject *image = text2->property("vp").value<QObject*>();
4533 QWeakPointer<QObject> imageGuard(image);
4534 QVERIFY(!imageGuard.isNull());
4535 QCOMPARE(image->property("imageCanary").toInt(), 13);
4536 // now reparent the "Image" object (currently, it has JS ownership)
4537 image->setParent(object); // reparented to base object. after deassignVarProp, the ctxt will be invalid.
4538 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4539 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4540 QCoreApplication::processEvents();
4541 QVERIFY(!imageGuard.isNull()); // should still be alive.
4542 QVERIFY(!image->property("imageCanary").isValid()); // but varProperties won't be available (null context).
4544 QVERIFY(imageGuard.isNull()); // should now be dead.
4547 void tst_qqmlecmascript::propertyVarCircular()
4549 // enforce behaviour regarding circular references - ensure qdvmemo deletion.
4550 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.qml"));
4551 QObject *object = component.create();
4552 QVERIFY(object != 0);
4553 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4554 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4555 QCoreApplication::processEvents();
4556 QCOMPARE(object->property("canaryInt"), QVariant(5));
4557 QVariant canaryResourceVariant = object->property("canaryResource");
4558 QVERIFY(canaryResourceVariant.isValid());
4559 QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
4560 canaryResourceVariant = QVariant(); // invalidate it to remove one copy of the pixmap from memory.
4561 QMetaObject::invokeMethod(object, "deassignCanaryResource"); // remove one copy of the pixmap from memory
4562 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4563 QCoreApplication::processEvents();
4564 QVERIFY(!canaryResourcePixmap.isDetached()); // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
4565 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4566 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4567 QCoreApplication::processEvents();
4568 QCOMPARE(object->property("canaryInt"), QVariant(2));
4569 QCOMPARE(object->property("canaryResource"), QVariant(1));
4570 QVERIFY(canaryResourcePixmap.isDetached()); // now detached, since orig copy was member of qdvmemo which was deleted.
4574 void tst_qqmlecmascript::propertyVarCircular2()
4576 // track deletion of JS-owned parent item with Cpp-owned child
4577 // where the child has a var property referencing its parent.
4578 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4579 QObject *object = component.create();
4580 QVERIFY(object != 0);
4581 QMetaObject::invokeMethod(object, "assignCircular");
4582 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4583 QCoreApplication::processEvents();
4584 QObject *rootObject = object->property("vp").value<QObject*>();
4585 QVERIFY(rootObject != 0);
4586 QObject *childObject = rootObject->findChild<QObject*>("text");
4587 QVERIFY(childObject != 0);
4588 QWeakPointer<QObject> rootObjectTracker(rootObject);
4589 QVERIFY(!rootObjectTracker.isNull());
4590 QWeakPointer<QObject> childObjectTracker(childObject);
4591 QVERIFY(!childObjectTracker.isNull());
4593 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4594 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4595 QMetaObject::invokeMethod(object, "deassignCircular");
4596 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4597 QCoreApplication::processEvents();
4598 QVERIFY(rootObjectTracker.isNull()); // should have been collected
4599 QVERIFY(childObjectTracker.isNull()); // should have been collected
4603 void tst_qqmlecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
4605 *(int*)(parameter) += 1;
4606 qPersistentDispose(object);
4609 void tst_qqmlecmascript::propertyVarInheritance()
4611 int propertyVarWeakRefCallbackCount = 0;
4613 // enforce behaviour regarding element inheritance - ensure handle disposal.
4614 // The particular component under test here has a chain of references.
4615 QQmlComponent component(&engine, testFileUrl("propertyVar.inherit.qml"));
4616 QObject *object = component.create();
4617 QVERIFY(object != 0);
4618 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4619 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4620 QCoreApplication::processEvents();
4621 // we want to be able to track when the varProperties array of the last metaobject is disposed
4622 QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
4623 QObject *ico5 = object->property("varProperty").value<QObject*>()->property("inheritanceVarProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
4624 QQmlVMEMetaObject *icovmemo = QQmlVMEMetaObject::get(ico5);
4625 QQmlVMEMetaObject *ccovmemo = QQmlVMEMetaObject::get(cco5);
4626 v8::Persistent<v8::Value> icoCanaryHandle;
4627 v8::Persistent<v8::Value> ccoCanaryHandle;
4630 // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only
4631 // public function which can return us a handle to something in the varProperties array.
4632 icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ")));
4633 ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")));
4634 // we make them weak and invoke the gc, but we should not hit the weak-callback yet
4635 // as the varproperties array of each vmemo still references the resource.
4636 icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4637 ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4639 QVERIFY(propertyVarWeakRefCallbackCount == 0);
4641 // now we deassign the var prop, which should trigger collection of item subtrees.
4642 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4643 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4644 QCoreApplication::processEvents();
4645 // ensure that there are only weak handles to the underlying varProperties array remaining.
4647 QCOMPARE(propertyVarWeakRefCallbackCount, 2); // should have been called for both, since all refs should be weak.
4649 // since there are no parent vmemo's to keep implicit references alive, and the only handles
4650 // to what remains are weak, all varProperties arrays must have been collected.
4653 void tst_qqmlecmascript::propertyVarInheritance2()
4655 int propertyVarWeakRefCallbackCount = 0;
4657 // The particular component under test here does NOT have a chain of references; the
4658 // only link between rootObject and childObject is that rootObject is the parent of childObject.
4659 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4660 QObject *object = component.create();
4661 QVERIFY(object != 0);
4662 QMetaObject::invokeMethod(object, "assignCircular");
4663 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4664 QCoreApplication::processEvents();
4665 QObject *rootObject = object->property("vp").value<QObject*>();
4666 QVERIFY(rootObject != 0);
4667 QObject *childObject = rootObject->findChild<QObject*>("text");
4668 QVERIFY(childObject != 0);
4669 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4670 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4671 v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
4674 propertyVarWeakRefCallbackCount = 0; // reset callback count.
4675 childObjectVarArrayValueHandle = qPersistentNew(QQmlVMEMetaObject::get(childObject)->vmeProperty(childObject->metaObject()->indexOfProperty("vp")));
4676 childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4678 QVERIFY(propertyVarWeakRefCallbackCount == 0); // should not have been collected yet.
4679 QCOMPARE(childObject->property("vp").value<QObject*>(), rootObject);
4680 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4682 QMetaObject::invokeMethod(object, "deassignCircular");
4683 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4684 QCoreApplication::processEvents();
4685 QVERIFY(propertyVarWeakRefCallbackCount == 1); // should have been collected now.
4689 // Ensure that QObject type conversion works on binding assignment
4690 void tst_qqmlecmascript::elementAssign()
4692 QQmlComponent component(&engine, testFileUrl("elementAssign.qml"));
4694 QObject *object = component.create();
4695 QVERIFY(object != 0);
4697 QCOMPARE(object->property("test").toBool(), true);
4703 void tst_qqmlecmascript::objectPassThroughSignals()
4705 QQmlComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml"));
4707 QObject *object = component.create();
4708 QVERIFY(object != 0);
4710 QCOMPARE(object->property("test").toBool(), true);
4716 void tst_qqmlecmascript::objectConversion()
4718 QQmlComponent component(&engine, testFileUrl("objectConversion.qml"));
4720 QObject *object = component.create();
4721 QVERIFY(object != 0);
4723 QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
4724 QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
4731 void tst_qqmlecmascript::booleanConversion()
4733 QQmlComponent component(&engine, testFileUrl("booleanConversion.qml"));
4735 QObject *object = component.create();
4736 QVERIFY(object != 0);
4738 QCOMPARE(object->property("test_true1").toBool(), true);
4739 QCOMPARE(object->property("test_true2").toBool(), true);
4740 QCOMPARE(object->property("test_true3").toBool(), true);
4741 QCOMPARE(object->property("test_true4").toBool(), true);
4742 QCOMPARE(object->property("test_true5").toBool(), true);
4744 QCOMPARE(object->property("test_false1").toBool(), false);
4745 QCOMPARE(object->property("test_false2").toBool(), false);
4746 QCOMPARE(object->property("test_false3").toBool(), false);
4751 void tst_qqmlecmascript::handleReferenceManagement()
4756 // Linear QObject reference
4757 QQmlEngine hrmEngine;
4758 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml"));
4759 QObject *object = component.create();
4760 QVERIFY(object != 0);
4761 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4762 cro->setEngine(&hrmEngine);
4763 cro->setDtorCount(&dtorCount);
4764 QMetaObject::invokeMethod(object, "createReference");
4766 QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
4768 hrmEngine.collectGarbage();
4769 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4770 QCoreApplication::processEvents();
4771 QCOMPARE(dtorCount, 3);
4776 // Circular QObject reference
4777 QQmlEngine hrmEngine;
4778 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml"));
4779 QObject *object = component.create();
4780 QVERIFY(object != 0);
4781 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4782 cro->setEngine(&hrmEngine);
4783 cro->setDtorCount(&dtorCount);
4784 QMetaObject::invokeMethod(object, "circularReference");
4786 QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
4788 hrmEngine.collectGarbage();
4789 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4790 QCoreApplication::processEvents();
4791 QCOMPARE(dtorCount, 3);
4796 // Linear handle reference
4797 QQmlEngine hrmEngine;
4798 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.1.qml"));
4799 QObject *object = component.create();
4800 QVERIFY(object != 0);
4801 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4803 crh->setEngine(&hrmEngine);
4804 crh->setDtorCount(&dtorCount);
4805 QMetaObject::invokeMethod(object, "createReference");
4806 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4807 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4808 QVERIFY(first != 0);
4809 QVERIFY(second != 0);
4810 first->addReference(QQmlData::get(second)->v8object); // create reference
4811 // now we have to reparent second and make second owned by JS.
4812 second->setParent(0);
4813 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
4815 QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
4817 hrmEngine.collectGarbage();
4818 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4819 QCoreApplication::processEvents();
4820 QCOMPARE(dtorCount, 3);
4825 // Circular handle reference
4826 QQmlEngine hrmEngine;
4827 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.2.qml"));
4828 QObject *object = component.create();
4829 QVERIFY(object != 0);
4830 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4832 crh->setEngine(&hrmEngine);
4833 crh->setDtorCount(&dtorCount);
4834 QMetaObject::invokeMethod(object, "circularReference");
4835 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4836 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4837 QVERIFY(first != 0);
4838 QVERIFY(second != 0);
4839 first->addReference(QQmlData::get(second)->v8object); // create circular reference
4840 second->addReference(QQmlData::get(first)->v8object); // note: must be weak.
4841 // now we have to reparent and change ownership, and unset the property references.
4842 first->setParent(0);
4843 second->setParent(0);
4844 QQmlEngine::setObjectOwnership(first, QQmlEngine::JavaScriptOwnership);
4845 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
4846 object->setProperty("first", QVariant::fromValue<QObject*>(0));
4847 object->setProperty("second", QVariant::fromValue<QObject*>(0));
4849 QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
4851 hrmEngine.collectGarbage();
4852 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4853 QCoreApplication::processEvents();
4854 QCOMPARE(dtorCount, 3);
4859 // multiple engine interaction - linear reference
4860 QQmlEngine hrmEngine1;
4861 QQmlEngine hrmEngine2;
4862 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4863 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4864 QObject *object1 = component1.create();
4865 QObject *object2 = component2.create();
4866 QVERIFY(object1 != 0);
4867 QVERIFY(object2 != 0);
4868 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4869 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4872 crh1->setEngine(&hrmEngine1);
4873 crh2->setEngine(&hrmEngine2);
4874 crh1->setDtorCount(&dtorCount);
4875 crh2->setDtorCount(&dtorCount);
4876 QMetaObject::invokeMethod(object1, "createReference");
4877 QMetaObject::invokeMethod(object2, "createReference");
4878 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4879 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4880 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4881 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4882 QVERIFY(first1 != 0);
4883 QVERIFY(second1 != 0);
4884 QVERIFY(first2 != 0);
4885 QVERIFY(second2 != 0);
4886 first1->addReference(QQmlData::get(second2)->v8object); // create reference across engines
4887 // now we have to reparent second2 and make second2 owned by JS.
4888 second2->setParent(0);
4889 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4891 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4892 QCoreApplication::processEvents();
4893 QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
4896 hrmEngine1.collectGarbage();
4897 hrmEngine2.collectGarbage();
4898 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4899 QCoreApplication::processEvents();
4900 QCOMPARE(dtorCount, 6);
4905 // multiple engine interaction - circular reference
4906 QQmlEngine hrmEngine1;
4907 QQmlEngine hrmEngine2;
4908 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4909 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4910 QObject *object1 = component1.create();
4911 QObject *object2 = component2.create();
4912 QVERIFY(object1 != 0);
4913 QVERIFY(object2 != 0);
4914 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4915 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4918 crh1->setEngine(&hrmEngine1);
4919 crh2->setEngine(&hrmEngine2);
4920 crh1->setDtorCount(&dtorCount);
4921 crh2->setDtorCount(&dtorCount);
4922 QMetaObject::invokeMethod(object1, "createReference");
4923 QMetaObject::invokeMethod(object2, "createReference");
4924 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4925 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4926 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4927 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4928 QVERIFY(first1 != 0);
4929 QVERIFY(second1 != 0);
4930 QVERIFY(first2 != 0);
4931 QVERIFY(second2 != 0);
4932 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
4933 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
4934 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
4935 first2->addReference(QQmlData::get(first1)->v8object); // close the loop - circular ref across engines
4936 // now we have to reparent and change ownership to JS, and remove property references.
4937 first1->setParent(0);
4938 second1->setParent(0);
4939 first2->setParent(0);
4940 second2->setParent(0);
4941 QQmlEngine::setObjectOwnership(first1, QQmlEngine::JavaScriptOwnership);
4942 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
4943 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
4944 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4945 object1->setProperty("first", QVariant::fromValue<QObject*>(0));
4946 object1->setProperty("second", QVariant::fromValue<QObject*>(0));
4947 object2->setProperty("first", QVariant::fromValue<QObject*>(0));
4948 object2->setProperty("second", QVariant::fromValue<QObject*>(0));
4950 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4951 QCoreApplication::processEvents();
4952 QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
4955 hrmEngine1.collectGarbage();
4956 hrmEngine2.collectGarbage();
4957 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4958 QCoreApplication::processEvents();
4959 QCOMPARE(dtorCount, 6);
4964 // multiple engine interaction - linear reference with engine deletion
4965 QQmlEngine *hrmEngine1 = new QQmlEngine;
4966 QQmlEngine *hrmEngine2 = new QQmlEngine;
4967 QQmlComponent component1(hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4968 QQmlComponent component2(hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4969 QObject *object1 = component1.create();
4970 QObject *object2 = component2.create();
4971 QVERIFY(object1 != 0);
4972 QVERIFY(object2 != 0);
4973 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4974 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4977 crh1->setEngine(hrmEngine1);
4978 crh2->setEngine(hrmEngine2);
4979 crh1->setDtorCount(&dtorCount);
4980 crh2->setDtorCount(&dtorCount);
4981 QMetaObject::invokeMethod(object1, "createReference");
4982 QMetaObject::invokeMethod(object2, "createReference");
4983 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4984 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4985 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4986 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4987 QVERIFY(first1 != 0);
4988 QVERIFY(second1 != 0);
4989 QVERIFY(first2 != 0);
4990 QVERIFY(second2 != 0);
4991 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
4992 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
4993 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
4994 // now we have to reparent and change ownership to JS.
4995 first1->setParent(crh1);
4996 second1->setParent(0);
4997 first2->setParent(0);
4998 second2->setParent(0);
4999 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
5000 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
5001 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5004 QCOMPARE(dtorCount, 0);
5005 delete hrmEngine2; // should trigger deletion of objects with JS ownership tracked by this engine
5007 QCOMPARE(dtorCount, 2); // first2 and second2 should have been deleted.
5011 QCOMPARE(dtorCount, 6); // deleting object1 and object2 should trigger deletion of first1 and first2.
5013 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5014 QCoreApplication::processEvents();
5015 QCOMPARE(dtorCount, 6); // all objects should have been cleaned up prior to deleting hrmEngine1.
5019 // Dynamic variant property reference keeps target alive
5020 QQmlEngine hrmEngine;
5021 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.qml"));
5022 QObject *object = component.create();
5023 QVERIFY(object != 0);
5024 QMetaObject::invokeMethod(object, "createReference");
5026 QMetaObject::invokeMethod(object, "ensureReference");
5028 QMetaObject::invokeMethod(object, "removeReference");
5030 QMetaObject::invokeMethod(object, "ensureDeletion");
5031 QCOMPARE(object->property("success").toBool(), true);
5036 // Dynamic Item property reference keeps target alive
5037 QQmlEngine hrmEngine;
5038 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.2.qml"));
5039 QObject *object = component.create();
5040 QVERIFY(object != 0);
5041 QMetaObject::invokeMethod(object, "createReference");
5043 QMetaObject::invokeMethod(object, "ensureReference");
5045 QMetaObject::invokeMethod(object, "removeReference");
5047 QMetaObject::invokeMethod(object, "ensureDeletion");
5048 QCOMPARE(object->property("success").toBool(), true);
5053 // Item property reference to deleted item doesn't crash
5054 QQmlEngine hrmEngine;
5055 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.3.qml"));
5056 QObject *object = component.create();
5057 QVERIFY(object != 0);
5058 QMetaObject::invokeMethod(object, "createReference");
5060 QMetaObject::invokeMethod(object, "ensureReference");
5062 QMetaObject::invokeMethod(object, "manuallyDelete");
5064 QMetaObject::invokeMethod(object, "ensureDeleted");
5065 QCOMPARE(object->property("success").toBool(), true);
5070 void tst_qqmlecmascript::stringArg()
5072 QQmlComponent component(&engine, testFileUrl("stringArg.qml"));
5073 QObject *object = component.create();
5074 QVERIFY(object != 0);
5075 QMetaObject::invokeMethod(object, "success");
5076 QVERIFY(object->property("returnValue").toBool());
5078 QString w1 = testFileUrl("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
5079 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5080 QMetaObject::invokeMethod(object, "failure");
5081 QVERIFY(object->property("returnValue").toBool());
5086 void tst_qqmlecmascript::readonlyDeclaration()
5088 QQmlComponent component(&engine, testFileUrl("readonlyDeclaration.qml"));
5090 QObject *object = component.create();
5091 QVERIFY(object != 0);
5093 QCOMPARE(object->property("test").toBool(), true);
5098 Q_DECLARE_METATYPE(QList<int>)
5099 Q_DECLARE_METATYPE(QList<qreal>)
5100 Q_DECLARE_METATYPE(QList<bool>)
5101 Q_DECLARE_METATYPE(QList<QString>)
5102 Q_DECLARE_METATYPE(QList<QUrl>)
5103 void tst_qqmlecmascript::sequenceConversionRead()
5106 QUrl qmlFile = testFileUrl("sequenceConversion.read.qml");
5107 QQmlComponent component(&engine, qmlFile);
5108 QObject *object = component.create();
5109 QVERIFY(object != 0);
5110 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5113 QMetaObject::invokeMethod(object, "readSequences");
5114 QList<int> intList; intList << 1 << 2 << 3 << 4;
5115 QCOMPARE(object->property("intListLength").toInt(), intList.length());
5116 QCOMPARE(object->property("intList").value<QList<int> >(), intList);
5117 QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
5118 QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
5119 QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
5120 QList<bool> boolList; boolList << true << false << true << false;
5121 QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
5122 QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
5123 QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5124 QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
5125 QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
5126 QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
5127 QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
5128 QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
5129 QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5130 QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
5131 QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
5133 QMetaObject::invokeMethod(object, "readSequenceElements");
5134 QCOMPARE(object->property("intVal").toInt(), 2);
5135 QCOMPARE(object->property("qrealVal").toReal(), 2.2);
5136 QCOMPARE(object->property("boolVal").toBool(), false);
5137 QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
5138 QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
5139 QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
5141 QMetaObject::invokeMethod(object, "enumerateSequenceElements");
5142 QCOMPARE(object->property("enumerationMatches").toBool(), true);
5144 intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
5145 QQmlProperty seqProp(seq, "intListProperty");
5146 QCOMPARE(seqProp.read().value<QList<int> >(), intList);
5147 QQmlProperty seqProp2(seq, "intListProperty", &engine);
5148 QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
5150 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5151 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5157 QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml");
5158 QQmlComponent component(&engine, qmlFile);
5159 QObject *object = component.create();
5160 QVERIFY(object != 0);
5161 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5164 // we haven't registered QList<QPoint> as a sequence type.
5165 QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5166 QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
5167 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5168 QTest::ignoreMessage(QtWarningMsg, warningTwo.toLatin1().constData());
5170 QMetaObject::invokeMethod(object, "performTest");
5172 // QList<QPoint> has not been registered as a sequence type.
5173 QCOMPARE(object->property("pointListLength").toInt(), 0);
5174 QVERIFY(!object->property("pointList").isValid());
5175 QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5176 QQmlProperty seqProp(seq, "pointListProperty", &engine);
5177 QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
5183 void tst_qqmlecmascript::sequenceConversionWrite()
5186 QUrl qmlFile = testFileUrl("sequenceConversion.write.qml");
5187 QQmlComponent component(&engine, qmlFile);
5188 QObject *object = component.create();
5189 QVERIFY(object != 0);
5190 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5193 QMetaObject::invokeMethod(object, "writeSequences");
5194 QCOMPARE(object->property("success").toBool(), true);
5196 QMetaObject::invokeMethod(object, "writeSequenceElements");
5197 QCOMPARE(object->property("success").toBool(), true);
5199 QMetaObject::invokeMethod(object, "writeOtherElements");
5200 QCOMPARE(object->property("success").toBool(), true);
5202 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5203 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5209 QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml");
5210 QQmlComponent component(&engine, qmlFile);
5211 QObject *object = component.create();
5212 QVERIFY(object != 0);
5213 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5216 // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
5217 QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to an unregistered type");
5218 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5220 QMetaObject::invokeMethod(object, "performTest");
5222 QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
5223 QCOMPARE(seq->pointListProperty(), pointList);
5229 void tst_qqmlecmascript::sequenceConversionArray()
5231 // ensure that in JS the returned sequences act just like normal JS Arrays.
5232 QUrl qmlFile = testFileUrl("sequenceConversion.array.qml");
5233 QQmlComponent component(&engine, qmlFile);
5234 QObject *object = component.create();
5235 QVERIFY(object != 0);
5236 QMetaObject::invokeMethod(object, "indexedAccess");
5237 QVERIFY(object->property("success").toBool());
5238 QMetaObject::invokeMethod(object, "arrayOperations");
5239 QVERIFY(object->property("success").toBool());
5240 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5241 QVERIFY(object->property("success").toBool());
5242 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5243 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5248 void tst_qqmlecmascript::sequenceConversionIndexes()
5250 // ensure that we gracefully fail if unsupported index values are specified.
5251 // Qt container classes only support non-negative, signed integer index values.
5252 QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
5253 QQmlComponent component(&engine, qmlFile);
5254 QObject *object = component.create();
5255 QVERIFY(object != 0);
5256 QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
5257 QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
5258 QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
5259 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
5260 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
5261 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
5262 QMetaObject::invokeMethod(object, "indexedAccess");
5263 QVERIFY(object->property("success").toBool());
5267 void tst_qqmlecmascript::sequenceConversionThreads()
5269 // ensure that sequence conversion operations work correctly in a worker thread
5270 // and that serialisation between the main and worker thread succeeds.
5271 QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml");
5272 QQmlComponent component(&engine, qmlFile);
5273 QObject *object = component.create();
5274 QVERIFY(object != 0);
5276 QMetaObject::invokeMethod(object, "testIntSequence");
5277 QTRY_VERIFY(object->property("finished").toBool());
5278 QVERIFY(object->property("success").toBool());
5280 QMetaObject::invokeMethod(object, "testQrealSequence");
5281 QTRY_VERIFY(object->property("finished").toBool());
5282 QVERIFY(object->property("success").toBool());
5284 QMetaObject::invokeMethod(object, "testBoolSequence");
5285 QTRY_VERIFY(object->property("finished").toBool());
5286 QVERIFY(object->property("success").toBool());
5288 QMetaObject::invokeMethod(object, "testStringSequence");
5289 QTRY_VERIFY(object->property("finished").toBool());
5290 QVERIFY(object->property("success").toBool());
5292 QMetaObject::invokeMethod(object, "testQStringSequence");
5293 QTRY_VERIFY(object->property("finished").toBool());
5294 QVERIFY(object->property("success").toBool());
5296 QMetaObject::invokeMethod(object, "testUrlSequence");
5297 QTRY_VERIFY(object->property("finished").toBool());
5298 QVERIFY(object->property("success").toBool());
5300 QMetaObject::invokeMethod(object, "testVariantSequence");
5301 QTRY_VERIFY(object->property("finished").toBool());
5302 QVERIFY(object->property("success").toBool());
5307 void tst_qqmlecmascript::sequenceConversionBindings()
5310 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml");
5311 QQmlComponent component(&engine, qmlFile);
5312 QObject *object = component.create();
5313 QVERIFY(object != 0);
5314 QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
5315 QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
5316 QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
5317 QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
5318 QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
5323 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml");
5324 QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
5325 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5326 QQmlComponent component(&engine, qmlFile);
5327 QObject *object = component.create();
5328 QVERIFY(object != 0);
5333 void tst_qqmlecmascript::sequenceConversionCopy()
5335 QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml");
5336 QQmlComponent component(&engine, qmlFile);
5337 QObject *object = component.create();
5338 QVERIFY(object != 0);
5339 QMetaObject::invokeMethod(object, "testCopySequences");
5340 QCOMPARE(object->property("success").toBool(), true);
5341 QMetaObject::invokeMethod(object, "readSequenceCopyElements");
5342 QCOMPARE(object->property("success").toBool(), true);
5343 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5344 QCOMPARE(object->property("success").toBool(), true);
5348 void tst_qqmlecmascript::assignSequenceTypes()
5350 // test binding array to sequence type property
5352 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml"));
5353 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5354 QVERIFY(object != 0);
5355 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5356 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5357 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5358 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5359 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5360 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5364 // test binding literal to sequence type property
5366 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml"));
5367 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5368 QVERIFY(object != 0);
5369 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5370 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5371 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5372 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5373 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5374 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5378 // test binding single value to sequence type property
5380 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml"));
5381 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5382 QVERIFY(object != 0);
5383 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5384 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5385 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5386 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5390 // test assigning array to sequence type property in js function
5392 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml"));
5393 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5394 QVERIFY(object != 0);
5395 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5396 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5397 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5398 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5399 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5400 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5404 // test assigning literal to sequence type property in js function
5406 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml"));
5407 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5408 QVERIFY(object != 0);
5409 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5410 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5411 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5412 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5413 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5414 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5418 // test assigning single value to sequence type property in js function
5420 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml"));
5421 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5422 QVERIFY(object != 0);
5423 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5424 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5425 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5426 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5430 // test QList<QUrl> literal assignment and binding assignment causes url resolution when required
5432 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml"));
5433 QObject *object = component.create();
5434 QVERIFY(object != 0);
5435 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
5436 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
5437 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
5438 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
5439 MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco5"));
5440 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0);
5441 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5442 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5443 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5444 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5445 QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5450 // Test that assigning a null object works
5451 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
5452 void tst_qqmlecmascript::nullObjectBinding()
5454 QQmlComponent component(&engine, testFileUrl("nullObjectBinding.qml"));
5456 QObject *object = component.create();
5457 QVERIFY(object != 0);
5459 QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
5464 // Test that bindings don't evaluate once the engine has been destroyed
5465 void tst_qqmlecmascript::deletedEngine()
5467 QQmlEngine *engine = new QQmlEngine;
5468 QQmlComponent component(engine, testFileUrl("deletedEngine.qml"));
5470 QObject *object = component.create();
5471 QVERIFY(object != 0);
5473 QCOMPARE(object->property("a").toInt(), 39);
5474 object->setProperty("b", QVariant(9));
5475 QCOMPARE(object->property("a").toInt(), 117);
5479 QCOMPARE(object->property("a").toInt(), 117);
5480 object->setProperty("b", QVariant(10));
5481 QCOMPARE(object->property("a").toInt(), 117);
5486 // Test the crashing part of QTBUG-9705
5487 void tst_qqmlecmascript::libraryScriptAssert()
5489 QQmlComponent component(&engine, testFileUrl("libraryScriptAssert.qml"));
5491 QObject *object = component.create();
5492 QVERIFY(object != 0);
5497 void tst_qqmlecmascript::variantsAssignedUndefined()
5499 QQmlComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml"));
5501 QObject *object = component.create();
5502 QVERIFY(object != 0);
5504 QCOMPARE(object->property("test1").toInt(), 10);
5505 QCOMPARE(object->property("test2").toInt(), 11);
5507 object->setProperty("runTest", true);
5509 QCOMPARE(object->property("test1"), QVariant());
5510 QCOMPARE(object->property("test2"), QVariant());
5516 void tst_qqmlecmascript::qtbug_9792()
5518 QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml"));
5520 QQmlContext *context = new QQmlContext(engine.rootContext());
5522 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
5523 QVERIFY(object != 0);
5525 QTest::ignoreMessage(QtDebugMsg, "Hello world!");
5526 object->basicSignal();
5530 transientErrorsMsgCount = 0;
5531 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5533 object->basicSignal();
5535 qInstallMsgHandler(old);
5537 QCOMPARE(transientErrorsMsgCount, 0);
5542 // Verifies that QQmlGuard<>s used in the vmemetaobject are cleaned correctly
5543 void tst_qqmlecmascript::qtcreatorbug_1289()
5545 QQmlComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml"));
5547 QObject *o = component.create();
5550 QObject *nested = qvariant_cast<QObject *>(o->property("object"));
5551 QVERIFY(nested != 0);
5553 QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
5556 nested = qvariant_cast<QObject *>(o->property("object"));
5557 QVERIFY(nested == 0);
5559 // If the bug is present, the next line will crash
5563 // Test that we shut down without stupid warnings
5564 void tst_qqmlecmascript::noSpuriousWarningsAtShutdown()
5567 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml"));
5569 QObject *o = component.create();
5571 transientErrorsMsgCount = 0;
5572 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5576 qInstallMsgHandler(old);
5578 QCOMPARE(transientErrorsMsgCount, 0);
5583 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml"));
5585 QObject *o = component.create();
5587 transientErrorsMsgCount = 0;
5588 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5592 qInstallMsgHandler(old);
5594 QCOMPARE(transientErrorsMsgCount, 0);
5598 void tst_qqmlecmascript::canAssignNullToQObject()
5601 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml"));
5603 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5606 QVERIFY(o->objectProperty() != 0);
5608 o->setProperty("runTest", true);
5610 QVERIFY(o->objectProperty() == 0);
5616 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml"));
5618 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5621 QVERIFY(o->objectProperty() == 0);
5627 void tst_qqmlecmascript::functionAssignment_fromBinding()
5629 QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml"));
5631 QString url = component.url().toString();
5632 QString w1 = url + ":4:25: Unable to assign a function to a property of any type other than var.";
5633 QString w2 = url + ":5:25: Invalid use of Qt.binding() in a binding declaration.";
5634 QString w3 = url + ":6:21: Invalid use of Qt.binding() in a binding declaration.";
5635 QString w4 = url + ":7:15: Invalid use of Qt.binding() in a binding declaration.";
5636 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5637 QTest::ignoreMessage(QtWarningMsg, w2.toLatin1().constData());
5638 QTest::ignoreMessage(QtWarningMsg, w3.toLatin1().constData());
5639 QTest::ignoreMessage(QtWarningMsg, w4.toLatin1().constData());
5641 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5644 QVERIFY(!o->property("a").isValid());
5649 void tst_qqmlecmascript::functionAssignment_fromJS()
5651 QFETCH(QString, triggerProperty);
5653 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5654 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5656 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5658 QVERIFY(!o->property("a").isValid());
5660 o->setProperty("aNumber", QVariant(5));
5661 o->setProperty(triggerProperty.toUtf8().constData(), true);
5662 QCOMPARE(o->property("a"), QVariant(50));
5664 o->setProperty("aNumber", QVariant(10));
5665 QCOMPARE(o->property("a"), QVariant(100));
5670 void tst_qqmlecmascript::functionAssignment_fromJS_data()
5672 QTest::addColumn<QString>("triggerProperty");
5674 QTest::newRow("assign to property") << "assignToProperty";
5675 QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
5677 QTest::newRow("assign to value type") << "assignToValueType";
5679 QTest::newRow("use 'this'") << "assignWithThis";
5680 QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
5683 void tst_qqmlecmascript::functionAssignmentfromJS_invalid()
5685 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5686 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5688 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5690 QVERIFY(!o->property("a").isValid());
5692 o->setProperty("assignFuncWithoutReturn", true);
5693 QVERIFY(!o->property("a").isValid());
5695 QString url = component.url().toString();
5696 QString warning = url + ":67:17: Unable to assign QString to int";
5697 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5698 o->setProperty("assignWrongType", true);
5700 warning = url + ":71:29: Unable to assign QString to int";
5701 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5702 o->setProperty("assignWrongTypeToValueType", true);
5707 void tst_qqmlecmascript::functionAssignment_afterBinding()
5709 QQmlComponent component(&engine, testFileUrl("functionAssignment.3.qml"));
5711 QString url = component.url().toString();
5712 QString w1 = url + ":16: Error: Cannot assign JavaScript function to int";
5713 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5715 QObject *o = component.create();
5717 QCOMPARE(o->property("t1"), QVariant::fromValue<int>(4)); // should have bound
5718 QCOMPARE(o->property("t2"), QVariant::fromValue<int>(2)); // should not have changed
5723 void tst_qqmlecmascript::eval()
5725 QQmlComponent component(&engine, testFileUrl("eval.qml"));
5727 QObject *o = component.create();
5730 QCOMPARE(o->property("test1").toBool(), true);
5731 QCOMPARE(o->property("test2").toBool(), true);
5732 QCOMPARE(o->property("test3").toBool(), true);
5733 QCOMPARE(o->property("test4").toBool(), true);
5734 QCOMPARE(o->property("test5").toBool(), true);
5739 void tst_qqmlecmascript::function()
5741 QQmlComponent component(&engine, testFileUrl("function.qml"));
5743 QObject *o = component.create();
5746 QCOMPARE(o->property("test1").toBool(), true);
5747 QCOMPARE(o->property("test2").toBool(), true);
5748 QCOMPARE(o->property("test3").toBool(), true);
5753 void tst_qqmlecmascript::functionException()
5755 // QTBUG-24037 - shouldn't crash.
5756 QString errstr = testFileUrl("v8functionException.qml").toString() + QLatin1String(":13: SyntaxError: Unexpected token ILLEGAL");
5757 QTest::ignoreMessage(QtWarningMsg, qPrintable(errstr));
5758 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Exception occurred during compilation of function: dynamicSlot()");
5759 QQmlComponent component(&engine, testFileUrl("v8functionException.qml"));
5760 QObject *o = component.create();
5762 QMetaObject::invokeMethod(o, "dynamicSlot");
5766 // Test the "Qt.include" method
5767 void tst_qqmlecmascript::include()
5769 // Non-library relative include
5771 QQmlComponent component(&engine, testFileUrl("include.qml"));
5772 QObject *o = component.create();
5775 QCOMPARE(o->property("test0").toInt(), 99);
5776 QCOMPARE(o->property("test1").toBool(), true);
5777 QCOMPARE(o->property("test2").toBool(), true);
5778 QCOMPARE(o->property("test2_1").toBool(), true);
5779 QCOMPARE(o->property("test3").toBool(), true);
5780 QCOMPARE(o->property("test3_1").toBool(), true);
5785 // Library relative include
5787 QQmlComponent component(&engine, testFileUrl("include_shared.qml"));
5788 QObject *o = component.create();
5791 QCOMPARE(o->property("test0").toInt(), 99);
5792 QCOMPARE(o->property("test1").toBool(), true);
5793 QCOMPARE(o->property("test2").toBool(), true);
5794 QCOMPARE(o->property("test2_1").toBool(), true);
5795 QCOMPARE(o->property("test3").toBool(), true);
5796 QCOMPARE(o->property("test3_1").toBool(), true);
5803 QQmlComponent component(&engine, testFileUrl("include_callback.qml"));
5804 QObject *o = component.create();
5807 QCOMPARE(o->property("test1").toBool(), true);
5808 QCOMPARE(o->property("test2").toBool(), true);
5809 QCOMPARE(o->property("test3").toBool(), true);
5810 QCOMPARE(o->property("test4").toBool(), true);
5811 QCOMPARE(o->property("test5").toBool(), true);
5812 QCOMPARE(o->property("test6").toBool(), true);
5817 // Including file with ".pragma library"
5819 QQmlComponent component(&engine, testFileUrl("include_pragma.qml"));
5820 QObject *o = component.create();
5822 QCOMPARE(o->property("test1").toInt(), 100);
5829 TestHTTPServer server(8111);
5830 QVERIFY(server.isValid());
5831 server.serveDirectory(dataDirectory());
5833 QQmlComponent component(&engine, testFileUrl("include_remote.qml"));
5834 QObject *o = component.create();
5837 QTRY_VERIFY(o->property("done").toBool() == true);
5838 QTRY_VERIFY(o->property("done2").toBool() == true);
5840 QCOMPARE(o->property("test1").toBool(), true);
5841 QCOMPARE(o->property("test2").toBool(), true);
5842 QCOMPARE(o->property("test3").toBool(), true);
5843 QCOMPARE(o->property("test4").toBool(), true);
5844 QCOMPARE(o->property("test5").toBool(), true);
5846 QCOMPARE(o->property("test6").toBool(), true);
5847 QCOMPARE(o->property("test7").toBool(), true);
5848 QCOMPARE(o->property("test8").toBool(), true);
5849 QCOMPARE(o->property("test9").toBool(), true);
5850 QCOMPARE(o->property("test10").toBool(), true);
5857 TestHTTPServer server(8111);
5858 QVERIFY(server.isValid());
5859 server.serveDirectory(dataDirectory());
5861 QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml"));
5862 QObject *o = component.create();
5865 QTRY_VERIFY(o->property("done").toBool() == true);
5867 QCOMPARE(o->property("test1").toBool(), true);
5868 QCOMPARE(o->property("test2").toBool(), true);
5869 QCOMPARE(o->property("test3").toBool(), true);
5875 void tst_qqmlecmascript::signalHandlers()
5877 QQmlComponent component(&engine, testFileUrl("signalHandlers.qml"));
5878 QObject *o = component.create();
5881 QVERIFY(o->property("count").toInt() == 0);
5882 QMetaObject::invokeMethod(o, "testSignalCall");
5883 QCOMPARE(o->property("count").toInt(), 1);
5885 QMetaObject::invokeMethod(o, "testSignalHandlerCall");
5886 QCOMPARE(o->property("count").toInt(), 1);
5887 QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
5889 QVERIFY(o->property("funcCount").toInt() == 0);
5890 QMetaObject::invokeMethod(o, "testSignalConnection");
5891 QCOMPARE(o->property("funcCount").toInt(), 1);
5893 QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
5894 QCOMPARE(o->property("funcCount").toInt(), 2);
5896 QMetaObject::invokeMethod(o, "testSignalDefined");
5897 QCOMPARE(o->property("definedResult").toBool(), true);
5899 QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
5900 QCOMPARE(o->property("definedHandlerResult").toBool(), true);
5905 void tst_qqmlecmascript::qtbug_10696()
5907 QQmlComponent component(&engine, testFileUrl("qtbug_10696.qml"));
5908 QObject *o = component.create();
5913 void tst_qqmlecmascript::qtbug_11606()
5915 QQmlComponent component(&engine, testFileUrl("qtbug_11606.qml"));
5916 QObject *o = component.create();
5918 QCOMPARE(o->property("test").toBool(), true);
5922 void tst_qqmlecmascript::qtbug_11600()
5924 QQmlComponent component(&engine, testFileUrl("qtbug_11600.qml"));
5925 QObject *o = component.create();
5927 QCOMPARE(o->property("test").toBool(), true);
5931 void tst_qqmlecmascript::qtbug_21864()
5933 QQmlComponent component(&engine, testFileUrl("qtbug_21864.qml"));
5934 QObject *o = component.create();
5936 QCOMPARE(o->property("test").toBool(), true);
5940 void tst_qqmlecmascript::rewriteMultiLineStrings()
5944 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml"));
5945 QObject *o = component.create();
5947 QTRY_COMPARE(o->property("test").toBool(), true);
5952 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings_crlf.1.qml"));
5953 QObject *o = component.create();
5959 void tst_qqmlecmascript::qobjectConnectionListExceptionHandling()
5962 QQmlComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml"));
5963 QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined");
5964 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5965 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5966 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5967 QObject *o = component.create();
5969 QCOMPARE(o->property("test").toBool(), true);
5973 // Reading and writing non-scriptable properties should fail
5974 void tst_qqmlecmascript::nonscriptable()
5976 QQmlComponent component(&engine, testFileUrl("nonscriptable.qml"));
5977 QObject *o = component.create();
5979 QCOMPARE(o->property("readOk").toBool(), true);
5980 QCOMPARE(o->property("writeOk").toBool(), true);
5984 // deleteLater() should not be callable from QML
5985 void tst_qqmlecmascript::deleteLater()
5987 QQmlComponent component(&engine, testFileUrl("deleteLater.qml"));
5988 QObject *o = component.create();
5990 QCOMPARE(o->property("test").toBool(), true);
5994 // objectNameChanged() should be usable from QML
5995 void tst_qqmlecmascript::objectNameChangedSignal()
5997 QQmlComponent component(&engine, testFileUrl("objectNameChangedSignal.qml"));
5998 QObject *o = component.create();
6000 QCOMPARE(o->property("test").toBool(), false);
6001 o->setObjectName("obj");
6002 QCOMPARE(o->property("test").toBool(), true);
6006 // destroyed() should not be usable from QML
6007 void tst_qqmlecmascript::destroyedSignal()
6009 QQmlComponent component(&engine, testFileUrl("destroyedSignal.qml"));
6010 QVERIFY(component.isError());
6012 QString expectedErrorString = component.url().toString() + QLatin1String(":5:5: Cannot assign to non-existent property \"onDestroyed\"");
6013 QCOMPARE(component.errors().at(0).toString(), expectedErrorString);
6016 void tst_qqmlecmascript::in()
6018 QQmlComponent component(&engine, testFileUrl("in.qml"));
6019 QObject *o = component.create();
6021 QCOMPARE(o->property("test1").toBool(), true);
6022 QCOMPARE(o->property("test2").toBool(), true);
6026 void tst_qqmlecmascript::typeOf()
6028 QQmlComponent component(&engine, testFileUrl("typeOf.qml"));
6030 QObject *o = component.create();
6033 QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
6034 QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
6035 QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
6036 QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
6037 QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
6038 QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
6039 QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
6040 QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
6041 QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
6046 void tst_qqmlecmascript::qtbug_24448()
6048 QQmlComponent component(&engine, testFileUrl("qtbug_24448.qml"));
6049 QScopedPointer<QObject> o(component.create());
6051 QVERIFY(o->property("test").toBool());
6054 void tst_qqmlecmascript::sharedAttachedObject()
6056 QQmlComponent component(&engine, testFileUrl("sharedAttachedObject.qml"));
6057 QObject *o = component.create();
6059 QCOMPARE(o->property("test1").toBool(), true);
6060 QCOMPARE(o->property("test2").toBool(), true);
6065 void tst_qqmlecmascript::objectName()
6067 QQmlComponent component(&engine, testFileUrl("objectName.qml"));
6068 QObject *o = component.create();
6071 QCOMPARE(o->property("test1").toString(), QString("hello"));
6072 QCOMPARE(o->property("test2").toString(), QString("ell"));
6074 o->setObjectName("world");
6076 QCOMPARE(o->property("test1").toString(), QString("world"));
6077 QCOMPARE(o->property("test2").toString(), QString("orl"));
6082 void tst_qqmlecmascript::writeRemovesBinding()
6084 QQmlComponent component(&engine, testFileUrl("writeRemovesBinding.qml"));
6085 QObject *o = component.create();
6088 QCOMPARE(o->property("test").toBool(), true);
6093 // Test bindings assigned to alias properties actually assign to the alias' target
6094 void tst_qqmlecmascript::aliasBindingsAssignCorrectly()
6096 QQmlComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml"));
6097 QObject *o = component.create();
6100 QCOMPARE(o->property("test").toBool(), true);
6105 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
6106 void tst_qqmlecmascript::aliasBindingsOverrideTarget()
6109 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml"));
6110 QObject *o = component.create();
6113 QCOMPARE(o->property("test").toBool(), true);
6119 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml"));
6120 QObject *o = component.create();
6123 QCOMPARE(o->property("test").toBool(), true);
6129 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml"));
6130 QObject *o = component.create();
6133 QCOMPARE(o->property("test").toBool(), true);
6139 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
6140 void tst_qqmlecmascript::aliasWritesOverrideBindings()
6143 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml"));
6144 QObject *o = component.create();
6147 QCOMPARE(o->property("test").toBool(), true);
6153 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml"));
6154 QObject *o = component.create();
6157 QCOMPARE(o->property("test").toBool(), true);
6163 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml"));
6164 QObject *o = component.create();
6167 QCOMPARE(o->property("test").toBool(), true);
6173 // Allow an alais to a composite element
6175 void tst_qqmlecmascript::aliasToCompositeElement()
6177 QQmlComponent component(&engine, testFileUrl("aliasToCompositeElement.qml"));
6179 QObject *object = component.create();
6180 QVERIFY(object != 0);
6185 void tst_qqmlecmascript::qtbug_20344()
6187 QQmlComponent component(&engine, testFileUrl("qtbug_20344.qml"));
6189 QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
6190 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6192 QObject *object = component.create();
6193 QVERIFY(object != 0);
6198 void tst_qqmlecmascript::revisionErrors()
6201 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml"));
6202 QString url = component.url().toString();
6204 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6205 QString warning2 = url + ":11: ReferenceError: prop2 is not defined";
6206 QString warning3 = url + ":13: ReferenceError: method2 is not defined";
6208 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6209 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6210 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6211 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6212 QVERIFY(object != 0);
6216 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml"));
6217 QString url = component.url().toString();
6219 // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
6220 // method2, prop2 from MyRevisionedClass not available
6221 // method4, prop4 from MyRevisionedSubclass not available
6222 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6223 QString warning2 = url + ":14: ReferenceError: prop2 is not defined";
6224 QString warning3 = url + ":10: ReferenceError: prop4 is not defined";
6225 QString warning4 = url + ":16: ReferenceError: prop4 is not defined";
6226 QString warning5 = url + ":20: ReferenceError: method2 is not defined";
6228 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6229 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6230 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6231 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
6232 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
6233 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6234 QVERIFY(object != 0);
6238 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml"));
6239 QString url = component.url().toString();
6241 // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
6242 // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
6243 QString warning1 = url + ":30: ReferenceError: methodD is not defined";
6244 QString warning2 = url + ":10: ReferenceError: propD is not defined";
6245 QString warning3 = url + ":20: ReferenceError: propD is not defined";
6246 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6247 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6248 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6249 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6250 QVERIFY(object != 0);
6255 void tst_qqmlecmascript::revision()
6258 QQmlComponent component(&engine, testFileUrl("metaobjectRevision.qml"));
6259 QString url = component.url().toString();
6261 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6262 QVERIFY(object != 0);
6266 QQmlComponent component(&engine, testFileUrl("metaobjectRevision2.qml"));
6267 QString url = component.url().toString();
6269 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6270 QVERIFY(object != 0);
6274 QQmlComponent component(&engine, testFileUrl("metaobjectRevision3.qml"));
6275 QString url = component.url().toString();
6277 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6278 QVERIFY(object != 0);
6281 // Test that non-root classes can resolve revisioned methods
6283 QQmlComponent component(&engine, testFileUrl("metaobjectRevision4.qml"));
6285 QObject *object = component.create();
6286 QVERIFY(object != 0);
6287 QCOMPARE(object->property("test").toReal(), 11.);
6292 void tst_qqmlecmascript::realToInt()
6294 QQmlComponent component(&engine, testFileUrl("realToInt.qml"));
6295 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6296 QVERIFY(object != 0);
6298 QMetaObject::invokeMethod(object, "test1");
6299 QCOMPARE(object->value(), int(4));
6300 QMetaObject::invokeMethod(object, "test2");
6301 QCOMPARE(object->value(), int(8));
6304 void tst_qqmlecmascript::urlProperty()
6307 QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml"));
6308 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6309 QVERIFY(object != 0);
6310 object->setStringProperty("http://qt-project.org");
6311 QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
6312 QCOMPARE(object->intProperty(), 123);
6313 QCOMPARE(object->value(), 1);
6314 QCOMPARE(object->property("result").toBool(), true);
6318 void tst_qqmlecmascript::urlPropertyWithEncoding()
6321 QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml"));
6322 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6323 QVERIFY(object != 0);
6324 object->setStringProperty("http://qt-project.org");
6326 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6327 QCOMPARE(object->urlProperty(), encoded);
6328 QCOMPARE(object->value(), 0); // Interpreting URL as string yields canonicalised version
6329 QCOMPARE(object->property("result").toBool(), true);
6333 void tst_qqmlecmascript::urlListPropertyWithEncoding()
6336 QQmlComponent component(&engine, testFileUrl("urlListProperty.qml"));
6337 QObject *object = component.create();
6338 QVERIFY(object != 0);
6339 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
6340 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
6341 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
6342 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
6343 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0);
6345 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6346 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded));
6347 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded));
6348 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6349 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6354 void tst_qqmlecmascript::dynamicString()
6356 QQmlComponent component(&engine, testFileUrl("dynamicString.qml"));
6357 QObject *object = component.create();
6358 QVERIFY(object != 0);
6359 QCOMPARE(object->property("stringProperty").toString(),
6360 QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
6363 void tst_qqmlecmascript::deleteLaterObjectMethodCall()
6365 QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml"));
6366 QObject *object = component.create();
6367 QVERIFY(object != 0);
6370 void tst_qqmlecmascript::automaticSemicolon()
6372 QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml"));
6373 QObject *object = component.create();
6374 QVERIFY(object != 0);
6377 void tst_qqmlecmascript::unaryExpression()
6379 QQmlComponent component(&engine, testFileUrl("unaryExpression.qml"));
6380 QObject *object = component.create();
6381 QVERIFY(object != 0);
6384 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
6385 void tst_qqmlecmascript::doubleEvaluate()
6387 QQmlComponent component(&engine, testFileUrl("doubleEvaluate.qml"));
6388 QObject *object = component.create();
6389 QVERIFY(object != 0);
6390 WriteCounter *wc = qobject_cast<WriteCounter *>(object);
6392 QCOMPARE(wc->count(), 1);
6394 wc->setProperty("x", 9);
6396 QCOMPARE(wc->count(), 2);
6401 static QStringList messages;
6402 static void captureMsgHandler(QtMsgType, const char *msg)
6404 messages.append(QLatin1String(msg));
6407 void tst_qqmlecmascript::nonNotifyable()
6409 QV4Compiler::enableV4(false);
6410 QQmlComponent component(&engine, testFileUrl("nonNotifyable.qml"));
6411 QV4Compiler::enableV4(true);
6413 QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
6415 QObject *object = component.create();
6416 qInstallMsgHandler(old);
6418 QVERIFY(object != 0);
6420 QString expected1 = QLatin1String("QQmlExpression: Expression ") +
6421 component.url().toString() +
6422 QLatin1String(":5 depends on non-NOTIFYable properties:");
6423 QString expected2 = QLatin1String(" ") +
6424 QLatin1String(object->metaObject()->className()) +
6425 QLatin1String("::value");
6427 QCOMPARE(messages.length(), 2);
6428 QCOMPARE(messages.at(0), expected1);
6429 QCOMPARE(messages.at(1), expected2);
6434 void tst_qqmlecmascript::forInLoop()
6436 QQmlComponent component(&engine, testFileUrl("forInLoop.qml"));
6437 QObject *object = component.create();
6438 QVERIFY(object != 0);
6440 QMetaObject::invokeMethod(object, "listProperty");
6442 QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
6443 QCOMPARE(r.size(), 3);
6444 QCOMPARE(r[0],QLatin1String("0=obj1"));
6445 QCOMPARE(r[1],QLatin1String("1=obj2"));
6446 QCOMPARE(r[2],QLatin1String("2=obj3"));
6448 //TODO: should test for in loop for other objects (such as QObjects) as well.
6453 // An object the binding depends on is deleted while the binding is still running
6454 void tst_qqmlecmascript::deleteWhileBindingRunning()
6456 QQmlComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml"));
6457 QObject *object = component.create();
6458 QVERIFY(object != 0);
6462 void tst_qqmlecmascript::qtbug_22679()
6465 object.setStringProperty(QLatin1String("Please work correctly"));
6466 engine.rootContext()->setContextProperty("contextProp", &object);
6468 QQmlComponent component(&engine, testFileUrl("qtbug_22679.qml"));
6469 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6470 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6472 QObject *o = component.create();
6474 QCOMPARE(warningsSpy.count(), 0);
6478 void tst_qqmlecmascript::qtbug_22843_data()
6480 QTest::addColumn<bool>("library");
6482 QTest::newRow("without .pragma library") << false;
6483 QTest::newRow("with .pragma library") << true;
6486 void tst_qqmlecmascript::qtbug_22843()
6488 QFETCH(bool, library);
6490 QString fileName("qtbug_22843");
6492 fileName += QLatin1String(".library");
6493 fileName += QLatin1String(".qml");
6495 QQmlComponent component(&engine, testFileUrl(fileName));
6496 QString url = component.url().toString();
6497 QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )");
6498 QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'");
6500 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6501 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6502 for (int x = 0; x < 3; ++x) {
6503 warningsSpy.clear();
6504 // For libraries, only the first import attempt should produce a
6505 // SyntaxError warning; subsequent component creation should not
6506 // attempt to reload the script.
6507 bool expectSyntaxError = !library || (x == 0);
6508 if (expectSyntaxError)
6509 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
6510 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
6511 QObject *object = component.create();
6512 QVERIFY(object != 0);
6513 QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
6519 void tst_qqmlecmascript::switchStatement()
6522 QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml"));
6523 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6524 QVERIFY(object != 0);
6526 // `object->value()' is the number of executed statements
6528 object->setStringProperty("A");
6529 QCOMPARE(object->value(), 5);
6531 object->setStringProperty("S");
6532 QCOMPARE(object->value(), 3);
6534 object->setStringProperty("D");
6535 QCOMPARE(object->value(), 3);
6537 object->setStringProperty("F");
6538 QCOMPARE(object->value(), 4);
6540 object->setStringProperty("something else");
6541 QCOMPARE(object->value(), 1);
6545 QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml"));
6546 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6547 QVERIFY(object != 0);
6549 // `object->value()' is the number of executed statements
6551 object->setStringProperty("A");
6552 QCOMPARE(object->value(), 5);
6554 object->setStringProperty("S");
6555 QCOMPARE(object->value(), 3);
6557 object->setStringProperty("D");
6558 QCOMPARE(object->value(), 3);
6560 object->setStringProperty("F");
6561 QCOMPARE(object->value(), 3);
6563 object->setStringProperty("something else");
6564 QCOMPARE(object->value(), 4);
6568 QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml"));
6569 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6570 QVERIFY(object != 0);
6572 // `object->value()' is the number of executed statements
6574 object->setStringProperty("A");
6575 QCOMPARE(object->value(), 5);
6577 object->setStringProperty("S");
6578 QCOMPARE(object->value(), 3);
6580 object->setStringProperty("D");
6581 QCOMPARE(object->value(), 3);
6583 object->setStringProperty("F");
6584 QCOMPARE(object->value(), 3);
6586 object->setStringProperty("something else");
6587 QCOMPARE(object->value(), 6);
6591 QQmlComponent component(&engine, testFileUrl("switchStatement.4.qml"));
6593 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
6594 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6596 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6597 QVERIFY(object != 0);
6599 // `object->value()' is the number of executed statements
6601 object->setStringProperty("A");
6602 QCOMPARE(object->value(), 5);
6604 object->setStringProperty("S");
6605 QCOMPARE(object->value(), 3);
6607 object->setStringProperty("D");
6608 QCOMPARE(object->value(), 3);
6610 object->setStringProperty("F");
6611 QCOMPARE(object->value(), 3);
6613 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6615 object->setStringProperty("something else");
6619 QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml"));
6620 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6621 QVERIFY(object != 0);
6623 // `object->value()' is the number of executed statements
6625 object->setStringProperty("A");
6626 QCOMPARE(object->value(), 1);
6628 object->setStringProperty("S");
6629 QCOMPARE(object->value(), 1);
6631 object->setStringProperty("D");
6632 QCOMPARE(object->value(), 1);
6634 object->setStringProperty("F");
6635 QCOMPARE(object->value(), 1);
6637 object->setStringProperty("something else");
6638 QCOMPARE(object->value(), 1);
6642 QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml"));
6643 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6644 QVERIFY(object != 0);
6646 // `object->value()' is the number of executed statements
6648 object->setStringProperty("A");
6649 QCOMPARE(object->value(), 123);
6651 object->setStringProperty("S");
6652 QCOMPARE(object->value(), 123);
6654 object->setStringProperty("D");
6655 QCOMPARE(object->value(), 321);
6657 object->setStringProperty("F");
6658 QCOMPARE(object->value(), 321);
6660 object->setStringProperty("something else");
6661 QCOMPARE(object->value(), 0);
6665 void tst_qqmlecmascript::withStatement()
6668 QQmlComponent component(&engine, testFileUrl("withStatement.1.qml"));
6669 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6670 QVERIFY(object != 0);
6672 QCOMPARE(object->value(), 123);
6676 void tst_qqmlecmascript::tryStatement()
6679 QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml"));
6680 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6681 QVERIFY(object != 0);
6683 QCOMPARE(object->value(), 123);
6687 QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml"));
6688 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6689 QVERIFY(object != 0);
6691 QCOMPARE(object->value(), 321);
6695 QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml"));
6696 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6697 QVERIFY(object != 0);
6699 QCOMPARE(object->value(), 1);
6703 QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml"));
6704 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6705 QVERIFY(object != 0);
6707 QCOMPARE(object->value(), 1);
6711 class CppInvokableWithQObjectDerived : public QObject
6715 CppInvokableWithQObjectDerived() {}
6716 ~CppInvokableWithQObjectDerived() {}
6718 Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data)
6720 MyQmlObject *obj = new MyQmlObject();
6721 obj->setStringProperty(data);
6725 Q_INVOKABLE QString getStringProperty(MyQmlObject *obj)
6727 return obj->stringProperty();
6731 void tst_qqmlecmascript::invokableWithQObjectDerived()
6733 CppInvokableWithQObjectDerived invokable;
6737 engine.rootContext()->setContextProperty("invokable", &invokable);
6739 QQmlComponent component(&engine, testFileUrl("qobjectDerivedArgument.qml"));
6741 QObject *object = component.create();
6743 QVERIFY(object != 0);
6744 QVERIFY(object->property("result").value<bool>() == true);
6750 void tst_qqmlecmascript::realTypePrecision()
6752 // Properties and signal parameters of type real should have double precision.
6753 QQmlComponent component(&engine, testFileUrl("realTypePrecision.qml"));
6754 QScopedPointer<QObject> object(component.create());
6755 QVERIFY(object != 0);
6756 QCOMPARE(object->property("test").toDouble(), 1234567890.);
6757 QCOMPARE(object->property("test2").toDouble(), 1234567890.);
6758 QCOMPARE(object->property("test3").toDouble(), 1234567890.);
6759 QCOMPARE(object->property("test4").toDouble(), 1234567890.);
6760 QCOMPARE(object->property("test5").toDouble(), 1234567890.);
6761 QCOMPARE(object->property("test6").toDouble(), 1234567890.*2);
6764 void tst_qqmlecmascript::registeredFlagMethod()
6767 QQmlComponent component(&engine, testFileUrl("registeredFlagMethod.qml"));
6768 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6769 QVERIFY(object != 0);
6771 QCOMPARE(object->buttons(), 0);
6772 emit object->basicSignal();
6773 QCOMPARE(object->buttons(), Qt::RightButton);
6779 void tst_qqmlecmascript::replaceBinding()
6782 QQmlComponent c(&engine, testFileUrl("replaceBinding.qml"));
6783 QObject *obj = c.create();
6786 QVERIFY(obj->property("success").toBool());
6790 void tst_qqmlecmascript::deleteRootObjectInCreation()
6794 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.qml"));
6795 QObject *obj = c.create();
6797 QVERIFY(obj->property("rootIndestructible").toBool());
6798 QVERIFY(!obj->property("childDestructible").toBool());
6800 QVERIFY(obj->property("childDestructible").toBool());
6805 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.2.qml"));
6806 QObject *object = c.create();
6807 QVERIFY(object != 0);
6808 QVERIFY(object->property("testConditionsMet").toBool());
6813 void tst_qqmlecmascript::onDestruction()
6816 // Delete object manually to invoke the associated handlers,
6817 // prior to engine destruction.
6819 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
6820 QObject *obj = c.create();
6826 // In this case, the teardown of the engine causes deletion
6827 // of contexts and child items. This triggers the
6828 // onDestruction handler of a (previously .destroy()ed)
6829 // component instance. This shouldn't crash.
6831 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
6832 QObject *obj = c.create();
6837 struct EventProcessor : public QObject
6841 Q_INVOKABLE void process()
6843 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
6844 QCoreApplication::processEvents();
6848 void tst_qqmlecmascript::bindingSuppression()
6851 EventProcessor processor;
6852 engine.rootContext()->setContextProperty("pendingEvents", &processor);
6854 transientErrorsMsgCount = 0;
6855 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
6857 QQmlComponent c(&engine, testFileUrl("bindingSuppression.qml"));
6858 QObject *obj = c.create();
6862 qInstallMsgHandler(old);
6863 QCOMPARE(transientErrorsMsgCount, 0);
6866 void tst_qqmlecmascript::signalEmitted()
6869 // calling destroy on the sibling.
6871 QQmlComponent c(&engine, testFileUrl("signalEmitted.2.qml"));
6872 QObject *obj = c.create();
6874 QTRY_VERIFY(obj->property("success").toBool());
6879 // allowing gc to clean up the sibling.
6881 QQmlComponent c(&engine, testFileUrl("signalEmitted.3.qml"));
6882 QObject *obj = c.create();
6884 gc(engine); // should collect c1.
6885 QTRY_VERIFY(obj->property("success").toBool());
6890 // allowing gc to clean up the sibling after manually destroying target.
6892 QQmlComponent c(&engine, testFileUrl("signalEmitted.4.qml"));
6893 QObject *obj = c.create();
6895 gc(engine); // should collect c1.
6896 QMetaObject::invokeMethod(obj, "destroyC2");
6897 QTRY_VERIFY(obj->property("success").toBool()); // handles events (incl. delete later).
6903 void tst_qqmlecmascript::threadSignal()
6906 QQmlComponent c(&engine, testFileUrl("threadSignal.qml"));
6907 QObject *object = c.create();
6908 QVERIFY(object != 0);
6909 QTRY_VERIFY(object->property("passed").toBool());
6913 QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml"));
6914 QObject *object = c.create();
6915 QVERIFY(object != 0);
6916 QSignalSpy doneSpy(object, SIGNAL(done(const QString &)));
6917 QMetaObject::invokeMethod(object, "doIt");
6918 QTRY_VERIFY(object->property("passed").toBool());
6919 QCOMPARE(doneSpy.count(), 1);
6924 // ensure that the qqmldata::destroyed() handler doesn't cause problems
6925 void tst_qqmlecmascript::qqmldataDestroyed()
6927 // gc cleans up a qobject, later the qqmldata destroyed handler will run.
6929 QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.qml"));
6930 QObject *object = c.create();
6931 QVERIFY(object != 0);
6932 // now gc causing the collection of the dynamically constructed object.
6933 engine.collectGarbage();
6934 engine.collectGarbage();
6935 // now process events to allow deletion (calling qqmldata::destroyed())
6936 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
6937 QCoreApplication::processEvents();
6942 // in this case, the object has CPP ownership, and the gc will
6943 // be triggered during its beginCreate stage.
6945 QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.2.qml"));
6946 QObject *object = c.create();
6947 QVERIFY(object != 0);
6948 QVERIFY(object->property("testConditionsMet").toBool());
6949 // the gc() within the handler will have triggered the weak
6950 // qobject reference callback. If that incorrectly disposes
6951 // the handle, when the qqmldata::destroyed() handler is
6952 // called due to object deletion we will see a crash.
6954 // shouldn't have crashed.
6958 void tst_qqmlecmascript::secondAlias()
6960 QQmlComponent c(&engine, testFileUrl("secondAlias.qml"));
6961 QObject *object = c.create();
6962 QVERIFY(object != 0);
6963 QCOMPARE(object->property("test").toInt(), 200);
6967 // An alias to a var property works
6968 void tst_qqmlecmascript::varAlias()
6970 QQmlComponent c(&engine, testFileUrl("varAlias.qml"));
6971 QObject *object = c.create();
6972 QVERIFY(object != 0);
6973 QCOMPARE(object->property("test").toInt(), 192);
6977 // Used to trigger an assert in the lazy meta object creation stage
6978 void tst_qqmlecmascript::overrideDataAssert()
6980 QQmlComponent c(&engine, testFileUrl("overrideDataAssert.qml"));
6981 QObject *object = c.create();
6982 QVERIFY(object != 0);
6983 object->metaObject();
6987 QTEST_MAIN(tst_qqmlecmascript)
6989 #include "tst_qqmlecmascript.moc"