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 propertyVarCpp();
164 void propertyVarOwnership();
165 void propertyVarImplicitOwnership();
166 void propertyVarReparent();
167 void propertyVarReparentNullContext();
168 void propertyVarCircular();
169 void propertyVarCircular2();
170 void propertyVarInheritance();
171 void propertyVarInheritance2();
172 void elementAssign();
173 void objectPassThroughSignals();
174 void objectConversion();
175 void booleanConversion();
176 void handleReferenceManagement();
178 void readonlyDeclaration();
179 void sequenceConversionRead();
180 void sequenceConversionWrite();
181 void sequenceConversionArray();
182 void sequenceConversionIndexes();
183 void sequenceConversionThreads();
184 void sequenceConversionBindings();
185 void sequenceConversionCopy();
186 void assignSequenceTypes();
189 void singleV8BindingDestroyedDuringEvaluation();
192 void dynamicCreationCrash();
193 void dynamicCreationOwnership();
195 void nullObjectBinding();
196 void deletedEngine();
197 void libraryScriptAssert();
198 void variantsAssignedUndefined();
200 void qtcreatorbug_1289();
201 void noSpuriousWarningsAtShutdown();
202 void canAssignNullToQObject();
203 void functionAssignment_fromBinding();
204 void functionAssignment_fromJS();
205 void functionAssignment_fromJS_data();
206 void functionAssignmentfromJS_invalid();
207 void functionAssignment_afterBinding();
210 void functionException();
215 void qobjectConnectionListExceptionHandling();
216 void nonscriptable();
221 void sharedAttachedObject();
223 void writeRemovesBinding();
224 void aliasBindingsAssignCorrectly();
225 void aliasBindingsOverrideTarget();
226 void aliasWritesOverrideBindings();
227 void aliasToCompositeElement();
230 void urlPropertyWithEncoding();
231 void urlListPropertyWithEncoding();
232 void dynamicString();
234 void signalHandlers();
235 void doubleEvaluate();
237 void nonNotifyable();
238 void deleteWhileBindingRunning();
239 void callQtInvokables();
240 void invokableObjectArg();
241 void invokableObjectRet();
244 void qtbug_22843_data();
246 void rewriteMultiLineStrings();
247 void revisionErrors();
249 void invokableWithQObjectDerived();
250 void realTypePrecision();
251 void registeredFlagMethod();
252 void deleteLaterObjectMethodCall();
253 void automaticSemicolon();
254 void unaryExpression();
255 void switchStatement();
256 void withStatement();
258 void replaceBinding();
259 void deleteRootObjectInCreation();
260 void onDestruction();
261 void bindingSuppression();
264 static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
268 void tst_qqmlecmascript::initTestCase()
270 QQmlDataTest::initTestCase();
273 QString dataDir(dataDirectory() + QLatin1Char('/') + QLatin1String("lib"));
274 engine.addImportPath(dataDir);
277 void tst_qqmlecmascript::assignBasicTypes()
280 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.qml"));
281 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
282 QVERIFY(object != 0);
283 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
284 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
285 QCOMPARE(object->stringProperty(), QString("Hello World!"));
286 QCOMPARE(object->uintProperty(), uint(10));
287 QCOMPARE(object->intProperty(), -19);
288 QCOMPARE((float)object->realProperty(), float(23.2));
289 QCOMPARE((float)object->doubleProperty(), float(-19.75));
290 QCOMPARE((float)object->floatProperty(), float(8.5));
291 QCOMPARE(object->colorProperty(), QColor("red"));
292 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
293 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
294 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
295 QCOMPARE(object->pointProperty(), QPoint(99,13));
296 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
297 QCOMPARE(object->sizeProperty(), QSize(99, 13));
298 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
299 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
300 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
301 QCOMPARE(object->boolProperty(), true);
302 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
303 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
304 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
308 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.2.qml"));
309 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
310 QVERIFY(object != 0);
311 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
312 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
313 QCOMPARE(object->stringProperty(), QString("Hello World!"));
314 QCOMPARE(object->uintProperty(), uint(10));
315 QCOMPARE(object->intProperty(), -19);
316 QCOMPARE((float)object->realProperty(), float(23.2));
317 QCOMPARE((float)object->doubleProperty(), float(-19.75));
318 QCOMPARE((float)object->floatProperty(), float(8.5));
319 QCOMPARE(object->colorProperty(), QColor("red"));
320 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
321 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
322 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
323 QCOMPARE(object->pointProperty(), QPoint(99,13));
324 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
325 QCOMPARE(object->sizeProperty(), QSize(99, 13));
326 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
327 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
328 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
329 QCOMPARE(object->boolProperty(), true);
330 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
331 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
332 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
337 void tst_qqmlecmascript::assignDate_data()
339 QTest::addColumn<QUrl>("source");
341 QTest::newRow("Component.onComplete JS Parse") << testFileUrl("assignDate.qml");
342 QTest::newRow("Component.onComplete JS") << testFileUrl("assignDate.1.qml");
343 QTest::newRow("Binding JS") << testFileUrl("assignDate.2.qml");
344 QTest::newRow("Binding UTC") << testFileUrl("assignDate.3.qml");
345 QTest::newRow("Binding JS UTC") << testFileUrl("assignDate.4.qml");
346 QTest::newRow("Binding UTC+2") << testFileUrl("assignDate.5.qml");
347 QTest::newRow("Binding JS UTC+2 ") << testFileUrl("assignDate.6.qml");
350 void tst_qqmlecmascript::assignDate()
352 QFETCH(QUrl, source);
354 QQmlComponent component(&engine, source);
355 QScopedPointer<QObject> obj(component.create());
356 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
357 QVERIFY(object != 0);
359 // Dates received from JS are automatically converted to local time
360 QDate expectedDate(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 0), Qt::UTC).toLocalTime().date());
361 QDateTime expectedDateTime(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 1), Qt::UTC).toLocalTime());
362 QDateTime expectedDateTime2(QDateTime(QDate(2009, 5, 12), QTime(23, 59, 59), Qt::UTC).toLocalTime());
364 QCOMPARE(object->dateProperty(), expectedDate);
365 QCOMPARE(object->dateTimeProperty(), expectedDateTime);
366 QCOMPARE(object->dateTimeProperty2(), expectedDateTime2);
367 QCOMPARE(object->boolProperty(), true);
370 void tst_qqmlecmascript::exportDate_data()
372 QTest::addColumn<QUrl>("source");
373 QTest::addColumn<QDateTime>("datetime");
375 // Verify that we can export datetime information to QML and that consumers can access
376 // the data correctly provided they know the TZ info associated with the value
378 const QDate date(2009, 5, 12);
379 const QTime early(0, 0, 1);
380 const QTime late(23, 59, 59);
381 const int offset(((11 * 60) + 30) * 60);
383 QTest::newRow("Localtime early") << testFileUrl("exportDate.qml") << QDateTime(date, early, Qt::LocalTime);
384 QTest::newRow("Localtime late") << testFileUrl("exportDate.2.qml") << QDateTime(date, late, Qt::LocalTime);
385 QTest::newRow("UTC early") << testFileUrl("exportDate.3.qml") << QDateTime(date, early, Qt::UTC);
386 QTest::newRow("UTC late") << testFileUrl("exportDate.4.qml") << QDateTime(date, late, Qt::UTC);
388 QDateTime dt(date, early, Qt::OffsetFromUTC);
389 dt.setUtcOffset(offset);
390 QTest::newRow("+11:30 early") << testFileUrl("exportDate.5.qml") << dt;
393 QDateTime dt(date, late, Qt::OffsetFromUTC);
394 dt.setUtcOffset(offset);
395 QTest::newRow("+11:30 late") << testFileUrl("exportDate.6.qml") << dt;
398 QDateTime dt(date, early, Qt::OffsetFromUTC);
399 dt.setUtcOffset(-offset);
400 QTest::newRow("-11:30 early") << testFileUrl("exportDate.7.qml") << dt;
403 QDateTime dt(date, late, Qt::OffsetFromUTC);
404 dt.setUtcOffset(-offset);
405 QTest::newRow("-11:30 late") << testFileUrl("exportDate.8.qml") << dt;
409 void tst_qqmlecmascript::exportDate()
411 QFETCH(QUrl, source);
412 QFETCH(QDateTime, datetime);
414 DateTimeExporter exporter(datetime);
417 e.rootContext()->setContextProperty("datetimeExporter", &exporter);
419 QQmlComponent component(&e, source);
420 QScopedPointer<QObject> obj(component.create());
421 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
422 QVERIFY(object != 0);
423 QCOMPARE(object->boolProperty(), true);
426 void tst_qqmlecmascript::idShortcutInvalidates()
429 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.qml"));
430 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
431 QVERIFY(object != 0);
432 QVERIFY(object->objectProperty() != 0);
433 delete object->objectProperty();
434 QVERIFY(object->objectProperty() == 0);
439 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.1.qml"));
440 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
441 QVERIFY(object != 0);
442 QVERIFY(object->objectProperty() != 0);
443 delete object->objectProperty();
444 QVERIFY(object->objectProperty() == 0);
449 void tst_qqmlecmascript::boolPropertiesEvaluateAsBool()
452 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.1.qml"));
453 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
454 QVERIFY(object != 0);
455 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
459 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.2.qml"));
460 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
461 QVERIFY(object != 0);
462 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
467 void tst_qqmlecmascript::signalAssignment()
470 QQmlComponent component(&engine, testFileUrl("signalAssignment.1.qml"));
471 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
472 QVERIFY(object != 0);
473 QCOMPARE(object->string(), QString());
474 emit object->basicSignal();
475 QCOMPARE(object->string(), QString("pass"));
480 QQmlComponent component(&engine, testFileUrl("signalAssignment.2.qml"));
481 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
482 QVERIFY(object != 0);
483 QCOMPARE(object->string(), QString());
484 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
485 QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
490 QQmlComponent component(&engine, testFileUrl("signalAssignment.3.qml"));
491 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
492 QVERIFY(object != 0);
493 QCOMPARE(object->string(), QString());
494 emit object->unnamedArgumentSignal(19, 10.25, "Hello world!");
495 QEXPECT_FAIL("", "QTBUG-24481", Continue);
496 QCOMPARE(object->string(), QString("pass 19 Hello world!"));
501 QQmlComponent component(&engine, testFileUrl("signalAssignment.4.qml"));
502 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
503 QVERIFY(object != 0);
504 QCOMPARE(object->string(), QString());
505 emit object->signalWithGlobalName(19);
506 QCOMPARE(object->string(), QString("pass 5"));
511 void tst_qqmlecmascript::methods()
514 QQmlComponent component(&engine, testFileUrl("methods.1.qml"));
515 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
516 QVERIFY(object != 0);
517 QCOMPARE(object->methodCalled(), false);
518 QCOMPARE(object->methodIntCalled(), false);
519 emit object->basicSignal();
520 QCOMPARE(object->methodCalled(), true);
521 QCOMPARE(object->methodIntCalled(), false);
526 QQmlComponent component(&engine, testFileUrl("methods.2.qml"));
527 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
528 QVERIFY(object != 0);
529 QCOMPARE(object->methodCalled(), false);
530 QCOMPARE(object->methodIntCalled(), false);
531 emit object->basicSignal();
532 QCOMPARE(object->methodCalled(), false);
533 QCOMPARE(object->methodIntCalled(), true);
538 QQmlComponent component(&engine, testFileUrl("methods.3.qml"));
539 QObject *object = component.create();
540 QVERIFY(object != 0);
541 QCOMPARE(object->property("test").toInt(), 19);
546 QQmlComponent component(&engine, testFileUrl("methods.4.qml"));
547 QObject *object = component.create();
548 QVERIFY(object != 0);
549 QCOMPARE(object->property("test").toInt(), 19);
550 QCOMPARE(object->property("test2").toInt(), 17);
551 QCOMPARE(object->property("test3").toInt(), 16);
556 QQmlComponent component(&engine, testFileUrl("methods.5.qml"));
557 QObject *object = component.create();
558 QVERIFY(object != 0);
559 QCOMPARE(object->property("test").toInt(), 9);
564 void tst_qqmlecmascript::bindingLoop()
566 QQmlComponent component(&engine, testFileUrl("bindingLoop.qml"));
567 QString warning = component.url().toString() + ":5:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\"";
568 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
569 QObject *object = component.create();
570 QVERIFY(object != 0);
574 void tst_qqmlecmascript::basicExpressions_data()
576 QTest::addColumn<QString>("expression");
577 QTest::addColumn<QVariant>("result");
578 QTest::addColumn<bool>("nest");
580 QTest::newRow("Syntax error (self test)") << "{console.log({'a':1'}.a)}" << QVariant() << false;
581 QTest::newRow("Context property") << "a" << QVariant(1944) << false;
582 QTest::newRow("Context property") << "a" << QVariant(1944) << true;
583 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << false;
584 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << true;
585 QTest::newRow("Overridden context property") << "b" << QVariant("Milk") << false;
586 QTest::newRow("Overridden context property") << "b" << QVariant("Cow") << true;
587 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << false;
588 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << true;
589 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object2") << false;
590 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object3") << true;
591 QTest::newRow("Default object property") << "horseLegs" << QVariant(4) << false;
592 QTest::newRow("Default object property") << "antLegs" << QVariant(6) << false;
593 QTest::newRow("Default object property") << "emuLegs" << QVariant(2) << false;
594 QTest::newRow("Nested default object property") << "horseLegs" << QVariant(4) << true;
595 QTest::newRow("Nested default object property") << "antLegs" << QVariant(7) << true;
596 QTest::newRow("Nested default object property") << "emuLegs" << QVariant(2) << true;
597 QTest::newRow("Nested default object property") << "humanLegs" << QVariant(2) << true;
598 QTest::newRow("Context property override default object property") << "millipedeLegs" << QVariant(100) << true;
601 void tst_qqmlecmascript::basicExpressions()
603 QFETCH(QString, expression);
604 QFETCH(QVariant, result);
610 MyDefaultObject1 default1;
611 MyDefaultObject3 default3;
612 object1.setStringProperty("Object1");
613 object2.setStringProperty("Object2");
614 object3.setStringProperty("Object3");
616 QQmlContext context(engine.rootContext());
617 QQmlContext nestedContext(&context);
619 context.setContextObject(&default1);
620 context.setContextProperty("a", QVariant(1944));
621 context.setContextProperty("b", QVariant("Milk"));
622 context.setContextProperty("object", &object1);
623 context.setContextProperty("objectOverride", &object2);
624 nestedContext.setContextObject(&default3);
625 nestedContext.setContextProperty("b", QVariant("Cow"));
626 nestedContext.setContextProperty("objectOverride", &object3);
627 nestedContext.setContextProperty("millipedeLegs", QVariant(100));
629 MyExpression expr(nest?&nestedContext:&context, expression);
630 QCOMPARE(expr.evaluate(), result);
633 void tst_qqmlecmascript::arrayExpressions()
639 QQmlContext context(engine.rootContext());
640 context.setContextProperty("a", &obj1);
641 context.setContextProperty("b", &obj2);
642 context.setContextProperty("c", &obj3);
644 MyExpression expr(&context, "[a, b, c, 10]");
645 QVariant result = expr.evaluate();
646 QCOMPARE(result.userType(), qMetaTypeId<QList<QObject *> >());
647 QList<QObject *> list = qvariant_cast<QList<QObject *> >(result);
648 QCOMPARE(list.count(), 4);
649 QCOMPARE(list.at(0), &obj1);
650 QCOMPARE(list.at(1), &obj2);
651 QCOMPARE(list.at(2), &obj3);
652 QCOMPARE(list.at(3), (QObject *)0);
655 // Tests that modifying a context property will reevaluate expressions
656 void tst_qqmlecmascript::contextPropertiesTriggerReeval()
658 QQmlContext context(engine.rootContext());
661 MyQmlObject *object3 = new MyQmlObject;
663 object1.setStringProperty("Hello");
664 object2.setStringProperty("World");
666 context.setContextProperty("testProp", QVariant(1));
667 context.setContextProperty("testObj", &object1);
668 context.setContextProperty("testObj2", object3);
671 MyExpression expr(&context, "testProp + 1");
672 QCOMPARE(expr.changed, false);
673 QCOMPARE(expr.evaluate(), QVariant(2));
675 context.setContextProperty("testProp", QVariant(2));
676 QCOMPARE(expr.changed, true);
677 QCOMPARE(expr.evaluate(), QVariant(3));
681 MyExpression expr(&context, "testProp + testProp + testProp");
682 QCOMPARE(expr.changed, false);
683 QCOMPARE(expr.evaluate(), QVariant(6));
685 context.setContextProperty("testProp", QVariant(4));
686 QCOMPARE(expr.changed, true);
687 QCOMPARE(expr.evaluate(), QVariant(12));
691 MyExpression expr(&context, "testObj.stringProperty");
692 QCOMPARE(expr.changed, false);
693 QCOMPARE(expr.evaluate(), QVariant("Hello"));
695 context.setContextProperty("testObj", &object2);
696 QCOMPARE(expr.changed, true);
697 QCOMPARE(expr.evaluate(), QVariant("World"));
701 MyExpression expr(&context, "testObj.stringProperty /**/");
702 QCOMPARE(expr.changed, false);
703 QCOMPARE(expr.evaluate(), QVariant("World"));
705 context.setContextProperty("testObj", &object1);
706 QCOMPARE(expr.changed, true);
707 QCOMPARE(expr.evaluate(), QVariant("Hello"));
711 MyExpression expr(&context, "testObj2");
712 QCOMPARE(expr.changed, false);
713 QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3));
719 void tst_qqmlecmascript::objectPropertiesTriggerReeval()
721 QQmlContext context(engine.rootContext());
725 context.setContextProperty("testObj", &object1);
727 object1.setStringProperty(QLatin1String("Hello"));
728 object2.setStringProperty(QLatin1String("Dog"));
729 object3.setStringProperty(QLatin1String("Cat"));
732 MyExpression expr(&context, "testObj.stringProperty");
733 QCOMPARE(expr.changed, false);
734 QCOMPARE(expr.evaluate(), QVariant("Hello"));
736 object1.setStringProperty(QLatin1String("World"));
737 QCOMPARE(expr.changed, true);
738 QCOMPARE(expr.evaluate(), QVariant("World"));
742 MyExpression expr(&context, "testObj.objectProperty.stringProperty");
743 QCOMPARE(expr.changed, false);
744 QCOMPARE(expr.evaluate(), QVariant());
746 object1.setObjectProperty(&object2);
747 QCOMPARE(expr.changed, true);
748 expr.changed = false;
749 QCOMPARE(expr.evaluate(), QVariant("Dog"));
751 object1.setObjectProperty(&object3);
752 QCOMPARE(expr.changed, true);
753 expr.changed = false;
754 QCOMPARE(expr.evaluate(), QVariant("Cat"));
756 object1.setObjectProperty(0);
757 QCOMPARE(expr.changed, true);
758 expr.changed = false;
759 QCOMPARE(expr.evaluate(), QVariant());
761 object1.setObjectProperty(&object3);
762 QCOMPARE(expr.changed, true);
763 expr.changed = false;
764 QCOMPARE(expr.evaluate(), QVariant("Cat"));
766 object3.setStringProperty("Donkey");
767 QCOMPARE(expr.changed, true);
768 expr.changed = false;
769 QCOMPARE(expr.evaluate(), QVariant("Donkey"));
773 void tst_qqmlecmascript::deferredProperties()
775 QQmlComponent component(&engine, testFileUrl("deferredProperties.qml"));
776 MyDeferredObject *object =
777 qobject_cast<MyDeferredObject *>(component.create());
778 QVERIFY(object != 0);
779 QCOMPARE(object->value(), 0);
780 QVERIFY(object->objectProperty() == 0);
781 QVERIFY(object->objectProperty2() != 0);
782 qmlExecuteDeferred(object);
783 QCOMPARE(object->value(), 10);
784 QVERIFY(object->objectProperty() != 0);
785 MyQmlObject *qmlObject =
786 qobject_cast<MyQmlObject *>(object->objectProperty());
787 QVERIFY(qmlObject != 0);
788 QCOMPARE(qmlObject->value(), 10);
789 object->setValue(19);
790 QCOMPARE(qmlObject->value(), 19);
795 // Check errors on deferred properties are correctly emitted
796 void tst_qqmlecmascript::deferredPropertiesErrors()
798 QQmlComponent component(&engine, testFileUrl("deferredPropertiesErrors.qml"));
799 MyDeferredObject *object =
800 qobject_cast<MyDeferredObject *>(component.create());
801 QVERIFY(object != 0);
802 QCOMPARE(object->value(), 0);
803 QVERIFY(object->objectProperty() == 0);
804 QVERIFY(object->objectProperty2() == 0);
806 QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject*";
807 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
809 qmlExecuteDeferred(object);
814 void tst_qqmlecmascript::extensionObjects()
816 QQmlComponent component(&engine, testFileUrl("extensionObjects.qml"));
817 MyExtendedObject *object =
818 qobject_cast<MyExtendedObject *>(component.create());
819 QVERIFY(object != 0);
820 QCOMPARE(object->baseProperty(), 13);
821 QCOMPARE(object->coreProperty(), 9);
822 object->setProperty("extendedProperty", QVariant(11));
823 object->setProperty("baseExtendedProperty", QVariant(92));
824 QCOMPARE(object->coreProperty(), 11);
825 QCOMPARE(object->baseProperty(), 92);
827 MyExtendedObject *nested = qobject_cast<MyExtendedObject*>(qvariant_cast<QObject *>(object->property("nested")));
829 QCOMPARE(nested->baseProperty(), 13);
830 QCOMPARE(nested->coreProperty(), 9);
831 nested->setProperty("extendedProperty", QVariant(11));
832 nested->setProperty("baseExtendedProperty", QVariant(92));
833 QCOMPARE(nested->coreProperty(), 11);
834 QCOMPARE(nested->baseProperty(), 92);
839 void tst_qqmlecmascript::overrideExtensionProperties()
841 QQmlComponent component(&engine, testFileUrl("extensionObjectsPropertyOverride.qml"));
842 OverrideDefaultPropertyObject *object =
843 qobject_cast<OverrideDefaultPropertyObject *>(component.create());
844 QVERIFY(object != 0);
845 QVERIFY(object->secondProperty() != 0);
846 QVERIFY(object->firstProperty() == 0);
851 void tst_qqmlecmascript::attachedProperties()
854 QQmlComponent component(&engine, testFileUrl("attachedProperty.qml"));
855 QObject *object = component.create();
856 QVERIFY(object != 0);
857 QCOMPARE(object->property("a").toInt(), 19);
858 QCOMPARE(object->property("b").toInt(), 19);
859 QCOMPARE(object->property("c").toInt(), 19);
860 QCOMPARE(object->property("d").toInt(), 19);
865 QQmlComponent component(&engine, testFileUrl("attachedProperty.2.qml"));
866 QObject *object = component.create();
867 QVERIFY(object != 0);
868 QCOMPARE(object->property("a").toInt(), 26);
869 QCOMPARE(object->property("b").toInt(), 26);
870 QCOMPARE(object->property("c").toInt(), 26);
871 QCOMPARE(object->property("d").toInt(), 26);
877 QQmlComponent component(&engine, testFileUrl("writeAttachedProperty.qml"));
878 QObject *object = component.create();
879 QVERIFY(object != 0);
881 QMetaObject::invokeMethod(object, "writeValue2");
883 MyQmlAttachedObject *attached =
884 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
885 QVERIFY(attached != 0);
887 QCOMPARE(attached->value2(), 9);
892 void tst_qqmlecmascript::enums()
896 QQmlComponent component(&engine, testFileUrl("enums.1.qml"));
897 QObject *object = component.create();
898 QVERIFY(object != 0);
900 QCOMPARE(object->property("a").toInt(), 0);
901 QCOMPARE(object->property("b").toInt(), 1);
902 QCOMPARE(object->property("c").toInt(), 2);
903 QCOMPARE(object->property("d").toInt(), 3);
904 QCOMPARE(object->property("e").toInt(), 0);
905 QCOMPARE(object->property("f").toInt(), 1);
906 QCOMPARE(object->property("g").toInt(), 2);
907 QCOMPARE(object->property("h").toInt(), 3);
908 QCOMPARE(object->property("i").toInt(), 19);
909 QCOMPARE(object->property("j").toInt(), 19);
913 // Non-existent enums
915 QQmlComponent component(&engine, testFileUrl("enums.2.qml"));
917 QString warning1 = component.url().toString() + ":5: Unable to assign [undefined] to int";
918 QString warning2 = component.url().toString() + ":6: Unable to assign [undefined] to int";
919 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
920 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
922 QObject *object = component.create();
923 QVERIFY(object != 0);
924 QCOMPARE(object->property("a").toInt(), 0);
925 QCOMPARE(object->property("b").toInt(), 0);
931 QQmlComponent component(&engine, testFileUrl("enums.3.qml"));
932 QObject *object = component.create();
933 QVERIFY(object != 0);
935 // check the values are what we expect
936 QCOMPARE(object->property("a").toInt(), 4);
937 QCOMPARE(object->property("b").toInt(), 5);
938 QCOMPARE(object->property("c").toInt(), 9);
939 QCOMPARE(object->property("d").toInt(), 13);
940 QCOMPARE(object->property("e").toInt(), 2);
941 QCOMPARE(object->property("f").toInt(), 3);
942 QCOMPARE(object->property("h").toInt(), 2);
943 QCOMPARE(object->property("i").toInt(), 3);
945 // count of change signals
946 QCOMPARE(object->property("ac").toInt(), 0);
947 QCOMPARE(object->property("bc").toInt(), 0);
948 QCOMPARE(object->property("cc").toInt(), 0);
949 QCOMPARE(object->property("dc").toInt(), 0);
950 QCOMPARE(object->property("ec").toInt(), 0);
951 QCOMPARE(object->property("fc").toInt(), 0);
952 QCOMPARE(object->property("hc").toInt(), 1); // namespace -> binding
953 QCOMPARE(object->property("ic").toInt(), 1); // namespace -> binding
959 void tst_qqmlecmascript::valueTypeFunctions()
961 QQmlComponent component(&engine, testFileUrl("valueTypeFunctions.qml"));
962 MyTypeObject *obj = qobject_cast<MyTypeObject*>(component.create());
964 QCOMPARE(obj->rectProperty(), QRect(0,0,100,100));
965 QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5));
971 Tests that writing a constant to a property with a binding on it disables the
974 void tst_qqmlecmascript::constantsOverrideBindings()
978 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.1.qml"));
979 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
980 QVERIFY(object != 0);
982 QCOMPARE(object->property("c2").toInt(), 0);
983 object->setProperty("c1", QVariant(9));
984 QCOMPARE(object->property("c2").toInt(), 9);
986 emit object->basicSignal();
988 QCOMPARE(object->property("c2").toInt(), 13);
989 object->setProperty("c1", QVariant(8));
990 QCOMPARE(object->property("c2").toInt(), 13);
995 // During construction
997 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.2.qml"));
998 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
999 QVERIFY(object != 0);
1001 QCOMPARE(object->property("c1").toInt(), 0);
1002 QCOMPARE(object->property("c2").toInt(), 10);
1003 object->setProperty("c1", QVariant(9));
1004 QCOMPARE(object->property("c1").toInt(), 9);
1005 QCOMPARE(object->property("c2").toInt(), 10);
1013 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.3.qml"));
1014 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1015 QVERIFY(object != 0);
1017 QCOMPARE(object->property("c2").toInt(), 0);
1018 object->setProperty("c1", QVariant(9));
1019 QCOMPARE(object->property("c2").toInt(), 9);
1021 object->setProperty("c2", QVariant(13));
1022 QCOMPARE(object->property("c2").toInt(), 13);
1023 object->setProperty("c1", QVariant(7));
1024 QCOMPARE(object->property("c1").toInt(), 7);
1025 QCOMPARE(object->property("c2").toInt(), 13);
1033 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.4.qml"));
1034 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1035 QVERIFY(object != 0);
1037 QCOMPARE(object->property("c1").toInt(), 0);
1038 QCOMPARE(object->property("c3").toInt(), 10);
1039 object->setProperty("c1", QVariant(9));
1040 QCOMPARE(object->property("c1").toInt(), 9);
1041 QCOMPARE(object->property("c3").toInt(), 10);
1048 Tests that assigning a binding to a property that already has a binding causes
1049 the original binding to be disabled.
1051 void tst_qqmlecmascript::outerBindingOverridesInnerBinding()
1053 QQmlComponent component(&engine,
1054 testFileUrl("outerBindingOverridesInnerBinding.qml"));
1055 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1056 QVERIFY(object != 0);
1058 QCOMPARE(object->property("c1").toInt(), 0);
1059 QCOMPARE(object->property("c2").toInt(), 0);
1060 QCOMPARE(object->property("c3").toInt(), 0);
1062 object->setProperty("c1", QVariant(9));
1063 QCOMPARE(object->property("c1").toInt(), 9);
1064 QCOMPARE(object->property("c2").toInt(), 0);
1065 QCOMPARE(object->property("c3").toInt(), 0);
1067 object->setProperty("c3", QVariant(8));
1068 QCOMPARE(object->property("c1").toInt(), 9);
1069 QCOMPARE(object->property("c2").toInt(), 8);
1070 QCOMPARE(object->property("c3").toInt(), 8);
1076 Access a non-existent attached object.
1078 Tests for a regression where this used to crash.
1080 void tst_qqmlecmascript::nonExistentAttachedObject()
1082 QQmlComponent component(&engine, testFileUrl("nonExistentAttachedObject.qml"));
1084 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString";
1085 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
1087 QObject *object = component.create();
1088 QVERIFY(object != 0);
1093 void tst_qqmlecmascript::scope()
1096 QQmlComponent component(&engine, testFileUrl("scope.qml"));
1097 QObject *object = component.create();
1098 QVERIFY(object != 0);
1100 QCOMPARE(object->property("test1").toInt(), 1);
1101 QCOMPARE(object->property("test2").toInt(), 2);
1102 QCOMPARE(object->property("test3").toString(), QString("1Test"));
1103 QCOMPARE(object->property("test4").toString(), QString("2Test"));
1104 QCOMPARE(object->property("test5").toInt(), 1);
1105 QCOMPARE(object->property("test6").toInt(), 1);
1106 QCOMPARE(object->property("test7").toInt(), 2);
1107 QCOMPARE(object->property("test8").toInt(), 2);
1108 QCOMPARE(object->property("test9").toInt(), 1);
1109 QCOMPARE(object->property("test10").toInt(), 3);
1115 QQmlComponent component(&engine, testFileUrl("scope.2.qml"));
1116 QObject *object = component.create();
1117 QVERIFY(object != 0);
1119 QCOMPARE(object->property("test1").toInt(), 19);
1120 QCOMPARE(object->property("test2").toInt(), 19);
1121 QCOMPARE(object->property("test3").toInt(), 14);
1122 QCOMPARE(object->property("test4").toInt(), 14);
1123 QCOMPARE(object->property("test5").toInt(), 24);
1124 QCOMPARE(object->property("test6").toInt(), 24);
1130 QQmlComponent component(&engine, testFileUrl("scope.3.qml"));
1131 QObject *object = component.create();
1132 QVERIFY(object != 0);
1134 QCOMPARE(object->property("test1").toBool(), true);
1135 QCOMPARE(object->property("test2").toBool(), true);
1136 QCOMPARE(object->property("test3").toBool(), true);
1141 // Signal argument scope
1143 QQmlComponent component(&engine, testFileUrl("scope.4.qml"));
1144 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1145 QVERIFY(object != 0);
1147 QCOMPARE(object->property("test").toInt(), 0);
1148 QCOMPARE(object->property("test2").toString(), QString());
1150 emit object->argumentSignal(13, "Argument Scope", 9, MyQmlObject::EnumValue4, Qt::RightButton);
1152 QCOMPARE(object->property("test").toInt(), 13);
1153 QCOMPARE(object->property("test2").toString(), QString("Argument Scope"));
1159 QQmlComponent component(&engine, testFileUrl("scope.5.qml"));
1160 QObject *object = component.create();
1161 QVERIFY(object != 0);
1163 QCOMPARE(object->property("test1").toBool(), true);
1164 QCOMPARE(object->property("test2").toBool(), true);
1170 QQmlComponent component(&engine, testFileUrl("scope.6.qml"));
1171 QObject *object = component.create();
1172 QVERIFY(object != 0);
1174 QCOMPARE(object->property("test").toBool(), true);
1180 // In 4.7, non-library javascript files that had no imports shared the imports of their
1181 // importing context
1182 void tst_qqmlecmascript::importScope()
1184 QQmlComponent component(&engine, testFileUrl("importScope.qml"));
1185 QObject *o = component.create();
1188 QCOMPARE(o->property("test").toInt(), 240);
1194 Tests that "any" type passes through a synthesized signal parameter. This
1195 is essentially a test of QQmlMetaType::copy()
1197 void tst_qqmlecmascript::signalParameterTypes()
1199 QQmlComponent component(&engine, testFileUrl("signalParameterTypes.qml"));
1200 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1201 QVERIFY(object != 0);
1203 emit object->basicSignal();
1205 QCOMPARE(object->property("intProperty").toInt(), 10);
1206 QCOMPARE(object->property("realProperty").toReal(), 19.2);
1207 QVERIFY(object->property("colorProperty").value<QColor>() == QColor(255, 255, 0, 255));
1208 QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255)));
1209 QVERIFY(object->property("enumProperty") == MyQmlObject::EnumValue3);
1210 QVERIFY(object->property("qtEnumProperty") == Qt::LeftButton);
1216 Test that two JS objects for the same QObject compare as equal.
1218 void tst_qqmlecmascript::objectsCompareAsEqual()
1220 QQmlComponent component(&engine, testFileUrl("objectsCompareAsEqual.qml"));
1221 QObject *object = component.create();
1222 QVERIFY(object != 0);
1224 QCOMPARE(object->property("test1").toBool(), true);
1225 QCOMPARE(object->property("test2").toBool(), true);
1226 QCOMPARE(object->property("test3").toBool(), true);
1227 QCOMPARE(object->property("test4").toBool(), true);
1228 QCOMPARE(object->property("test5").toBool(), true);
1234 Confirm bindings and alias properties can coexist.
1236 Tests for a regression where the binding would not reevaluate.
1238 void tst_qqmlecmascript::aliasPropertyAndBinding()
1240 QQmlComponent component(&engine, testFileUrl("aliasPropertyAndBinding.qml"));
1241 QObject *object = component.create();
1242 QVERIFY(object != 0);
1244 QCOMPARE(object->property("c2").toInt(), 3);
1245 QCOMPARE(object->property("c3").toInt(), 3);
1247 object->setProperty("c2", QVariant(19));
1249 QCOMPARE(object->property("c2").toInt(), 19);
1250 QCOMPARE(object->property("c3").toInt(), 19);
1256 Ensure that we can write undefined value to an alias property,
1257 and that the aliased property is reset correctly if possible.
1259 void tst_qqmlecmascript::aliasPropertyReset()
1261 QObject *object = 0;
1263 // test that a manual write (of undefined) to a resettable aliased property succeeds
1264 QQmlComponent c1(&engine, testFileUrl("aliasreset/aliasPropertyReset.1.qml"));
1265 object = c1.create();
1266 QVERIFY(object != 0);
1267 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1268 QCOMPARE(object->property("aliasIsUndefined"), QVariant(false));
1269 QMetaObject::invokeMethod(object, "resetAliased");
1270 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1271 QCOMPARE(object->property("aliasIsUndefined"), QVariant(true));
1274 // test that a manual write (of undefined) to a resettable alias property succeeds
1275 QQmlComponent c2(&engine, testFileUrl("aliasreset/aliasPropertyReset.2.qml"));
1276 object = c2.create();
1277 QVERIFY(object != 0);
1278 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1279 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false));
1280 QMetaObject::invokeMethod(object, "resetAlias");
1281 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1282 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(true));
1285 // test that an alias to a bound property works correctly
1286 QQmlComponent c3(&engine, testFileUrl("aliasreset/aliasPropertyReset.3.qml"));
1287 object = c3.create();
1288 QVERIFY(object != 0);
1289 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1290 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false));
1291 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1292 QMetaObject::invokeMethod(object, "resetAlias");
1293 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1294 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(true));
1295 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1298 // test that a manual write (of undefined) to a resettable alias property
1299 // whose aliased property's object has been deleted, does not crash.
1300 QQmlComponent c4(&engine, testFileUrl("aliasreset/aliasPropertyReset.4.qml"));
1301 object = c4.create();
1302 QVERIFY(object != 0);
1303 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1304 QObject *loader = object->findChild<QObject*>("loader");
1305 QVERIFY(loader != 0);
1307 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // deletion should have caused value unset.
1308 QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash.
1309 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1310 QMetaObject::invokeMethod(object, "setAlias"); // shouldn't crash, and shouldn't change value (since it's no longer referencing anything).
1311 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1314 // test that binding an alias property to an undefined value works correctly
1315 QQmlComponent c5(&engine, testFileUrl("aliasreset/aliasPropertyReset.5.qml"));
1316 object = c5.create();
1317 QVERIFY(object != 0);
1318 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // bound to undefined value.
1321 // test that a manual write (of undefined) to a non-resettable property fails properly
1322 QUrl url = testFileUrl("aliasreset/aliasPropertyReset.error.1.qml");
1323 QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int");
1324 QQmlComponent e1(&engine, url);
1325 object = e1.create();
1326 QVERIFY(object != 0);
1327 QCOMPARE(object->property("intAlias").value<int>(), 12);
1328 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1329 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1330 QMetaObject::invokeMethod(object, "resetAlias");
1331 QCOMPARE(object->property("intAlias").value<int>(), 12);
1332 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1336 void tst_qqmlecmascript::componentCreation_data()
1338 QTest::addColumn<QString>("method");
1339 QTest::addColumn<QString>("creationError");
1340 QTest::addColumn<QString>("createdParent");
1342 QTest::newRow("url")
1346 QTest::newRow("urlMode")
1350 QTest::newRow("urlParent")
1354 QTest::newRow("urlNullParent")
1358 QTest::newRow("urlModeParent")
1362 QTest::newRow("urlModeNullParent")
1363 << "urlModeNullParent"
1366 QTest::newRow("invalidSecondArg")
1367 << "invalidSecondArg"
1368 << ":40: Error: Qt.createComponent(): Invalid arguments"
1370 QTest::newRow("invalidThirdArg")
1371 << "invalidThirdArg"
1372 << ":45: Error: Qt.createComponent(): Invalid parent object"
1374 QTest::newRow("invalidMode")
1376 << ":50: Error: Qt.createComponent(): Invalid arguments"
1381 Test using createComponent to dynamically generate a component.
1383 void tst_qqmlecmascript::componentCreation()
1385 QFETCH(QString, method);
1386 QFETCH(QString, creationError);
1387 QFETCH(QString, createdParent);
1389 QUrl testUrl(testFileUrl("componentCreation.qml"));
1391 if (!creationError.isEmpty()) {
1392 QString warning = testUrl.toString() + creationError;
1393 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1396 QQmlComponent component(&engine, testUrl);
1397 MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create());
1398 QVERIFY(object != 0);
1400 QMetaObject::invokeMethod(object, method.toUtf8());
1401 QQmlComponent *created = object->componentProperty();
1403 if (creationError.isEmpty()) {
1406 QObject *expectedParent;
1407 if (createdParent.isEmpty()) {
1408 // For now, the parent should be the engine; this will change for QTBUG-24841
1409 expectedParent = &engine;
1410 } else if (createdParent == QLatin1String("obj")) {
1411 expectedParent = object;
1412 } else if (createdParent == QLatin1String("null")) {
1415 QCOMPARE(created->parent(), expectedParent);
1419 void tst_qqmlecmascript::dynamicCreation_data()
1421 QTest::addColumn<QString>("method");
1422 QTest::addColumn<QString>("createdName");
1424 QTest::newRow("One") << "createOne" << "objectOne";
1425 QTest::newRow("Two") << "createTwo" << "objectTwo";
1426 QTest::newRow("Three") << "createThree" << "objectThree";
1430 Test using createQmlObject to dynamically generate an item
1431 Also using createComponent is tested.
1433 void tst_qqmlecmascript::dynamicCreation()
1435 QFETCH(QString, method);
1436 QFETCH(QString, createdName);
1438 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
1439 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1440 QVERIFY(object != 0);
1442 QMetaObject::invokeMethod(object, method.toUtf8());
1443 QObject *created = object->objectProperty();
1445 QCOMPARE(created->objectName(), createdName);
1451 Tests the destroy function
1453 void tst_qqmlecmascript::dynamicDestruction()
1456 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.qml"));
1457 QQmlGuard<MyQmlObject> object = qobject_cast<MyQmlObject*>(component.create());
1458 QVERIFY(object != 0);
1459 QQmlGuard<QObject> createdQmlObject = 0;
1461 QMetaObject::invokeMethod(object, "create");
1462 createdQmlObject = object->objectProperty();
1463 QVERIFY(createdQmlObject);
1464 QCOMPARE(createdQmlObject->objectName(), QString("emptyObject"));
1466 QMetaObject::invokeMethod(object, "killOther");
1467 QVERIFY(createdQmlObject);
1469 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1470 QCoreApplication::processEvents();
1471 QVERIFY(createdQmlObject);
1472 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1473 if (createdQmlObject) {
1475 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1476 QCoreApplication::processEvents();
1479 QVERIFY(!createdQmlObject);
1481 QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
1482 QMetaObject::invokeMethod(object, "killMe");
1484 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1485 QCoreApplication::processEvents();
1490 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.2.qml"));
1491 QObject *o = component.create();
1494 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1496 QMetaObject::invokeMethod(o, "create");
1498 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) != 0);
1500 QMetaObject::invokeMethod(o, "destroy");
1502 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1503 QCoreApplication::processEvents();
1505 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1512 QQmlGuard<QObject> createdQmlObject = 0;
1513 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.3.qml"));
1514 QObject *o = component.create();
1516 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1517 QMetaObject::invokeMethod(o, "create");
1518 createdQmlObject = qvariant_cast<QObject*>(o->property("objectProperty"));
1519 QVERIFY(createdQmlObject);
1520 QMetaObject::invokeMethod(o, "destroy");
1521 QVERIFY(qvariant_cast<bool>(o->property("test")) == false);
1522 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1524 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1525 QCoreApplication::processEvents();
1527 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1528 QVERIFY(qvariant_cast<bool>(o->property("test")) == true);
1534 tests that id.toString() works
1536 void tst_qqmlecmascript::objectToString()
1538 QQmlComponent component(&engine, testFileUrl("qmlToString.qml"));
1539 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1540 QVERIFY(object != 0);
1541 QMetaObject::invokeMethod(object, "testToString");
1542 QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_"));
1543 QVERIFY(object->stringProperty().endsWith(", \"objName\")"));
1549 tests that id.hasOwnProperty() works
1551 void tst_qqmlecmascript::objectHasOwnProperty()
1553 QUrl url = testFileUrl("qmlHasOwnProperty.qml");
1554 QString warning1 = url.toString() + ":59: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1555 QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1556 QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1558 QQmlComponent component(&engine, url);
1559 QObject *object = component.create();
1560 QVERIFY(object != 0);
1562 // test QObjects in QML
1563 QMetaObject::invokeMethod(object, "testHasOwnPropertySuccess");
1564 QVERIFY(object->property("result").value<bool>() == true);
1565 QMetaObject::invokeMethod(object, "testHasOwnPropertyFailure");
1566 QVERIFY(object->property("result").value<bool>() == false);
1568 // now test other types in QML
1569 QObject *child = object->findChild<QObject*>("typeObj");
1570 QVERIFY(child != 0);
1571 QMetaObject::invokeMethod(child, "testHasOwnPropertySuccess");
1572 QCOMPARE(child->property("valueTypeHasOwnProperty").toBool(), true);
1573 QCOMPARE(child->property("valueTypeHasOwnProperty2").toBool(), true);
1574 QCOMPARE(child->property("variantTypeHasOwnProperty").toBool(), true);
1575 QCOMPARE(child->property("stringTypeHasOwnProperty").toBool(), true);
1576 QCOMPARE(child->property("listTypeHasOwnProperty").toBool(), true);
1577 QCOMPARE(child->property("emptyListTypeHasOwnProperty").toBool(), true);
1578 QCOMPARE(child->property("enumTypeHasOwnProperty").toBool(), true);
1579 QCOMPARE(child->property("typenameHasOwnProperty").toBool(), true);
1580 QCOMPARE(child->property("typenameHasOwnProperty2").toBool(), true);
1581 QCOMPARE(child->property("moduleApiTypeHasOwnProperty").toBool(), true);
1582 QCOMPARE(child->property("moduleApiPropertyTypeHasOwnProperty").toBool(), true);
1584 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1585 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureOne");
1586 QCOMPARE(child->property("enumNonValueHasOwnProperty").toBool(), false);
1587 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1588 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureTwo");
1589 QCOMPARE(child->property("moduleApiNonPropertyHasOwnProperty").toBool(), false);
1590 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1591 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureThree");
1592 QCOMPARE(child->property("listAtInvalidHasOwnProperty").toBool(), false);
1598 Tests bindings that indirectly cause their own deletion work.
1600 This test is best run under valgrind to ensure no invalid memory access occur.
1602 void tst_qqmlecmascript::selfDeletingBinding()
1605 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.qml"));
1606 QObject *object = component.create();
1607 QVERIFY(object != 0);
1608 object->setProperty("triggerDelete", true);
1613 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.2.qml"));
1614 QObject *object = component.create();
1615 QVERIFY(object != 0);
1616 object->setProperty("triggerDelete", true);
1622 Test that extended object properties can be accessed.
1624 This test a regression where this used to crash. The issue was specificially
1625 for extended objects that did not include a synthesized meta object (so non-root
1626 and no synthesiszed properties).
1628 void tst_qqmlecmascript::extendedObjectPropertyLookup()
1630 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup.qml"));
1631 QObject *object = component.create();
1632 QVERIFY(object != 0);
1637 Test that extended object properties can be accessed correctly.
1639 void tst_qqmlecmascript::extendedObjectPropertyLookup2()
1641 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup2.qml"));
1642 QObject *object = component.create();
1643 QVERIFY(object != 0);
1645 QVariant returnValue;
1646 QVERIFY(QMetaObject::invokeMethod(object, "getValue", Q_RETURN_ARG(QVariant, returnValue)));
1647 QCOMPARE(returnValue.toInt(), 42);
1652 Test file/lineNumbers for binding/Script errors.
1654 void tst_qqmlecmascript::scriptErrors()
1656 QQmlComponent component(&engine, testFileUrl("scriptErrors.qml"));
1657 QString url = component.url().toString();
1659 QString warning1 = url.left(url.length() - 3) + "js:2: Error: Invalid write to global property \"a\"";
1660 QString warning2 = url + ":5: ReferenceError: a is not defined";
1661 QString warning3 = url.left(url.length() - 3) + "js:4: Error: Invalid write to global property \"a\"";
1662 QString warning4 = url + ":13: ReferenceError: a is not defined";
1663 QString warning5 = url + ":11: ReferenceError: a is not defined";
1664 QString warning6 = url + ":10: Unable to assign [undefined] to int";
1665 QString warning7 = url + ":15: Error: Cannot assign to read-only property \"trueProperty\"";
1666 QString warning8 = url + ":16: Error: Cannot assign to non-existent property \"fakeProperty\"";
1668 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1669 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1670 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1671 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
1672 QTest::ignoreMessage(QtWarningMsg, warning6.toLatin1().constData());
1673 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1674 QVERIFY(object != 0);
1676 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
1677 emit object->basicSignal();
1679 QTest::ignoreMessage(QtWarningMsg, warning7.toLatin1().constData());
1680 emit object->anotherBasicSignal();
1682 QTest::ignoreMessage(QtWarningMsg, warning8.toLatin1().constData());
1683 emit object->thirdBasicSignal();
1689 Test file/lineNumbers for inline functions.
1691 void tst_qqmlecmascript::functionErrors()
1693 QQmlComponent component(&engine, testFileUrl("functionErrors.qml"));
1694 QString url = component.url().toString();
1696 QString warning = url + ":5: Error: Invalid write to global property \"a\"";
1698 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1700 QObject *object = component.create();
1701 QVERIFY(object != 0);
1704 // test that if an exception occurs while invoking js function from cpp, it is reported as expected.
1705 QQmlComponent componentTwo(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
1706 url = componentTwo.url().toString();
1707 object = componentTwo.create();
1708 QVERIFY(object != 0);
1710 QString srpname = object->property("srp_name").toString();
1712 warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srpname
1713 + QLatin1String(" is not a function");
1714 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed.
1715 QMetaObject::invokeMethod(object, "retrieveScarceResource");
1720 Test various errors that can occur when assigning a property from script
1722 void tst_qqmlecmascript::propertyAssignmentErrors()
1724 QQmlComponent component(&engine, testFileUrl("propertyAssignmentErrors.qml"));
1726 QString url = component.url().toString();
1728 QObject *object = component.create();
1729 QVERIFY(object != 0);
1731 QCOMPARE(object->property("test1").toBool(), true);
1732 QCOMPARE(object->property("test2").toBool(), true);
1738 Test bindings still work when the reeval is triggered from within
1741 void tst_qqmlecmascript::signalTriggeredBindings()
1743 QQmlComponent component(&engine, testFileUrl("signalTriggeredBindings.qml"));
1744 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1745 QVERIFY(object != 0);
1747 QCOMPARE(object->property("base").toReal(), 50.);
1748 QCOMPARE(object->property("test1").toReal(), 50.);
1749 QCOMPARE(object->property("test2").toReal(), 50.);
1751 object->basicSignal();
1753 QCOMPARE(object->property("base").toReal(), 200.);
1754 QCOMPARE(object->property("test1").toReal(), 200.);
1755 QCOMPARE(object->property("test2").toReal(), 200.);
1757 object->argumentSignal(10, QString(), 10, MyQmlObject::EnumValue4, Qt::RightButton);
1759 QCOMPARE(object->property("base").toReal(), 400.);
1760 QCOMPARE(object->property("test1").toReal(), 400.);
1761 QCOMPARE(object->property("test2").toReal(), 400.);
1767 Test that list properties can be iterated from ECMAScript
1769 void tst_qqmlecmascript::listProperties()
1771 QQmlComponent component(&engine, testFileUrl("listProperties.qml"));
1772 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1773 QVERIFY(object != 0);
1775 QCOMPARE(object->property("test1").toInt(), 21);
1776 QCOMPARE(object->property("test2").toInt(), 2);
1777 QCOMPARE(object->property("test3").toBool(), true);
1778 QCOMPARE(object->property("test4").toBool(), true);
1783 void tst_qqmlecmascript::exceptionClearsOnReeval()
1785 QQmlComponent component(&engine, testFileUrl("exceptionClearsOnReeval.qml"));
1786 QString url = component.url().toString();
1788 QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null";
1790 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1791 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1792 QVERIFY(object != 0);
1794 QCOMPARE(object->property("test").toBool(), false);
1796 MyQmlObject object2;
1797 MyQmlObject object3;
1798 object2.setObjectProperty(&object3);
1799 object->setObjectProperty(&object2);
1801 QCOMPARE(object->property("test").toBool(), true);
1806 void tst_qqmlecmascript::exceptionSlotProducesWarning()
1808 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning.qml"));
1809 QString url = component.url().toString();
1811 QString warning = component.url().toString() + ":6: Error: JS exception";
1813 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1814 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1815 QVERIFY(object != 0);
1819 void tst_qqmlecmascript::exceptionBindingProducesWarning()
1821 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning2.qml"));
1822 QString url = component.url().toString();
1824 QString warning = component.url().toString() + ":5: Error: JS exception";
1826 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1827 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1828 QVERIFY(object != 0);
1832 void tst_qqmlecmascript::compileInvalidBinding()
1834 // QTBUG-23387: ensure that invalid bindings don't cause a crash.
1835 QQmlComponent component(&engine, testFileUrl("v8bindingException.qml"));
1836 QObject *object = component.create();
1837 QVERIFY(object != 0);
1841 static int transientErrorsMsgCount = 0;
1842 static void transientErrorsMsgHandler(QtMsgType, const char *)
1844 ++transientErrorsMsgCount;
1847 // Check that transient binding errors are not displayed
1848 void tst_qqmlecmascript::transientErrors()
1851 QQmlComponent component(&engine, testFileUrl("transientErrors.qml"));
1853 transientErrorsMsgCount = 0;
1854 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1856 QObject *object = component.create();
1857 QVERIFY(object != 0);
1859 qInstallMsgHandler(old);
1861 QCOMPARE(transientErrorsMsgCount, 0);
1866 // One binding erroring multiple times, but then resolving
1868 QQmlComponent component(&engine, testFileUrl("transientErrors.2.qml"));
1870 transientErrorsMsgCount = 0;
1871 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1873 QObject *object = component.create();
1874 QVERIFY(object != 0);
1876 qInstallMsgHandler(old);
1878 QCOMPARE(transientErrorsMsgCount, 0);
1884 // Check that errors during shutdown are minimized
1885 void tst_qqmlecmascript::shutdownErrors()
1887 QQmlComponent component(&engine, testFileUrl("shutdownErrors.qml"));
1888 QObject *object = component.create();
1889 QVERIFY(object != 0);
1891 transientErrorsMsgCount = 0;
1892 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1896 qInstallMsgHandler(old);
1897 QCOMPARE(transientErrorsMsgCount, 0);
1900 void tst_qqmlecmascript::compositePropertyType()
1902 QQmlComponent component(&engine, testFileUrl("compositePropertyType.qml"));
1904 QTest::ignoreMessage(QtDebugMsg, "hello world");
1905 QObject *object = qobject_cast<QObject *>(component.create());
1910 void tst_qqmlecmascript::jsObject()
1912 QQmlComponent component(&engine, testFileUrl("jsObject.qml"));
1913 QObject *object = component.create();
1914 QVERIFY(object != 0);
1916 QCOMPARE(object->property("test").toInt(), 92);
1921 void tst_qqmlecmascript::undefinedResetsProperty()
1924 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.qml"));
1925 QObject *object = component.create();
1926 QVERIFY(object != 0);
1928 QCOMPARE(object->property("resettableProperty").toInt(), 92);
1930 object->setProperty("setUndefined", true);
1932 QCOMPARE(object->property("resettableProperty").toInt(), 13);
1934 object->setProperty("setUndefined", false);
1936 QCOMPARE(object->property("resettableProperty").toInt(), 92);
1941 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.2.qml"));
1942 QObject *object = component.create();
1943 QVERIFY(object != 0);
1945 QCOMPARE(object->property("resettableProperty").toInt(), 19);
1947 QMetaObject::invokeMethod(object, "doReset");
1949 QCOMPARE(object->property("resettableProperty").toInt(), 13);
1955 // Aliases to variant properties should work
1956 void tst_qqmlecmascript::qtbug_22464()
1958 QQmlComponent component(&engine, testFileUrl("qtbug_22464.qml"));
1959 QObject *object = component.create();
1960 QVERIFY(object != 0);
1962 QCOMPARE(object->property("test").toBool(), true);
1967 void tst_qqmlecmascript::qtbug_21580()
1969 QQmlComponent component(&engine, testFileUrl("qtbug_21580.qml"));
1971 QObject *object = component.create();
1972 QVERIFY(object != 0);
1974 QCOMPARE(object->property("test").toBool(), true);
1979 // Causes a v8 binding, but not all v8 bindings to be destroyed during evaluation
1980 void tst_qqmlecmascript::singleV8BindingDestroyedDuringEvaluation()
1982 QQmlComponent component(&engine, testFileUrl("singleV8BindingDestroyedDuringEvaluation.qml"));
1984 QObject *object = component.create();
1985 QVERIFY(object != 0);
1990 void tst_qqmlecmascript::bug1()
1992 QQmlComponent component(&engine, testFileUrl("bug.1.qml"));
1993 QObject *object = component.create();
1994 QVERIFY(object != 0);
1996 QCOMPARE(object->property("test").toInt(), 14);
1998 object->setProperty("a", 11);
2000 QCOMPARE(object->property("test").toInt(), 3);
2002 object->setProperty("b", true);
2004 QCOMPARE(object->property("test").toInt(), 9);
2009 void tst_qqmlecmascript::bug2()
2011 QQmlComponent component(&engine);
2012 component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl());
2014 QObject *object = component.create();
2015 QVERIFY(object != 0);
2020 // Don't crash in createObject when the component has errors.
2021 void tst_qqmlecmascript::dynamicCreationCrash()
2023 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
2024 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2025 QVERIFY(object != 0);
2027 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2028 QMetaObject::invokeMethod(object, "dontCrash");
2029 QObject *created = object->objectProperty();
2030 QVERIFY(created == 0);
2035 // ownership transferred to JS, ensure that GC runs the dtor
2036 void tst_qqmlecmascript::dynamicCreationOwnership()
2039 int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
2041 // allow the engine to go out of scope too.
2043 QQmlEngine dcoEngine;
2044 QQmlComponent component(&dcoEngine, testFileUrl("dynamicCreationOwnership.qml"));
2045 QObject *object = component.create();
2046 QVERIFY(object != 0);
2047 MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo");
2048 QVERIFY(mdcdo != 0);
2049 mdcdo->setDtorCount(&dtorCount);
2051 for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
2052 QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
2054 // we do this once manually, but it should be done automatically
2055 // when the engine goes out of scope (since it should gc in dtor)
2056 QMetaObject::invokeMethod(object, "performGc");
2059 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2060 QCoreApplication::processEvents();
2066 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2067 QCoreApplication::processEvents();
2068 QCOMPARE(dtorCount, expectedDtorCount);
2071 void tst_qqmlecmascript::regExpBug()
2075 QQmlComponent component(&engine, testFileUrl("regExp.qml"));
2076 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2077 QVERIFY(object != 0);
2078 QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]"));
2084 QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString());
2085 QQmlComponent component(&engine, testFileUrl("regExp.2.qml"));
2086 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2087 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2089 QCOMPARE(component.errorString(), err);
2093 static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source)
2095 QString functionSource = QLatin1String("(function(object) { return ") +
2096 QLatin1String(source) + QLatin1String(" })");
2098 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2101 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2102 if (function.IsEmpty())
2104 v8::Handle<v8::Value> args[] = { o };
2105 function->Call(engine->global(), 1, args);
2106 return tc.HasCaught();
2109 static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o,
2110 const char *source, v8::Handle<v8::Value> result)
2112 QString functionSource = QLatin1String("(function(object) { return ") +
2113 QLatin1String(source) + QLatin1String(" })");
2115 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2118 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2119 if (function.IsEmpty())
2121 v8::Handle<v8::Value> args[] = { o };
2123 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2128 return value->StrictEquals(result);
2131 static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o,
2134 QString functionSource = QLatin1String("(function(object) { return ") +
2135 QLatin1String(source) + QLatin1String(" })");
2137 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2139 return v8::Handle<v8::Value>();
2140 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2141 if (function.IsEmpty())
2142 return v8::Handle<v8::Value>();
2143 v8::Handle<v8::Value> args[] = { o };
2145 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2148 return v8::Handle<v8::Value>();
2152 #define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
2153 #define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result)
2154 #define EVALUATE(source) evaluate(engine, object, source)
2156 void tst_qqmlecmascript::callQtInvokables()
2158 MyInvokableObject o;
2160 QQmlEngine qmlengine;
2161 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine);
2163 QV8Engine *engine = ep->v8engine();
2165 v8::HandleScope handle_scope;
2166 v8::Context::Scope scope(engine->context());
2168 v8::Local<v8::Object> object = engine->newQObject(&o)->ToObject();
2170 // Non-existent methods
2172 QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
2173 QCOMPARE(o.error(), false);
2174 QCOMPARE(o.invoked(), -1);
2175 QCOMPARE(o.actuals().count(), 0);
2178 QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)"));
2179 QCOMPARE(o.error(), false);
2180 QCOMPARE(o.invoked(), -1);
2181 QCOMPARE(o.actuals().count(), 0);
2183 // Insufficient arguments
2185 QVERIFY(EVALUATE_ERROR("object.method_int()"));
2186 QCOMPARE(o.error(), false);
2187 QCOMPARE(o.invoked(), -1);
2188 QCOMPARE(o.actuals().count(), 0);
2191 QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
2192 QCOMPARE(o.error(), false);
2193 QCOMPARE(o.invoked(), -1);
2194 QCOMPARE(o.actuals().count(), 0);
2196 // Excessive arguments
2198 QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", v8::Undefined()));
2199 QCOMPARE(o.error(), false);
2200 QCOMPARE(o.invoked(), 8);
2201 QCOMPARE(o.actuals().count(), 1);
2202 QCOMPARE(o.actuals().at(0), QVariant(10));
2205 QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", v8::Undefined()));
2206 QCOMPARE(o.error(), false);
2207 QCOMPARE(o.invoked(), 9);
2208 QCOMPARE(o.actuals().count(), 2);
2209 QCOMPARE(o.actuals().at(0), QVariant(10));
2210 QCOMPARE(o.actuals().at(1), QVariant(11));
2212 // Test return types
2214 QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", v8::Undefined()));
2215 QCOMPARE(o.error(), false);
2216 QCOMPARE(o.invoked(), 0);
2217 QCOMPARE(o.actuals().count(), 0);
2220 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", v8::Integer::New(6)));
2221 QCOMPARE(o.error(), false);
2222 QCOMPARE(o.invoked(), 1);
2223 QCOMPARE(o.actuals().count(), 0);
2226 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", v8::Number::New(19.75)));
2227 QCOMPARE(o.error(), false);
2228 QCOMPARE(o.invoked(), 2);
2229 QCOMPARE(o.actuals().count(), 0);
2233 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QPointF()");
2234 QVERIFY(!ret.IsEmpty());
2235 QCOMPARE(engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5)));
2236 QCOMPARE(o.error(), false);
2237 QCOMPARE(o.invoked(), 3);
2238 QCOMPARE(o.actuals().count(), 0);
2243 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QObject()");
2244 QCOMPARE(engine->toQObject(ret), (QObject *)&o);
2245 QCOMPARE(o.error(), false);
2246 QCOMPARE(o.invoked(), 4);
2247 QCOMPARE(o.actuals().count(), 0);
2251 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_unknown()", v8::Undefined()));
2252 QCOMPARE(o.error(), false);
2253 QCOMPARE(o.invoked(), 5);
2254 QCOMPARE(o.actuals().count(), 0);
2258 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QScriptValue()");
2259 QVERIFY(ret->IsString());
2260 QCOMPARE(engine->toString(ret), QString("Hello world"));
2261 QCOMPARE(o.error(), false);
2262 QCOMPARE(o.invoked(), 6);
2263 QCOMPARE(o.actuals().count(), 0);
2267 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks")));
2268 QCOMPARE(o.error(), false);
2269 QCOMPARE(o.invoked(), 7);
2270 QCOMPARE(o.actuals().count(), 0);
2274 QVERIFY(EVALUATE_VALUE("object.method_int(94)", v8::Undefined()));
2275 QCOMPARE(o.error(), false);
2276 QCOMPARE(o.invoked(), 8);
2277 QCOMPARE(o.actuals().count(), 1);
2278 QCOMPARE(o.actuals().at(0), QVariant(94));
2281 QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", v8::Undefined()));
2282 QCOMPARE(o.error(), false);
2283 QCOMPARE(o.invoked(), 8);
2284 QCOMPARE(o.actuals().count(), 1);
2285 QCOMPARE(o.actuals().at(0), QVariant(94));
2288 QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", v8::Undefined()));
2289 QCOMPARE(o.error(), false);
2290 QCOMPARE(o.invoked(), 8);
2291 QCOMPARE(o.actuals().count(), 1);
2292 QCOMPARE(o.actuals().at(0), QVariant(0));
2295 QVERIFY(EVALUATE_VALUE("object.method_int(null)", v8::Undefined()));
2296 QCOMPARE(o.error(), false);
2297 QCOMPARE(o.invoked(), 8);
2298 QCOMPARE(o.actuals().count(), 1);
2299 QCOMPARE(o.actuals().at(0), QVariant(0));
2302 QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", v8::Undefined()));
2303 QCOMPARE(o.error(), false);
2304 QCOMPARE(o.invoked(), 8);
2305 QCOMPARE(o.actuals().count(), 1);
2306 QCOMPARE(o.actuals().at(0), QVariant(0));
2309 QVERIFY(EVALUATE_VALUE("object.method_int(object)", v8::Undefined()));
2310 QCOMPARE(o.error(), false);
2311 QCOMPARE(o.invoked(), 8);
2312 QCOMPARE(o.actuals().count(), 1);
2313 QCOMPARE(o.actuals().at(0), QVariant(0));
2316 QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", v8::Undefined()));
2317 QCOMPARE(o.error(), false);
2318 QCOMPARE(o.invoked(), 9);
2319 QCOMPARE(o.actuals().count(), 2);
2320 QCOMPARE(o.actuals().at(0), QVariant(122));
2321 QCOMPARE(o.actuals().at(1), QVariant(9));
2324 QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", v8::Undefined()));
2325 QCOMPARE(o.error(), false);
2326 QCOMPARE(o.invoked(), 10);
2327 QCOMPARE(o.actuals().count(), 1);
2328 QCOMPARE(o.actuals().at(0), QVariant(94.3));
2331 QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", v8::Undefined()));
2332 QCOMPARE(o.error(), false);
2333 QCOMPARE(o.invoked(), 10);
2334 QCOMPARE(o.actuals().count(), 1);
2335 QCOMPARE(o.actuals().at(0), QVariant(94.3));
2338 QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", v8::Undefined()));
2339 QCOMPARE(o.error(), false);
2340 QCOMPARE(o.invoked(), 10);
2341 QCOMPARE(o.actuals().count(), 1);
2342 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2345 QVERIFY(EVALUATE_VALUE("object.method_real(null)", v8::Undefined()));
2346 QCOMPARE(o.error(), false);
2347 QCOMPARE(o.invoked(), 10);
2348 QCOMPARE(o.actuals().count(), 1);
2349 QCOMPARE(o.actuals().at(0), QVariant(0));
2352 QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", v8::Undefined()));
2353 QCOMPARE(o.error(), false);
2354 QCOMPARE(o.invoked(), 10);
2355 QCOMPARE(o.actuals().count(), 1);
2356 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2359 QVERIFY(EVALUATE_VALUE("object.method_real(object)", v8::Undefined()));
2360 QCOMPARE(o.error(), false);
2361 QCOMPARE(o.invoked(), 10);
2362 QCOMPARE(o.actuals().count(), 1);
2363 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2366 QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", v8::Undefined()));
2367 QCOMPARE(o.error(), false);
2368 QCOMPARE(o.invoked(), 11);
2369 QCOMPARE(o.actuals().count(), 1);
2370 QCOMPARE(o.actuals().at(0), QVariant("Hello world"));
2373 QVERIFY(EVALUATE_VALUE("object.method_QString(19)", v8::Undefined()));
2374 QCOMPARE(o.error(), false);
2375 QCOMPARE(o.invoked(), 11);
2376 QCOMPARE(o.actuals().count(), 1);
2377 QCOMPARE(o.actuals().at(0), QVariant("19"));
2381 QString expected = "MyInvokableObject(0x" + QString::number((quintptr)&o, 16) + ")";
2382 QVERIFY(EVALUATE_VALUE("object.method_QString(object)", v8::Undefined()));
2383 QCOMPARE(o.error(), false);
2384 QCOMPARE(o.invoked(), 11);
2385 QCOMPARE(o.actuals().count(), 1);
2386 QCOMPARE(o.actuals().at(0), QVariant(expected));
2390 QVERIFY(EVALUATE_VALUE("object.method_QString(null)", v8::Undefined()));
2391 QCOMPARE(o.error(), false);
2392 QCOMPARE(o.invoked(), 11);
2393 QCOMPARE(o.actuals().count(), 1);
2394 QCOMPARE(o.actuals().at(0), QVariant(QString()));
2397 QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", v8::Undefined()));
2398 QCOMPARE(o.error(), false);
2399 QCOMPARE(o.invoked(), 11);
2400 QCOMPARE(o.actuals().count(), 1);
2401 QCOMPARE(o.actuals().at(0), QVariant(QString()));
2404 QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", v8::Undefined()));
2405 QCOMPARE(o.error(), false);
2406 QCOMPARE(o.invoked(), 12);
2407 QCOMPARE(o.actuals().count(), 1);
2408 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2411 QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", v8::Undefined()));
2412 QCOMPARE(o.error(), false);
2413 QCOMPARE(o.invoked(), 12);
2414 QCOMPARE(o.actuals().count(), 1);
2415 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2418 QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", v8::Undefined()));
2419 QCOMPARE(o.error(), false);
2420 QCOMPARE(o.invoked(), 12);
2421 QCOMPARE(o.actuals().count(), 1);
2422 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2425 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", v8::Undefined()));
2426 QCOMPARE(o.error(), false);
2427 QCOMPARE(o.invoked(), 12);
2428 QCOMPARE(o.actuals().count(), 1);
2429 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2432 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", v8::Undefined()));
2433 QCOMPARE(o.error(), false);
2434 QCOMPARE(o.invoked(), 12);
2435 QCOMPARE(o.actuals().count(), 1);
2436 QCOMPARE(o.actuals().at(0), QVariant(QPointF(99.3, -10.2)));
2439 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", v8::Undefined()));
2440 QCOMPARE(o.error(), false);
2441 QCOMPARE(o.invoked(), 12);
2442 QCOMPARE(o.actuals().count(), 1);
2443 QCOMPARE(o.actuals().at(0), QVariant(QPointF(9, 12)));
2446 QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", v8::Undefined()));
2447 QCOMPARE(o.error(), false);
2448 QCOMPARE(o.invoked(), 13);
2449 QCOMPARE(o.actuals().count(), 1);
2450 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2453 QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", v8::Undefined()));
2454 QCOMPARE(o.error(), false);
2455 QCOMPARE(o.invoked(), 13);
2456 QCOMPARE(o.actuals().count(), 1);
2457 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2460 QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", v8::Undefined()));
2461 QCOMPARE(o.error(), false);
2462 QCOMPARE(o.invoked(), 13);
2463 QCOMPARE(o.actuals().count(), 1);
2464 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2467 QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", v8::Undefined()));
2468 QCOMPARE(o.error(), false);
2469 QCOMPARE(o.invoked(), 13);
2470 QCOMPARE(o.actuals().count(), 1);
2471 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2474 QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", v8::Undefined()));
2475 QCOMPARE(o.error(), false);
2476 QCOMPARE(o.invoked(), 13);
2477 QCOMPARE(o.actuals().count(), 1);
2478 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)&o));
2481 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined()));
2482 QCOMPARE(o.error(), false);
2483 QCOMPARE(o.invoked(), 14);
2484 QCOMPARE(o.actuals().count(), 1);
2485 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isNull());
2488 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined()));
2489 QCOMPARE(o.error(), false);
2490 QCOMPARE(o.invoked(), 14);
2491 QCOMPARE(o.actuals().count(), 1);
2492 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isUndefined());
2495 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined()));
2496 QCOMPARE(o.error(), false);
2497 QCOMPARE(o.invoked(), 14);
2498 QCOMPARE(o.actuals().count(), 1);
2499 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).strictlyEquals(QJSValue(19)));
2502 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined()));
2503 QCOMPARE(o.error(), false);
2504 QCOMPARE(o.invoked(), 14);
2505 QCOMPARE(o.actuals().count(), 1);
2506 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isArray());
2509 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined()));
2510 QCOMPARE(o.error(), false);
2511 QCOMPARE(o.invoked(), 15);
2512 QCOMPARE(o.actuals().count(), 2);
2513 QCOMPARE(o.actuals().at(0), QVariant(4));
2514 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isNull());
2517 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined()));
2518 QCOMPARE(o.error(), false);
2519 QCOMPARE(o.invoked(), 15);
2520 QCOMPARE(o.actuals().count(), 2);
2521 QCOMPARE(o.actuals().at(0), QVariant(8));
2522 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isUndefined());
2525 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined()));
2526 QCOMPARE(o.error(), false);
2527 QCOMPARE(o.invoked(), 15);
2528 QCOMPARE(o.actuals().count(), 2);
2529 QCOMPARE(o.actuals().at(0), QVariant(3));
2530 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).strictlyEquals(QJSValue(19)));
2533 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined()));
2534 QCOMPARE(o.error(), false);
2535 QCOMPARE(o.invoked(), 15);
2536 QCOMPARE(o.actuals().count(), 2);
2537 QCOMPARE(o.actuals().at(0), QVariant(44));
2538 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isArray());
2541 QVERIFY(EVALUATE_ERROR("object.method_overload()"));
2542 QCOMPARE(o.error(), false);
2543 QCOMPARE(o.invoked(), -1);
2544 QCOMPARE(o.actuals().count(), 0);
2547 QVERIFY(EVALUATE_VALUE("object.method_overload(10)", v8::Undefined()));
2548 QCOMPARE(o.error(), false);
2549 QCOMPARE(o.invoked(), 16);
2550 QCOMPARE(o.actuals().count(), 1);
2551 QCOMPARE(o.actuals().at(0), QVariant(10));
2554 QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", v8::Undefined()));
2555 QCOMPARE(o.error(), false);
2556 QCOMPARE(o.invoked(), 17);
2557 QCOMPARE(o.actuals().count(), 2);
2558 QCOMPARE(o.actuals().at(0), QVariant(10));
2559 QCOMPARE(o.actuals().at(1), QVariant(11));
2562 QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", v8::Undefined()));
2563 QCOMPARE(o.error(), false);
2564 QCOMPARE(o.invoked(), 18);
2565 QCOMPARE(o.actuals().count(), 1);
2566 QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2569 QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", v8::Undefined()));
2570 QCOMPARE(o.error(), false);
2571 QCOMPARE(o.invoked(), 19);
2572 QCOMPARE(o.actuals().count(), 1);
2573 QCOMPARE(o.actuals().at(0), QVariant(9));
2576 QVERIFY(EVALUATE_VALUE("object.method_default(10)", v8::Integer::New(19)));
2577 QCOMPARE(o.error(), false);
2578 QCOMPARE(o.invoked(), 20);
2579 QCOMPARE(o.actuals().count(), 2);
2580 QCOMPARE(o.actuals().at(0), QVariant(10));
2581 QCOMPARE(o.actuals().at(1), QVariant(19));
2584 QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", v8::Integer::New(13)));
2585 QCOMPARE(o.error(), false);
2586 QCOMPARE(o.invoked(), 20);
2587 QCOMPARE(o.actuals().count(), 2);
2588 QCOMPARE(o.actuals().at(0), QVariant(10));
2589 QCOMPARE(o.actuals().at(1), QVariant(13));
2592 QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", v8::Undefined()));
2593 QCOMPARE(o.error(), false);
2594 QCOMPARE(o.invoked(), -3);
2595 QCOMPARE(o.actuals().count(), 1);
2596 QCOMPARE(o.actuals().at(0), QVariant(9));
2599 QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", v8::Undefined()));
2600 QCOMPARE(o.error(), false);
2601 QCOMPARE(o.invoked(), 21);
2602 QCOMPARE(o.actuals().count(), 2);
2603 QCOMPARE(o.actuals().at(0), QVariant(9));
2604 QCOMPARE(o.actuals().at(1), QVariant());
2607 QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", v8::Undefined()));
2608 QCOMPARE(o.error(), false);
2609 QCOMPARE(o.invoked(), 21);
2610 QCOMPARE(o.actuals().count(), 2);
2611 QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2612 QCOMPARE(o.actuals().at(1), QVariant(QString("World")));
2615 QVERIFY(EVALUATE_VALUE("object.method_QJsonObject({foo:123})", v8::Undefined()));
2616 QCOMPARE(o.error(), false);
2617 QCOMPARE(o.invoked(), 22);
2618 QCOMPARE(o.actuals().count(), 1);
2619 QCOMPARE(qvariant_cast<QJsonObject>(o.actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2622 QVERIFY(EVALUATE_VALUE("object.method_QJsonArray([123])", v8::Undefined()));
2623 QCOMPARE(o.error(), false);
2624 QCOMPARE(o.invoked(), 23);
2625 QCOMPARE(o.actuals().count(), 1);
2626 QCOMPARE(qvariant_cast<QJsonArray>(o.actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2629 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(123)", v8::Undefined()));
2630 QCOMPARE(o.error(), false);
2631 QCOMPARE(o.invoked(), 24);
2632 QCOMPARE(o.actuals().count(), 1);
2633 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(123));
2636 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(42.35)", v8::Undefined()));
2637 QCOMPARE(o.error(), false);
2638 QCOMPARE(o.invoked(), 24);
2639 QCOMPARE(o.actuals().count(), 1);
2640 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(42.35));
2643 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue('ciao')", v8::Undefined()));
2644 QCOMPARE(o.error(), false);
2645 QCOMPARE(o.invoked(), 24);
2646 QCOMPARE(o.actuals().count(), 1);
2647 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QStringLiteral("ciao")));
2650 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(true)", v8::Undefined()));
2651 QCOMPARE(o.error(), false);
2652 QCOMPARE(o.invoked(), 24);
2653 QCOMPARE(o.actuals().count(), 1);
2654 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(true));
2657 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(false)", v8::Undefined()));
2658 QCOMPARE(o.error(), false);
2659 QCOMPARE(o.invoked(), 24);
2660 QCOMPARE(o.actuals().count(), 1);
2661 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(false));
2664 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(null)", v8::Undefined()));
2665 QCOMPARE(o.error(), false);
2666 QCOMPARE(o.invoked(), 24);
2667 QCOMPARE(o.actuals().count(), 1);
2668 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Null));
2671 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(undefined)", v8::Undefined()));
2672 QCOMPARE(o.error(), false);
2673 QCOMPARE(o.invoked(), 24);
2674 QCOMPARE(o.actuals().count(), 1);
2675 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2678 QVERIFY(EVALUATE_VALUE("object.method_overload({foo:123})", v8::Undefined()));
2679 QCOMPARE(o.error(), false);
2680 QCOMPARE(o.invoked(), 25);
2681 QCOMPARE(o.actuals().count(), 1);
2682 QCOMPARE(qvariant_cast<QJsonObject>(o.actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2685 QVERIFY(EVALUATE_VALUE("object.method_overload([123])", v8::Undefined()));
2686 QCOMPARE(o.error(), false);
2687 QCOMPARE(o.invoked(), 26);
2688 QCOMPARE(o.actuals().count(), 1);
2689 QCOMPARE(qvariant_cast<QJsonArray>(o.actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2692 QVERIFY(EVALUATE_VALUE("object.method_overload(null)", v8::Undefined()));
2693 QCOMPARE(o.error(), false);
2694 QCOMPARE(o.invoked(), 27);
2695 QCOMPARE(o.actuals().count(), 1);
2696 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Null));
2699 QVERIFY(EVALUATE_VALUE("object.method_overload(undefined)", v8::Undefined()));
2700 QCOMPARE(o.error(), false);
2701 QCOMPARE(o.invoked(), 27);
2702 QCOMPARE(o.actuals().count(), 1);
2703 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2706 // QTBUG-13047 (check that you can pass registered object types as args)
2707 void tst_qqmlecmascript::invokableObjectArg()
2709 QQmlComponent component(&engine, testFileUrl("invokableObjectArg.qml"));
2711 QObject *o = component.create();
2713 MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o);
2715 QCOMPARE(qmlobject->myinvokableObject, qmlobject);
2720 // QTBUG-13047 (check that you can return registered object types from methods)
2721 void tst_qqmlecmascript::invokableObjectRet()
2723 QQmlComponent component(&engine, testFileUrl("invokableObjectRet.qml"));
2725 QObject *o = component.create();
2727 QCOMPARE(o->property("test").toBool(), true);
2732 void tst_qqmlecmascript::listToVariant()
2734 QQmlComponent component(&engine, testFileUrl("listToVariant.qml"));
2736 MyQmlContainer container;
2738 QQmlContext context(engine.rootContext());
2739 context.setContextObject(&container);
2741 QObject *object = component.create(&context);
2742 QVERIFY(object != 0);
2744 QVariant v = object->property("test");
2745 QCOMPARE(v.userType(), qMetaTypeId<QQmlListReference>());
2746 QVERIFY(qvariant_cast<QQmlListReference>(v).object() == &container);
2752 Q_DECLARE_METATYPE(QQmlListProperty<MyQmlObject>)
2753 void tst_qqmlecmascript::listAssignment()
2755 QQmlComponent component(&engine, testFileUrl("listAssignment.qml"));
2756 QObject *obj = component.create();
2757 QCOMPARE(obj->property("list1length").toInt(), 2);
2758 QQmlListProperty<MyQmlObject> list1 = obj->property("list1").value<QQmlListProperty<MyQmlObject> >();
2759 QQmlListProperty<MyQmlObject> list2 = obj->property("list2").value<QQmlListProperty<MyQmlObject> >();
2760 QCOMPARE(list1.count(&list1), list2.count(&list2));
2761 QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0));
2762 QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1));
2767 void tst_qqmlecmascript::multiEngineObject()
2770 obj.setStringProperty("Howdy planet");
2773 e1.rootContext()->setContextProperty("thing", &obj);
2774 QQmlComponent c1(&e1, testFileUrl("multiEngineObject.qml"));
2777 e2.rootContext()->setContextProperty("thing", &obj);
2778 QQmlComponent c2(&e2, testFileUrl("multiEngineObject.qml"));
2780 QObject *o1 = c1.create();
2781 QObject *o2 = c2.create();
2783 QCOMPARE(o1->property("test").toString(), QString("Howdy planet"));
2784 QCOMPARE(o2->property("test").toString(), QString("Howdy planet"));
2790 // Test that references to QObjects are cleanup when the object is destroyed
2791 void tst_qqmlecmascript::deletedObject()
2793 QQmlComponent component(&engine, testFileUrl("deletedObject.qml"));
2795 QObject *object = component.create();
2797 QCOMPARE(object->property("test1").toBool(), true);
2798 QCOMPARE(object->property("test2").toBool(), true);
2799 QCOMPARE(object->property("test3").toBool(), true);
2800 QCOMPARE(object->property("test4").toBool(), true);
2805 void tst_qqmlecmascript::attachedPropertyScope()
2807 QQmlComponent component(&engine, testFileUrl("attachedPropertyScope.qml"));
2809 QObject *object = component.create();
2810 QVERIFY(object != 0);
2812 MyQmlAttachedObject *attached =
2813 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
2814 QVERIFY(attached != 0);
2816 QCOMPARE(object->property("value2").toInt(), 0);
2818 attached->emitMySignal();
2820 QCOMPARE(object->property("value2").toInt(), 9);
2825 void tst_qqmlecmascript::scriptConnect()
2828 QQmlComponent component(&engine, testFileUrl("scriptConnect.1.qml"));
2830 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2831 QVERIFY(object != 0);
2833 QCOMPARE(object->property("test").toBool(), false);
2834 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2835 QCOMPARE(object->property("test").toBool(), true);
2841 QQmlComponent component(&engine, testFileUrl("scriptConnect.2.qml"));
2843 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2844 QVERIFY(object != 0);
2846 QCOMPARE(object->property("test").toBool(), false);
2847 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2848 QCOMPARE(object->property("test").toBool(), true);
2854 QQmlComponent component(&engine, testFileUrl("scriptConnect.3.qml"));
2856 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2857 QVERIFY(object != 0);
2859 QCOMPARE(object->property("test").toBool(), false);
2860 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2861 QCOMPARE(object->property("test").toBool(), true);
2867 QQmlComponent component(&engine, testFileUrl("scriptConnect.4.qml"));
2869 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2870 QVERIFY(object != 0);
2872 QCOMPARE(object->methodCalled(), false);
2873 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2874 QCOMPARE(object->methodCalled(), true);
2880 QQmlComponent component(&engine, testFileUrl("scriptConnect.5.qml"));
2882 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2883 QVERIFY(object != 0);
2885 QCOMPARE(object->methodCalled(), false);
2886 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2887 QCOMPARE(object->methodCalled(), true);
2893 QQmlComponent component(&engine, testFileUrl("scriptConnect.6.qml"));
2895 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2896 QVERIFY(object != 0);
2898 QCOMPARE(object->property("test").toInt(), 0);
2899 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2900 QCOMPARE(object->property("test").toInt(), 2);
2906 void tst_qqmlecmascript::scriptDisconnect()
2909 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.1.qml"));
2911 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2912 QVERIFY(object != 0);
2914 QCOMPARE(object->property("test").toInt(), 0);
2915 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2916 QCOMPARE(object->property("test").toInt(), 1);
2917 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2918 QCOMPARE(object->property("test").toInt(), 2);
2919 emit object->basicSignal();
2920 QCOMPARE(object->property("test").toInt(), 2);
2921 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2922 QCOMPARE(object->property("test").toInt(), 2);
2928 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.2.qml"));
2930 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2931 QVERIFY(object != 0);
2933 QCOMPARE(object->property("test").toInt(), 0);
2934 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2935 QCOMPARE(object->property("test").toInt(), 1);
2936 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2937 QCOMPARE(object->property("test").toInt(), 2);
2938 emit object->basicSignal();
2939 QCOMPARE(object->property("test").toInt(), 2);
2940 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2941 QCOMPARE(object->property("test").toInt(), 2);
2947 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.3.qml"));
2949 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2950 QVERIFY(object != 0);
2952 QCOMPARE(object->property("test").toInt(), 0);
2953 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2954 QCOMPARE(object->property("test").toInt(), 1);
2955 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2956 QCOMPARE(object->property("test").toInt(), 2);
2957 emit object->basicSignal();
2958 QCOMPARE(object->property("test").toInt(), 2);
2959 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2960 QCOMPARE(object->property("test").toInt(), 3);
2965 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.4.qml"));
2967 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2968 QVERIFY(object != 0);
2970 QCOMPARE(object->property("test").toInt(), 0);
2971 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2972 QCOMPARE(object->property("test").toInt(), 1);
2973 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2974 QCOMPARE(object->property("test").toInt(), 2);
2975 emit object->basicSignal();
2976 QCOMPARE(object->property("test").toInt(), 2);
2977 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2978 QCOMPARE(object->property("test").toInt(), 3);
2984 class OwnershipObject : public QObject
2988 OwnershipObject() { object = new QObject; }
2990 QPointer<QObject> object;
2993 QObject *getObject() { return object; }
2996 void tst_qqmlecmascript::ownership()
2998 OwnershipObject own;
2999 QQmlContext *context = new QQmlContext(engine.rootContext());
3000 context->setContextObject(&own);
3003 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3005 QVERIFY(own.object != 0);
3007 QObject *object = component.create(context);
3009 engine.collectGarbage();
3011 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3012 QCoreApplication::processEvents();
3014 QVERIFY(own.object == 0);
3019 own.object = new QObject(&own);
3022 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3024 QVERIFY(own.object != 0);
3026 QObject *object = component.create(context);
3028 engine.collectGarbage();
3030 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3031 QCoreApplication::processEvents();
3033 QVERIFY(own.object != 0);
3041 class CppOwnershipReturnValue : public QObject
3045 CppOwnershipReturnValue() : value(0) {}
3046 ~CppOwnershipReturnValue() { delete value; }
3048 Q_INVOKABLE QObject *create() {
3049 value = new QObject;
3050 QQmlEngine::setObjectOwnership(value, QQmlEngine::CppOwnership);
3054 Q_INVOKABLE MyQmlObject *createQmlObject() {
3055 MyQmlObject *rv = new MyQmlObject;
3060 QPointer<QObject> value;
3064 // Test setObjectOwnership(CppOwnership) works even when there is no QQmlData
3065 void tst_qqmlecmascript::cppOwnershipReturnValue()
3067 CppOwnershipReturnValue source;
3071 engine.rootContext()->setContextProperty("source", &source);
3073 QVERIFY(source.value == 0);
3075 QQmlComponent component(&engine);
3076 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl());
3078 QObject *object = component.create();
3080 QVERIFY(object != 0);
3081 QVERIFY(source.value != 0);
3086 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3087 QCoreApplication::processEvents();
3089 QVERIFY(source.value != 0);
3093 void tst_qqmlecmascript::ownershipCustomReturnValue()
3095 CppOwnershipReturnValue source;
3099 engine.rootContext()->setContextProperty("source", &source);
3101 QVERIFY(source.value == 0);
3103 QQmlComponent component(&engine);
3104 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl());
3106 QObject *object = component.create();
3108 QVERIFY(object != 0);
3109 QVERIFY(source.value != 0);
3114 engine.collectGarbage();
3115 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3116 QCoreApplication::processEvents();
3118 QVERIFY(source.value == 0);
3121 //the return value from getObject will be JS ownership,
3122 //unless strong Cpp ownership has been set
3123 class OwnershipChangingObject : public QObject
3127 OwnershipChangingObject(): object(0) { }
3129 QPointer<QObject> object;
3132 QObject *getObject() { return object; }
3133 void setObject(QObject *obj) { object = obj; }
3136 void tst_qqmlecmascript::ownershipRootObject()
3138 OwnershipChangingObject own;
3139 QQmlContext *context = new QQmlContext(engine.rootContext());
3140 context->setContextObject(&own);
3142 QQmlComponent component(&engine, testFileUrl("ownershipRootObject.qml"));
3143 QQmlGuard<QObject> object = component.create(context);
3146 engine.collectGarbage();
3148 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3149 QCoreApplication::processEvents();
3151 QVERIFY(own.object != 0);
3157 void tst_qqmlecmascript::ownershipConsistency()
3159 OwnershipChangingObject own;
3160 QQmlContext *context = new QQmlContext(engine.rootContext());
3161 context->setContextObject(&own);
3163 QString expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":19: Error: Invalid attempt to destroy() an indestructible object");
3164 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3165 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":15: Error: Invalid attempt to destroy() an indestructible object");
3166 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3167 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":6: Error: Invalid attempt to destroy() an indestructible object");
3168 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3169 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":10: Error: Invalid attempt to destroy() an indestructible object");
3170 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3172 QQmlComponent component(&engine, testFileUrl("ownershipConsistency.qml"));
3173 QQmlGuard<QObject> object = component.create(context);
3176 engine.collectGarbage();
3178 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3179 QCoreApplication::processEvents();
3181 QVERIFY(own.object != 0);
3187 void tst_qqmlecmascript::ownershipQmlIncubated()
3189 QQmlComponent component(&engine, testFileUrl("ownershipQmlIncubated.qml"));
3190 QObject *object = component.create();
3193 QTRY_VERIFY(object->property("incubatedItem").value<QObject*>() != 0);
3195 QMetaObject::invokeMethod(object, "deleteIncubatedItem");
3197 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3198 QCoreApplication::processEvents();
3200 QVERIFY(object->property("incubatedItem").value<QObject*>() == 0);
3205 class QListQObjectMethodsObject : public QObject
3209 QListQObjectMethodsObject() {
3210 m_objects.append(new MyQmlObject());
3211 m_objects.append(new MyQmlObject());
3214 ~QListQObjectMethodsObject() {
3215 qDeleteAll(m_objects);
3219 QList<QObject *> getObjects() { return m_objects; }
3222 QList<QObject *> m_objects;
3225 // Tests that returning a QList<QObject*> from a method works
3226 void tst_qqmlecmascript::qlistqobjectMethods()
3228 QListQObjectMethodsObject obj;
3229 QQmlContext *context = new QQmlContext(engine.rootContext());
3230 context->setContextObject(&obj);
3232 QQmlComponent component(&engine, testFileUrl("qlistqobjectMethods.qml"));
3234 QObject *object = component.create(context);
3236 QCOMPARE(object->property("test").toInt(), 2);
3237 QCOMPARE(object->property("test2").toBool(), true);
3244 void tst_qqmlecmascript::strictlyEquals()
3246 QQmlComponent component(&engine, testFileUrl("strictlyEquals.qml"));
3248 QObject *object = component.create();
3249 QVERIFY(object != 0);
3251 QCOMPARE(object->property("test1").toBool(), true);
3252 QCOMPARE(object->property("test2").toBool(), true);
3253 QCOMPARE(object->property("test3").toBool(), true);
3254 QCOMPARE(object->property("test4").toBool(), true);
3255 QCOMPARE(object->property("test5").toBool(), true);
3256 QCOMPARE(object->property("test6").toBool(), true);
3257 QCOMPARE(object->property("test7").toBool(), true);
3258 QCOMPARE(object->property("test8").toBool(), true);
3263 void tst_qqmlecmascript::compiled()
3265 QQmlComponent component(&engine, testFileUrl("compiled.qml"));
3267 QObject *object = component.create();
3268 QVERIFY(object != 0);
3270 QCOMPARE(object->property("test1").toReal(), qreal(15.7));
3271 QCOMPARE(object->property("test2").toReal(), qreal(-6.7));
3272 QCOMPARE(object->property("test3").toBool(), true);
3273 QCOMPARE(object->property("test4").toBool(), false);
3274 QCOMPARE(object->property("test5").toBool(), false);
3275 QCOMPARE(object->property("test6").toBool(), true);
3277 QCOMPARE(object->property("test7").toInt(), 185);
3278 QCOMPARE(object->property("test8").toInt(), 167);
3279 QCOMPARE(object->property("test9").toBool(), true);
3280 QCOMPARE(object->property("test10").toBool(), false);
3281 QCOMPARE(object->property("test11").toBool(), false);
3282 QCOMPARE(object->property("test12").toBool(), true);
3284 QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld"));
3285 QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World"));
3286 QCOMPARE(object->property("test15").toBool(), false);
3287 QCOMPARE(object->property("test16").toBool(), true);
3289 QCOMPARE(object->property("test17").toInt(), 5);
3290 QCOMPARE(object->property("test18").toReal(), qreal(176));
3291 QCOMPARE(object->property("test19").toInt(), 7);
3292 QCOMPARE(object->property("test20").toReal(), qreal(6.7));
3293 QCOMPARE(object->property("test21").toString(), QLatin1String("6.7"));
3294 QCOMPARE(object->property("test22").toString(), QLatin1String("!"));
3295 QCOMPARE(object->property("test23").toBool(), true);
3296 QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33));
3297 QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA));
3302 // Test that numbers assigned in bindings as strings work consistently
3303 void tst_qqmlecmascript::numberAssignment()
3305 QQmlComponent component(&engine, testFileUrl("numberAssignment.qml"));
3307 QObject *object = component.create();
3308 QVERIFY(object != 0);
3310 QCOMPARE(object->property("test1"), QVariant((qreal)6.7));
3311 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3312 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3313 QCOMPARE(object->property("test3"), QVariant((qreal)6));
3314 QCOMPARE(object->property("test4"), QVariant((qreal)6));
3316 QCOMPARE(object->property("test5"), QVariant((int)7));
3317 QCOMPARE(object->property("test6"), QVariant((int)7));
3318 QCOMPARE(object->property("test7"), QVariant((int)6));
3319 QCOMPARE(object->property("test8"), QVariant((int)6));
3321 QCOMPARE(object->property("test9"), QVariant((unsigned int)7));
3322 QCOMPARE(object->property("test10"), QVariant((unsigned int)7));
3323 QCOMPARE(object->property("test11"), QVariant((unsigned int)6));
3324 QCOMPARE(object->property("test12"), QVariant((unsigned int)6));
3329 void tst_qqmlecmascript::propertySplicing()
3331 QQmlComponent component(&engine, testFileUrl("propertySplicing.qml"));
3333 QObject *object = component.create();
3334 QVERIFY(object != 0);
3336 QCOMPARE(object->property("test").toBool(), true);
3342 void tst_qqmlecmascript::signalWithUnknownTypes()
3344 QQmlComponent component(&engine, testFileUrl("signalWithUnknownTypes.qml"));
3346 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3347 QVERIFY(object != 0);
3349 MyQmlObject::MyType type;
3350 type.value = 0x8971123;
3351 emit object->signalWithUnknownType(type);
3353 MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant());
3355 QCOMPARE(result.value, type.value);
3361 void tst_qqmlecmascript::signalWithJSValueInVariant_data()
3363 QTest::addColumn<QString>("expression");
3364 QTest::addColumn<QString>("compare");
3366 QString compareStrict("(function(a, b) { return a === b; })");
3367 QTest::newRow("true") << "true" << compareStrict;
3368 QTest::newRow("undefined") << "undefined" << compareStrict;
3369 QTest::newRow("null") << "null" << compareStrict;
3370 QTest::newRow("123") << "123" << compareStrict;
3371 QTest::newRow("'ciao'") << "'ciao'" << compareStrict;
3373 QString comparePropertiesStrict(
3375 " if (typeof b != 'object')"
3377 " var props = Object.getOwnPropertyNames(b);"
3378 " for (var i = 0; i < props.length; ++i) {"
3379 " var p = props[i];"
3380 " return arguments.callee(a[p], b[p]);"
3383 QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })" << comparePropertiesStrict;
3384 QTest::newRow("[10,20,30]") << "[10,20,30]" << comparePropertiesStrict;
3387 void tst_qqmlecmascript::signalWithJSValueInVariant()
3389 QFETCH(QString, expression);
3390 QFETCH(QString, compare);
3392 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3393 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3394 QVERIFY(object != 0);
3396 QJSValue value = engine.evaluate(expression);
3397 QVERIFY(!value.isError());
3398 object->setProperty("expression", expression);
3399 object->setProperty("compare", compare);
3400 object->setProperty("pass", false);
3402 emit object->signalWithVariant(QVariant::fromValue(value));
3403 QVERIFY(object->property("pass").toBool());
3406 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines_data()
3408 signalWithJSValueInVariant_data();
3411 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines()
3413 QFETCH(QString, expression);
3414 QFETCH(QString, compare);
3416 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3417 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3418 QVERIFY(object != 0);
3421 QJSValue value = engine2.evaluate(expression);
3422 QVERIFY(!value.isError());
3423 object->setProperty("expression", expression);
3424 object->setProperty("compare", compare);
3425 object->setProperty("pass", false);
3427 QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine.");
3428 emit object->signalWithVariant(QVariant::fromValue(value));
3429 QVERIFY(!object->property("pass").toBool());
3432 void tst_qqmlecmascript::signalWithQJSValue_data()
3434 signalWithJSValueInVariant_data();
3437 void tst_qqmlecmascript::signalWithQJSValue()
3439 QFETCH(QString, expression);
3440 QFETCH(QString, compare);
3442 QQmlComponent component(&engine, testFileUrl("signalWithQJSValue.qml"));
3443 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3444 QVERIFY(object != 0);
3446 QJSValue value = engine.evaluate(expression);
3447 QVERIFY(!value.isError());
3448 object->setProperty("expression", expression);
3449 object->setProperty("compare", compare);
3450 object->setProperty("pass", false);
3452 emit object->signalWithQJSValue(value);
3454 QVERIFY(object->property("pass").toBool());
3455 QVERIFY(object->qjsvalue().strictlyEquals(value));
3458 void tst_qqmlecmascript::moduleApi_data()
3460 QTest::addColumn<QUrl>("testfile");
3461 QTest::addColumn<QString>("errorMessage");
3462 QTest::addColumn<QStringList>("warningMessages");
3463 QTest::addColumn<QStringList>("readProperties");
3464 QTest::addColumn<QVariantList>("readExpectedValues");
3465 QTest::addColumn<QStringList>("writeProperties");
3466 QTest::addColumn<QVariantList>("writeValues");
3467 QTest::addColumn<QStringList>("readBackProperties");
3468 QTest::addColumn<QVariantList>("readBackExpectedValues");
3470 QTest::newRow("qobject, register + read + method")
3471 << testFileUrl("moduleapi/qobjectModuleApi.qml")
3474 << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
3475 << "qobjectMinorVersionTest" << "qobjectMajorVersionTest" << "qobjectParentedTest")
3476 << (QVariantList() << 20 << 20 << 1 << 20 << 20 << 26)
3482 QTest::newRow("script, register + read")
3483 << testFileUrl("moduleapi/scriptModuleApi.qml")
3486 << (QStringList() << "scriptTest")
3487 << (QVariantList() << 13)
3493 QTest::newRow("qobject, caching + read")
3494 << testFileUrl("moduleapi/qobjectModuleApiCaching.qml")
3497 << (QStringList() << "existingUriTest" << "qobjectParentedTest")
3498 << (QVariantList() << 20 << 26) // 26, shouldn't have incremented to 27.
3504 QTest::newRow("script, caching + read")
3505 << testFileUrl("moduleapi/scriptModuleApiCaching.qml")
3508 << (QStringList() << "scriptTest")
3509 << (QVariantList() << 13) // 13, shouldn't have incremented to 14.
3515 QTest::newRow("qobject, writing + readonly constraints")
3516 << testFileUrl("moduleapi/qobjectModuleApiWriting.qml")
3518 << (QStringList() << QString(testFileUrl("moduleapi/qobjectModuleApiWriting.qml").toString() + QLatin1String(":15: Error: Cannot assign to read-only property \"qobjectTestProperty\"")))
3519 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3520 << (QVariantList() << 20 << 50 << 10)
3521 << (QStringList() << "firstProperty" << "secondProperty")
3522 << (QVariantList() << 30 << 30)
3523 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3524 << (QVariantList() << 20 << 30 << 30);
3526 QTest::newRow("script, writing + readonly constraints")
3527 << testFileUrl("moduleapi/scriptModuleApiWriting.qml")
3529 << (QStringList() << QString(testFileUrl("moduleapi/scriptModuleApiWriting.qml").toString() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\"")))
3530 << (QStringList() << "readBack" << "unchanged")
3531 << (QVariantList() << 13 << 42)
3532 << (QStringList() << "firstProperty" << "secondProperty")
3533 << (QVariantList() << 30 << 30)
3534 << (QStringList() << "readBack" << "unchanged")
3535 << (QVariantList() << 30 << 42);
3537 QTest::newRow("qobject module API enum values in JS")
3538 << testFileUrl("moduleapi/qobjectModuleApiEnums.qml")
3541 << (QStringList() << "enumValue" << "enumMethod")
3542 << (QVariantList() << 42 << 30)
3548 QTest::newRow("qobject, invalid major version fail")
3549 << testFileUrl("moduleapi/moduleApiMajorVersionFail.qml")
3550 << QString("QQmlComponent: Component is not ready")
3559 QTest::newRow("qobject, invalid minor version fail")
3560 << testFileUrl("moduleapi/moduleApiMinorVersionFail.qml")
3561 << QString("QQmlComponent: Component is not ready")
3570 QTest::newRow("legacy module api registration")
3571 << testFileUrl("moduleapi/qobjectModuleApiLegacy.qml")
3573 << QStringList() // warning doesn't occur in the test, but in registerTypes()
3574 << (QStringList() << "legacyModulePropertyTest" << "legacyModuleMethodTest")
3575 << (QVariantList() << 20 << 2)
3582 void tst_qqmlecmascript::moduleApi()
3584 QFETCH(QUrl, testfile);
3585 QFETCH(QString, errorMessage);
3586 QFETCH(QStringList, warningMessages);
3587 QFETCH(QStringList, readProperties);
3588 QFETCH(QVariantList, readExpectedValues);
3589 QFETCH(QStringList, writeProperties);
3590 QFETCH(QVariantList, writeValues);
3591 QFETCH(QStringList, readBackProperties);
3592 QFETCH(QVariantList, readBackExpectedValues);
3594 QQmlComponent component(&engine, testfile);
3596 if (!errorMessage.isEmpty())
3597 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3599 if (warningMessages.size())
3600 foreach (const QString &warning, warningMessages)
3601 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3603 QObject *object = component.create();
3604 if (!errorMessage.isEmpty()) {
3605 QVERIFY(object == 0);
3607 QVERIFY(object != 0);
3608 for (int i = 0; i < readProperties.size(); ++i)
3609 QCOMPARE(object->property(readProperties.at(i).toLatin1().constData()), readExpectedValues.at(i));
3610 for (int i = 0; i < writeProperties.size(); ++i)
3611 QVERIFY(object->setProperty(writeProperties.at(i).toLatin1().constData(), writeValues.at(i)));
3612 for (int i = 0; i < readBackProperties.size(); ++i)
3613 QCOMPARE(object->property(readBackProperties.at(i).toLatin1().constData()), readBackExpectedValues.at(i));
3618 void tst_qqmlecmascript::importScripts_data()
3620 QTest::addColumn<QUrl>("testfile");
3621 QTest::addColumn<QString>("errorMessage");
3622 QTest::addColumn<QStringList>("warningMessages");
3623 QTest::addColumn<QStringList>("propertyNames");
3624 QTest::addColumn<QVariantList>("propertyValues");
3626 QTest::newRow("basic functionality")
3627 << testFileUrl("jsimport/testImport.qml")
3630 << (QStringList() << QLatin1String("importedScriptStringValue")
3631 << QLatin1String("importedScriptFunctionValue")
3632 << QLatin1String("importedModuleAttachedPropertyValue")
3633 << QLatin1String("importedModuleEnumValue"))
3634 << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3639 QTest::newRow("import scoping")
3640 << testFileUrl("jsimport/testImportScoping.qml")
3643 << (QStringList() << QLatin1String("componentError"))
3644 << (QVariantList() << QVariant(5));
3646 QTest::newRow("parent scope shouldn't be inherited by import with imports")
3647 << testFileUrl("jsimportfail/failOne.qml")
3649 << (QStringList() << QString(testFileUrl("jsimportfail/failOne.qml").toString() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
3650 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3651 << (QVariantList() << QVariant(QString()));
3653 QTest::newRow("javascript imports in an import should be private to the import scope")
3654 << testFileUrl("jsimportfail/failTwo.qml")
3656 << (QStringList() << QString(testFileUrl("jsimportfail/failTwo.qml").toString() + QLatin1String(":6: ReferenceError: ImportOneJs is not defined")))
3657 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3658 << (QVariantList() << QVariant(QString()));
3660 QTest::newRow("module imports in an import should be private to the import scope")
3661 << testFileUrl("jsimportfail/failThree.qml")
3663 << (QStringList() << QString(testFileUrl("jsimportfail/failThree.qml").toString() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
3664 << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
3665 << (QVariantList() << QVariant(false));
3667 QTest::newRow("typenames in an import should be private to the import scope")
3668 << testFileUrl("jsimportfail/failFour.qml")
3670 << (QStringList() << QString(testFileUrl("jsimportfail/failFour.qml").toString() + QLatin1String(":6: ReferenceError: JsQtTest is not defined")))
3671 << (QStringList() << QLatin1String("importedModuleEnumValue"))
3672 << (QVariantList() << QVariant(0));
3674 QTest::newRow("import with imports has it's own activation scope")
3675 << testFileUrl("jsimportfail/failFive.qml")
3677 << (QStringList() << QString(testFileUrl("jsimportfail/importWithImports.js").toString() + QLatin1String(":8: ReferenceError: Component is not defined")))
3678 << (QStringList() << QLatin1String("componentError"))
3679 << (QVariantList() << QVariant(0));
3681 QTest::newRow("import pragma library script")
3682 << testFileUrl("jsimport/testImportPragmaLibrary.qml")
3685 << (QStringList() << QLatin1String("testValue"))
3686 << (QVariantList() << QVariant(31));
3688 QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3689 << testFileUrl("jsimportfail/testImportPragmaLibrary.qml")
3691 << (QStringList() << QString(testFileUrl("jsimportfail/importPragmaLibrary.js").toString() + QLatin1String(":6: ReferenceError: Component is not defined")))
3692 << (QStringList() << QLatin1String("testValue"))
3693 << (QVariantList() << QVariant(0));
3695 QTest::newRow("import pragma library script which has an import")
3696 << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
3699 << (QStringList() << QLatin1String("testValue"))
3700 << (QVariantList() << QVariant(55));
3702 QTest::newRow("import pragma library script which has a pragma library import")
3703 << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3706 << (QStringList() << QLatin1String("testValue"))
3707 << (QVariantList() << QVariant(18));
3709 QTest::newRow("import module api into js import")
3710 << testFileUrl("jsimport/testImportModuleApi.qml")
3713 << (QStringList() << QLatin1String("testValue"))
3714 << (QVariantList() << QVariant(20));
3716 QTest::newRow("import module which exports a script")
3717 << testFileUrl("jsimport/testJsImport.qml")
3720 << (QStringList() << QLatin1String("importedScriptStringValue")
3721 << QLatin1String("renamedScriptStringValue")
3722 << QLatin1String("reimportedScriptStringValue"))
3723 << (QVariantList() << QVariant(QString("Hello"))
3724 << QVariant(QString("Hello"))
3725 << QVariant(QString("Hello")));
3728 void tst_qqmlecmascript::importScripts()
3730 QFETCH(QUrl, testfile);
3731 QFETCH(QString, errorMessage);
3732 QFETCH(QStringList, warningMessages);
3733 QFETCH(QStringList, propertyNames);
3734 QFETCH(QVariantList, propertyValues);
3736 QQmlComponent component(&engine, testfile);
3738 if (!errorMessage.isEmpty())
3739 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3741 if (warningMessages.size())
3742 foreach (const QString &warning, warningMessages)
3743 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3745 QObject *object = component.create();
3746 if (!errorMessage.isEmpty()) {
3747 QVERIFY(object == 0);
3749 QVERIFY(object != 0);
3750 for (int i = 0; i < propertyNames.size(); ++i)
3751 QCOMPARE(object->property(propertyNames.at(i).toLatin1().constData()), propertyValues.at(i));
3756 void tst_qqmlecmascript::scarceResources_other()
3758 /* These tests require knowledge of state, since we test values after
3759 performing signal or function invocation. */
3761 QPixmap origPixmap(100, 100);
3762 origPixmap.fill(Qt::blue);
3763 QString srp_name, expectedWarning;
3764 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
3765 ScarceResourceObject *eo = 0;
3767 QObject *object = 0;
3769 /* property var semantics */
3771 // test that scarce resources are handled properly in signal invocation
3772 QQmlComponent varComponentTen(&engine, testFileUrl("scarceResourceSignal.var.qml"));
3773 object = varComponentTen.create();
3774 srsc = object->findChild<QObject*>("srsc");
3776 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3777 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3778 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3779 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3780 QMetaObject::invokeMethod(srsc, "testSignal");
3781 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3782 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3783 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3784 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3785 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3786 QVERIFY(srsc->property("scarceResourceCopy").isValid());
3787 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3788 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3789 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3790 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3793 // test that scarce resources are handled properly from js functions in qml files
3794 QQmlComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml"));
3795 object = varComponentEleven.create();
3796 QVERIFY(object != 0);
3797 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3798 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3799 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3800 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3801 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3802 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3803 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3804 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3805 QMetaObject::invokeMethod(object, "releaseScarceResource");
3806 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3807 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3808 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3809 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3812 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3813 QQmlComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
3814 object = varComponentTwelve.create();
3815 QVERIFY(object != 0);
3816 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3817 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3818 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3819 srp_name = object->property("srp_name").toString();
3820 expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3821 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3822 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3823 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
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 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3829 // test that if an Item which has JS ownership but has a scarce resource property is garbage collected,
3830 // that the scarce resource is removed from the engine's list of scarce resources to clean up.
3831 QQmlComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml"));
3832 object = varComponentThirteen.create();
3833 QVERIFY(object != 0);
3834 QVERIFY(!object->property("varProperty").isValid()); // not assigned yet
3835 QMetaObject::invokeMethod(object, "assignVarProperty");
3836 QVERIFY(ep->scarceResources.isEmpty()); // the scarce resource is a VME property.
3837 QMetaObject::invokeMethod(object, "deassignVarProperty");
3838 QVERIFY(ep->scarceResources.isEmpty()); // should still be empty; the resource should have been released on gc.
3841 /* property variant semantics */
3843 // test that scarce resources are handled properly in signal invocation
3844 QQmlComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml"));
3845 object = variantComponentTen.create();
3846 QVERIFY(object != 0);
3847 srsc = object->findChild<QObject*>("srsc");
3849 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3850 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3851 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3852 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3853 QMetaObject::invokeMethod(srsc, "testSignal");
3854 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3855 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3856 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3857 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3858 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3859 QVERIFY(srsc->property("scarceResourceCopy").isValid());
3860 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3861 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3862 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3863 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3866 // test that scarce resources are handled properly from js functions in qml files
3867 QQmlComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml"));
3868 object = variantComponentEleven.create();
3869 QVERIFY(object != 0);
3870 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3871 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3872 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3873 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3874 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3875 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3876 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3877 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3878 QMetaObject::invokeMethod(object, "releaseScarceResource");
3879 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3880 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3881 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3882 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3885 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3886 QQmlComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml"));
3887 object = variantComponentTwelve.create();
3888 QVERIFY(object != 0);
3889 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3890 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3891 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3892 srp_name = object->property("srp_name").toString();
3893 expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3894 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3895 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3896 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
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 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3903 void tst_qqmlecmascript::scarceResources_data()
3905 QTest::addColumn<QUrl>("qmlFile");
3906 QTest::addColumn<bool>("readDetachStatus");
3907 QTest::addColumn<bool>("expectedDetachStatus");
3908 QTest::addColumn<QStringList>("propertyNames");
3909 QTest::addColumn<QVariantList>("expectedValidity");
3910 QTest::addColumn<QVariantList>("expectedValues");
3911 QTest::addColumn<QStringList>("expectedErrors");
3913 QPixmap origPixmap(100, 100);
3914 origPixmap.fill(Qt::blue);
3916 /* property var semantics */
3918 // in the following three cases, the instance created from the component
3919 // has a property which is a copy of the scarce resource; hence, the
3920 // resource should NOT be detached prior to deletion of the object instance,
3921 // unless the resource is destroyed explicitly.
3922 QTest::newRow("var: import scarce resource copy directly")
3923 << testFileUrl("scarceResourceCopy.var.qml")
3925 << false // won't be detached, because assigned to property and not explicitly released
3926 << (QStringList() << QLatin1String("scarceResourceCopy"))
3927 << (QList<QVariant>() << true)
3928 << (QList<QVariant>() << origPixmap)
3931 QTest::newRow("var: import scarce resource copy from JS")
3932 << testFileUrl("scarceResourceCopyFromJs.var.qml")
3934 << false // won't be detached, because assigned to property and not explicitly released
3935 << (QStringList() << QLatin1String("scarceResourceCopy"))
3936 << (QList<QVariant>() << true)
3937 << (QList<QVariant>() << origPixmap)
3940 QTest::newRow("var: import released scarce resource copy from JS")
3941 << testFileUrl("scarceResourceDestroyedCopy.var.qml")
3943 << true // explicitly released, so it will be detached
3944 << (QStringList() << QLatin1String("scarceResourceCopy"))
3945 << (QList<QVariant>() << false)
3946 << (QList<QVariant>() << QVariant())
3949 // in the following three cases, no other copy should exist in memory,
3950 // and so it should be detached (unless explicitly preserved).
3951 QTest::newRow("var: import auto-release SR from JS in binding side-effect")
3952 << testFileUrl("scarceResourceTest.var.qml")
3954 << true // auto released, so it will be detached
3955 << (QStringList() << QLatin1String("scarceResourceTest"))
3956 << (QList<QVariant>() << true)
3957 << (QList<QVariant>() << QVariant(100))
3959 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
3960 << testFileUrl("scarceResourceTestPreserve.var.qml")
3962 << false // won't be detached because we explicitly preserve it
3963 << (QStringList() << QLatin1String("scarceResourceTest"))
3964 << (QList<QVariant>() << true)
3965 << (QList<QVariant>() << QVariant(100))
3967 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
3968 << testFileUrl("scarceResourceTestMultiple.var.qml")
3970 << true // will be detached because all resources were released manually or automatically.
3971 << (QStringList() << QLatin1String("scarceResourceTest"))
3972 << (QList<QVariant>() << true)
3973 << (QList<QVariant>() << QVariant(100))
3976 // In the following three cases, test that scarce resources are handled
3977 // correctly for imports.
3978 QTest::newRow("var: import with no binding")
3979 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
3980 << false // cannot check detach status.
3983 << QList<QVariant>()
3984 << QList<QVariant>()
3986 QTest::newRow("var: import with binding without explicit preserve")
3987 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
3990 << (QStringList() << QLatin1String("scarceResourceCopy"))
3991 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
3992 << (QList<QVariant>() << QVariant())
3994 QTest::newRow("var: import with explicit release after binding evaluation")
3995 << testFileUrl("scarceResourceCopyImport.var.qml")
3998 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
3999 << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated.
4000 << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true))
4002 QTest::newRow("var: import with different js objects")
4003 << testFileUrl("scarceResourceCopyImportDifferent.var.qml")
4006 << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4007 << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4008 << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false))
4010 QTest::newRow("var: import with different js objects and explicit release")
4011 << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml")
4014 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4015 << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4016 << (QList<QVariant>() << QVariant(origPixmap) << QVariant())
4018 QTest::newRow("var: import with same js objects and explicit release")
4019 << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml")
4022 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4023 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4024 << (QList<QVariant>() << QVariant() << QVariant())
4026 QTest::newRow("var: binding with same js objects and explicit release")
4027 << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml")
4030 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4031 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4032 << (QList<QVariant>() << QVariant() << QVariant())
4036 /* property variant semantics */
4038 // in the following three cases, the instance created from the component
4039 // has a property which is a copy of the scarce resource; hence, the
4040 // resource should NOT be detached prior to deletion of the object instance,
4041 // unless the resource is destroyed explicitly.
4042 QTest::newRow("variant: import scarce resource copy directly")
4043 << testFileUrl("scarceResourceCopy.variant.qml")
4045 << false // won't be detached, because assigned to property and not explicitly released
4046 << (QStringList() << QLatin1String("scarceResourceCopy"))
4047 << (QList<QVariant>() << true)
4048 << (QList<QVariant>() << origPixmap)
4051 QTest::newRow("variant: import scarce resource copy from JS")
4052 << testFileUrl("scarceResourceCopyFromJs.variant.qml")
4054 << false // won't be detached, because assigned to property and not explicitly released
4055 << (QStringList() << QLatin1String("scarceResourceCopy"))
4056 << (QList<QVariant>() << true)
4057 << (QList<QVariant>() << origPixmap)
4060 QTest::newRow("variant: import released scarce resource copy from JS")
4061 << testFileUrl("scarceResourceDestroyedCopy.variant.qml")
4063 << true // explicitly released, so it will be detached
4064 << (QStringList() << QLatin1String("scarceResourceCopy"))
4065 << (QList<QVariant>() << false)
4066 << (QList<QVariant>() << QVariant())
4069 // in the following three cases, no other copy should exist in memory,
4070 // and so it should be detached (unless explicitly preserved).
4071 QTest::newRow("variant: import auto-release SR from JS in binding side-effect")
4072 << testFileUrl("scarceResourceTest.variant.qml")
4074 << true // auto released, so it will be detached
4075 << (QStringList() << QLatin1String("scarceResourceTest"))
4076 << (QList<QVariant>() << true)
4077 << (QList<QVariant>() << QVariant(100))
4079 QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect")
4080 << testFileUrl("scarceResourceTestPreserve.variant.qml")
4082 << false // won't be detached because we explicitly preserve it
4083 << (QStringList() << QLatin1String("scarceResourceTest"))
4084 << (QList<QVariant>() << true)
4085 << (QList<QVariant>() << QVariant(100))
4087 QTest::newRow("variant: import multiple scarce resources")
4088 << testFileUrl("scarceResourceTestMultiple.variant.qml")
4090 << true // will be detached because all resources were released manually or automatically.
4091 << (QStringList() << QLatin1String("scarceResourceTest"))
4092 << (QList<QVariant>() << true)
4093 << (QList<QVariant>() << QVariant(100))
4096 // In the following three cases, test that scarce resources are handled
4097 // correctly for imports.
4098 QTest::newRow("variant: import with no binding")
4099 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4100 << false // cannot check detach status.
4103 << QList<QVariant>()
4104 << QList<QVariant>()
4106 QTest::newRow("variant: import with binding without explicit preserve")
4107 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4110 << (QStringList() << QLatin1String("scarceResourceCopy"))
4111 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4112 << (QList<QVariant>() << QVariant())
4114 QTest::newRow("variant: import with explicit release after binding evaluation")
4115 << testFileUrl("scarceResourceCopyImport.variant.qml")
4118 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo"))
4119 << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies.
4120 << (QList<QVariant>() << origPixmap << origPixmap << QVariant())
4124 void tst_qqmlecmascript::scarceResources()
4126 QFETCH(QUrl, qmlFile);
4127 QFETCH(bool, readDetachStatus);
4128 QFETCH(bool, expectedDetachStatus);
4129 QFETCH(QStringList, propertyNames);
4130 QFETCH(QVariantList, expectedValidity);
4131 QFETCH(QVariantList, expectedValues);
4132 QFETCH(QStringList, expectedErrors);
4134 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4135 ScarceResourceObject *eo = 0;
4136 QObject *object = 0;
4138 QQmlComponent c(&engine, qmlFile);
4139 object = c.create();
4140 QVERIFY(object != 0);
4141 for (int i = 0; i < propertyNames.size(); ++i) {
4142 QString prop = propertyNames.at(i);
4143 bool validity = expectedValidity.at(i).toBool();
4144 QVariant value = expectedValues.at(i);
4146 QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity);
4147 if (value.type() == QVariant::Int) {
4148 QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt());
4149 } else if (value.type() == QVariant::Pixmap) {
4150 QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>());
4154 if (readDetachStatus) {
4155 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4156 QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
4159 QVERIFY(ep->scarceResources.isEmpty());
4163 void tst_qqmlecmascript::propertyChangeSlots()
4165 // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
4166 QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml"));
4167 QObject *object = component.create();
4168 QVERIFY(object != 0);
4171 // ensure that invalid property names fail properly.
4172 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4173 QQmlComponent e1(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.1.qml"));
4174 QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
4175 QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
4176 object = e1.create();
4177 QVERIFY(object == 0);
4180 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4181 QQmlComponent e2(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.2.qml"));
4182 expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
4183 QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
4184 object = e2.create();
4185 QVERIFY(object == 0);
4188 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4189 QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml"));
4190 expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
4191 QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
4192 object = e3.create();
4193 QVERIFY(object == 0);
4196 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4197 QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml"));
4198 expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
4199 QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
4200 object = e4.create();
4201 QVERIFY(object == 0);
4205 void tst_qqmlecmascript::propertyVar_data()
4207 QTest::addColumn<QUrl>("qmlFile");
4210 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyVar.1.qml");
4211 QTest::newRow("non-bindable object changed") << testFileUrl("propertyVar.2.qml");
4212 QTest::newRow("primitive changed") << testFileUrl("propertyVar.3.qml");
4213 QTest::newRow("javascript array modification") << testFileUrl("propertyVar.4.qml");
4214 QTest::newRow("javascript map modification") << testFileUrl("propertyVar.5.qml");
4215 QTest::newRow("javascript array assignment") << testFileUrl("propertyVar.6.qml");
4216 QTest::newRow("javascript map assignment") << testFileUrl("propertyVar.7.qml");
4217 QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml");
4218 QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml");
4219 QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml");
4220 QTest::newRow("javascript function assignment") << testFileUrl("propertyVar.11.qml");
4221 QTest::newRow("javascript special assignment") << testFileUrl("propertyVar.12.qml");
4222 QTest::newRow("declarative binding assignment") << testFileUrl("propertyVar.13.qml");
4223 QTest::newRow("imperative binding assignment") << testFileUrl("propertyVar.14.qml");
4224 QTest::newRow("stored binding assignment") << testFileUrl("propertyVar.15.qml");
4227 void tst_qqmlecmascript::propertyVar()
4229 QFETCH(QUrl, qmlFile);
4231 QQmlComponent component(&engine, qmlFile);
4232 QObject *object = component.create();
4233 QVERIFY(object != 0);
4235 QCOMPARE(object->property("test").toBool(), true);
4240 // Tests that we can write QVariant values to var properties from C++
4241 void tst_qqmlecmascript::propertyVarCpp()
4243 QObject *object = 0;
4245 // ensure that writing to and reading from a var property from cpp works as required.
4246 // Literal values stored in var properties can be read and written as QVariants
4247 // of a specific type, whereas object values are read as QVariantMaps.
4248 QQmlComponent component(&engine, testFileUrl("propertyVarCpp.qml"));
4249 object = component.create();
4250 QVERIFY(object != 0);
4251 // assign int to property var that currently has int assigned
4252 QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
4253 QCOMPARE(object->property("varBound"), QVariant(15));
4254 QCOMPARE(object->property("intBound"), QVariant(15));
4255 QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
4256 QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
4257 // assign string to property var that current has bool assigned
4258 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
4259 QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
4260 QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
4261 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
4262 // now enforce behaviour when accessing JavaScript objects from cpp.
4263 QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
4267 static void gc(QQmlEngine &engine)
4269 engine.collectGarbage();
4270 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4271 QCoreApplication::processEvents();
4274 void tst_qqmlecmascript::propertyVarOwnership()
4276 // Referenced JS objects are not collected
4278 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.qml"));
4279 QObject *object = component.create();
4280 QVERIFY(object != 0);
4281 QCOMPARE(object->property("test").toBool(), false);
4282 QMetaObject::invokeMethod(object, "runTest");
4283 QCOMPARE(object->property("test").toBool(), true);
4286 // Referenced JS objects are not collected
4288 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml"));
4289 QObject *object = component.create();
4290 QVERIFY(object != 0);
4291 QCOMPARE(object->property("test").toBool(), false);
4292 QMetaObject::invokeMethod(object, "runTest");
4293 QCOMPARE(object->property("test").toBool(), true);
4296 // Qt objects are not collected until they've been dereferenced
4298 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml"));
4299 QObject *object = component.create();
4300 QVERIFY(object != 0);
4302 QCOMPARE(object->property("test2").toBool(), false);
4303 QCOMPARE(object->property("test2").toBool(), false);
4305 QMetaObject::invokeMethod(object, "runTest");
4306 QCOMPARE(object->property("test1").toBool(), true);
4308 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4309 QVERIFY(!referencedObject.isNull());
4311 QVERIFY(!referencedObject.isNull());
4313 QMetaObject::invokeMethod(object, "runTest2");
4314 QCOMPARE(object->property("test2").toBool(), true);
4316 QVERIFY(referencedObject.isNull());
4320 // Self reference does not prevent Qt object collection
4322 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml"));
4323 QObject *object = component.create();
4324 QVERIFY(object != 0);
4326 QCOMPARE(object->property("test").toBool(), true);
4328 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4329 QVERIFY(!referencedObject.isNull());
4331 QVERIFY(!referencedObject.isNull());
4333 QMetaObject::invokeMethod(object, "runTest");
4335 QVERIFY(referencedObject.isNull());
4339 // Garbage collection cannot result in attempted dereference of empty handle
4341 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.5.qml"));
4342 QObject *object = component.create();
4343 QVERIFY(object != 0);
4344 QMetaObject::invokeMethod(object, "runTest");
4345 QCOMPARE(object->property("test").toBool(), true);
4350 void tst_qqmlecmascript::propertyVarImplicitOwnership()
4352 // The childObject has a reference to a different QObject. We want to ensure
4353 // that the different item will not be cleaned up until required. IE, the childObject
4354 // has implicit ownership of the constructed QObject.
4355 QQmlComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml"));
4356 QObject *object = component.create();
4357 QVERIFY(object != 0);
4358 QMetaObject::invokeMethod(object, "assignCircular");
4359 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4360 QCoreApplication::processEvents();
4361 QObject *rootObject = object->property("vp").value<QObject*>();
4362 QVERIFY(rootObject != 0);
4363 QObject *childObject = rootObject->findChild<QObject*>("text");
4364 QVERIFY(childObject != 0);
4365 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4366 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4367 QMetaObject::invokeMethod(childObject, "constructQObject"); // creates a reference to a constructed QObject.
4368 QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
4369 QVERIFY(!qobjectGuard.isNull());
4370 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4371 QCoreApplication::processEvents();
4372 QVERIFY(!qobjectGuard.isNull());
4373 QMetaObject::invokeMethod(object, "deassignCircular");
4374 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4375 QCoreApplication::processEvents();
4376 QVERIFY(qobjectGuard.isNull()); // should have been collected now.
4380 void tst_qqmlecmascript::propertyVarReparent()
4382 // ensure that nothing breaks if we re-parent objects
4383 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4384 QObject *object = component.create();
4385 QVERIFY(object != 0);
4386 QMetaObject::invokeMethod(object, "assignVarProp");
4387 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4388 QCoreApplication::processEvents();
4389 QObject *rect = object->property("vp").value<QObject*>();
4390 QObject *text = rect->findChild<QObject*>("textOne");
4391 QObject *text2 = rect->findChild<QObject*>("textTwo");
4392 QWeakPointer<QObject> rectGuard(rect);
4393 QWeakPointer<QObject> textGuard(text);
4394 QWeakPointer<QObject> text2Guard(text2);
4395 QVERIFY(!rectGuard.isNull());
4396 QVERIFY(!textGuard.isNull());
4397 QVERIFY(!text2Guard.isNull());
4398 QCOMPARE(text->property("textCanary").toInt(), 11);
4399 QCOMPARE(text2->property("textCanary").toInt(), 12);
4400 // now construct an image which we will reparent.
4401 QMetaObject::invokeMethod(text2, "constructQObject");
4402 QObject *image = text2->property("vp").value<QObject*>();
4403 QWeakPointer<QObject> imageGuard(image);
4404 QVERIFY(!imageGuard.isNull());
4405 QCOMPARE(image->property("imageCanary").toInt(), 13);
4406 // now reparent the "Image" object (currently, it has JS ownership)
4407 image->setParent(text); // shouldn't be collected after deassignVp now, since has a parent.
4408 QMetaObject::invokeMethod(text2, "deassignVp");
4409 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4410 QCoreApplication::processEvents();
4411 QCOMPARE(text->property("textCanary").toInt(), 11);
4412 QCOMPARE(text2->property("textCanary").toInt(), 22);
4413 QVERIFY(!imageGuard.isNull()); // should still be alive.
4414 QCOMPARE(image->property("imageCanary").toInt(), 13); // still able to access var properties
4415 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4416 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4417 QCoreApplication::processEvents();
4418 QVERIFY(imageGuard.isNull()); // should now have been deleted, due to parent being deleted.
4422 void tst_qqmlecmascript::propertyVarReparentNullContext()
4424 // sometimes reparenting can cause problems
4425 // (eg, if the ctxt is collected, varproperties are no longer available)
4426 // this test ensures that no crash occurs in that situation.
4427 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4428 QObject *object = component.create();
4429 QVERIFY(object != 0);
4430 QMetaObject::invokeMethod(object, "assignVarProp");
4431 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4432 QCoreApplication::processEvents();
4433 QObject *rect = object->property("vp").value<QObject*>();
4434 QObject *text = rect->findChild<QObject*>("textOne");
4435 QObject *text2 = rect->findChild<QObject*>("textTwo");
4436 QWeakPointer<QObject> rectGuard(rect);
4437 QWeakPointer<QObject> textGuard(text);
4438 QWeakPointer<QObject> text2Guard(text2);
4439 QVERIFY(!rectGuard.isNull());
4440 QVERIFY(!textGuard.isNull());
4441 QVERIFY(!text2Guard.isNull());
4442 QCOMPARE(text->property("textCanary").toInt(), 11);
4443 QCOMPARE(text2->property("textCanary").toInt(), 12);
4444 // now construct an image which we will reparent.
4445 QMetaObject::invokeMethod(text2, "constructQObject");
4446 QObject *image = text2->property("vp").value<QObject*>();
4447 QWeakPointer<QObject> imageGuard(image);
4448 QVERIFY(!imageGuard.isNull());
4449 QCOMPARE(image->property("imageCanary").toInt(), 13);
4450 // now reparent the "Image" object (currently, it has JS ownership)
4451 image->setParent(object); // reparented to base object. after deassignVarProp, the ctxt will be invalid.
4452 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4453 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4454 QCoreApplication::processEvents();
4455 QVERIFY(!imageGuard.isNull()); // should still be alive.
4456 QVERIFY(!image->property("imageCanary").isValid()); // but varProperties won't be available (null context).
4458 QVERIFY(imageGuard.isNull()); // should now be dead.
4461 void tst_qqmlecmascript::propertyVarCircular()
4463 // enforce behaviour regarding circular references - ensure qdvmemo deletion.
4464 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.qml"));
4465 QObject *object = component.create();
4466 QVERIFY(object != 0);
4467 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4468 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4469 QCoreApplication::processEvents();
4470 QCOMPARE(object->property("canaryInt"), QVariant(5));
4471 QVariant canaryResourceVariant = object->property("canaryResource");
4472 QVERIFY(canaryResourceVariant.isValid());
4473 QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
4474 canaryResourceVariant = QVariant(); // invalidate it to remove one copy of the pixmap from memory.
4475 QMetaObject::invokeMethod(object, "deassignCanaryResource"); // remove one copy of the pixmap from memory
4476 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4477 QCoreApplication::processEvents();
4478 QVERIFY(!canaryResourcePixmap.isDetached()); // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
4479 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4480 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4481 QCoreApplication::processEvents();
4482 QCOMPARE(object->property("canaryInt"), QVariant(2));
4483 QCOMPARE(object->property("canaryResource"), QVariant(1));
4484 QVERIFY(canaryResourcePixmap.isDetached()); // now detached, since orig copy was member of qdvmemo which was deleted.
4488 void tst_qqmlecmascript::propertyVarCircular2()
4490 // track deletion of JS-owned parent item with Cpp-owned child
4491 // where the child has a var property referencing its parent.
4492 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4493 QObject *object = component.create();
4494 QVERIFY(object != 0);
4495 QMetaObject::invokeMethod(object, "assignCircular");
4496 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4497 QCoreApplication::processEvents();
4498 QObject *rootObject = object->property("vp").value<QObject*>();
4499 QVERIFY(rootObject != 0);
4500 QObject *childObject = rootObject->findChild<QObject*>("text");
4501 QVERIFY(childObject != 0);
4502 QWeakPointer<QObject> rootObjectTracker(rootObject);
4503 QVERIFY(!rootObjectTracker.isNull());
4504 QWeakPointer<QObject> childObjectTracker(childObject);
4505 QVERIFY(!childObjectTracker.isNull());
4507 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4508 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4509 QMetaObject::invokeMethod(object, "deassignCircular");
4510 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4511 QCoreApplication::processEvents();
4512 QVERIFY(rootObjectTracker.isNull()); // should have been collected
4513 QVERIFY(childObjectTracker.isNull()); // should have been collected
4517 void tst_qqmlecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
4519 *(int*)(parameter) += 1;
4520 qPersistentDispose(object);
4523 void tst_qqmlecmascript::propertyVarInheritance()
4525 int propertyVarWeakRefCallbackCount = 0;
4527 // enforce behaviour regarding element inheritance - ensure handle disposal.
4528 // The particular component under test here has a chain of references.
4529 QQmlComponent component(&engine, testFileUrl("propertyVar.inherit.qml"));
4530 QObject *object = component.create();
4531 QVERIFY(object != 0);
4532 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4533 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4534 QCoreApplication::processEvents();
4535 // we want to be able to track when the varProperties array of the last metaobject is disposed
4536 QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
4537 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*>();
4538 QQmlVMEMetaObject *icovmemo = ((QQmlVMEMetaObject *)(ico5->metaObject()));
4539 QQmlVMEMetaObject *ccovmemo = ((QQmlVMEMetaObject *)(cco5->metaObject()));
4540 v8::Persistent<v8::Value> icoCanaryHandle;
4541 v8::Persistent<v8::Value> ccoCanaryHandle;
4544 // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only
4545 // public function which can return us a handle to something in the varProperties array.
4546 icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ")));
4547 ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")));
4548 // we make them weak and invoke the gc, but we should not hit the weak-callback yet
4549 // as the varproperties array of each vmemo still references the resource.
4550 icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4551 ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4553 QVERIFY(propertyVarWeakRefCallbackCount == 0);
4555 // now we deassign the var prop, which should trigger collection of item subtrees.
4556 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4557 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4558 QCoreApplication::processEvents();
4559 // ensure that there are only weak handles to the underlying varProperties array remaining.
4561 QCOMPARE(propertyVarWeakRefCallbackCount, 2); // should have been called for both, since all refs should be weak.
4563 // since there are no parent vmemo's to keep implicit references alive, and the only handles
4564 // to what remains are weak, all varProperties arrays must have been collected.
4567 void tst_qqmlecmascript::propertyVarInheritance2()
4569 int propertyVarWeakRefCallbackCount = 0;
4571 // The particular component under test here does NOT have a chain of references; the
4572 // only link between rootObject and childObject is that rootObject is the parent of childObject.
4573 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4574 QObject *object = component.create();
4575 QVERIFY(object != 0);
4576 QMetaObject::invokeMethod(object, "assignCircular");
4577 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4578 QCoreApplication::processEvents();
4579 QObject *rootObject = object->property("vp").value<QObject*>();
4580 QVERIFY(rootObject != 0);
4581 QObject *childObject = rootObject->findChild<QObject*>("text");
4582 QVERIFY(childObject != 0);
4583 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4584 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4585 v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
4588 propertyVarWeakRefCallbackCount = 0; // reset callback count.
4589 childObjectVarArrayValueHandle = qPersistentNew(((QQmlVMEMetaObject *)(childObject->metaObject()))->vmeProperty(childObject->metaObject()->indexOfProperty("vp")));
4590 childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4592 QVERIFY(propertyVarWeakRefCallbackCount == 0); // should not have been collected yet.
4593 QCOMPARE(childObject->property("vp").value<QObject*>(), rootObject);
4594 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4596 QMetaObject::invokeMethod(object, "deassignCircular");
4597 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4598 QCoreApplication::processEvents();
4599 QVERIFY(propertyVarWeakRefCallbackCount == 1); // should have been collected now.
4603 // Ensure that QObject type conversion works on binding assignment
4604 void tst_qqmlecmascript::elementAssign()
4606 QQmlComponent component(&engine, testFileUrl("elementAssign.qml"));
4608 QObject *object = component.create();
4609 QVERIFY(object != 0);
4611 QCOMPARE(object->property("test").toBool(), true);
4617 void tst_qqmlecmascript::objectPassThroughSignals()
4619 QQmlComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml"));
4621 QObject *object = component.create();
4622 QVERIFY(object != 0);
4624 QCOMPARE(object->property("test").toBool(), true);
4630 void tst_qqmlecmascript::objectConversion()
4632 QQmlComponent component(&engine, testFileUrl("objectConversion.qml"));
4634 QObject *object = component.create();
4635 QVERIFY(object != 0);
4637 QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
4638 QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
4645 void tst_qqmlecmascript::booleanConversion()
4647 QQmlComponent component(&engine, testFileUrl("booleanConversion.qml"));
4649 QObject *object = component.create();
4650 QVERIFY(object != 0);
4652 QCOMPARE(object->property("test_true1").toBool(), true);
4653 QCOMPARE(object->property("test_true2").toBool(), true);
4654 QCOMPARE(object->property("test_true3").toBool(), true);
4655 QCOMPARE(object->property("test_true4").toBool(), true);
4656 QCOMPARE(object->property("test_true5").toBool(), true);
4658 QCOMPARE(object->property("test_false1").toBool(), false);
4659 QCOMPARE(object->property("test_false2").toBool(), false);
4660 QCOMPARE(object->property("test_false3").toBool(), false);
4665 void tst_qqmlecmascript::handleReferenceManagement()
4670 // Linear QObject reference
4671 QQmlEngine hrmEngine;
4672 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml"));
4673 QObject *object = component.create();
4674 QVERIFY(object != 0);
4675 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4676 cro->setEngine(&hrmEngine);
4677 cro->setDtorCount(&dtorCount);
4678 QMetaObject::invokeMethod(object, "createReference");
4680 QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
4682 hrmEngine.collectGarbage();
4683 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4684 QCoreApplication::processEvents();
4685 QCOMPARE(dtorCount, 3);
4690 // Circular QObject reference
4691 QQmlEngine hrmEngine;
4692 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml"));
4693 QObject *object = component.create();
4694 QVERIFY(object != 0);
4695 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4696 cro->setEngine(&hrmEngine);
4697 cro->setDtorCount(&dtorCount);
4698 QMetaObject::invokeMethod(object, "circularReference");
4700 QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
4702 hrmEngine.collectGarbage();
4703 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4704 QCoreApplication::processEvents();
4705 QCOMPARE(dtorCount, 3);
4710 // Linear handle reference
4711 QQmlEngine hrmEngine;
4712 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.1.qml"));
4713 QObject *object = component.create();
4714 QVERIFY(object != 0);
4715 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4717 crh->setEngine(&hrmEngine);
4718 crh->setDtorCount(&dtorCount);
4719 QMetaObject::invokeMethod(object, "createReference");
4720 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4721 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4722 QVERIFY(first != 0);
4723 QVERIFY(second != 0);
4724 first->addReference(QQmlData::get(second)->v8object); // create reference
4725 // now we have to reparent second and make second owned by JS.
4726 second->setParent(0);
4727 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
4729 QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
4731 hrmEngine.collectGarbage();
4732 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4733 QCoreApplication::processEvents();
4734 QCOMPARE(dtorCount, 3);
4739 // Circular handle reference
4740 QQmlEngine hrmEngine;
4741 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.2.qml"));
4742 QObject *object = component.create();
4743 QVERIFY(object != 0);
4744 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4746 crh->setEngine(&hrmEngine);
4747 crh->setDtorCount(&dtorCount);
4748 QMetaObject::invokeMethod(object, "circularReference");
4749 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4750 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4751 QVERIFY(first != 0);
4752 QVERIFY(second != 0);
4753 first->addReference(QQmlData::get(second)->v8object); // create circular reference
4754 second->addReference(QQmlData::get(first)->v8object); // note: must be weak.
4755 // now we have to reparent and change ownership.
4756 first->setParent(0);
4757 second->setParent(0);
4758 QQmlEngine::setObjectOwnership(first, QQmlEngine::JavaScriptOwnership);
4759 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
4761 QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
4763 hrmEngine.collectGarbage();
4764 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4765 QCoreApplication::processEvents();
4766 QCOMPARE(dtorCount, 3);
4771 // multiple engine interaction - linear reference
4772 QQmlEngine hrmEngine1;
4773 QQmlEngine hrmEngine2;
4774 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4775 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4776 QObject *object1 = component1.create();
4777 QObject *object2 = component2.create();
4778 QVERIFY(object1 != 0);
4779 QVERIFY(object2 != 0);
4780 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4781 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4784 crh1->setEngine(&hrmEngine1);
4785 crh2->setEngine(&hrmEngine2);
4786 crh1->setDtorCount(&dtorCount);
4787 crh2->setDtorCount(&dtorCount);
4788 QMetaObject::invokeMethod(object1, "createReference");
4789 QMetaObject::invokeMethod(object2, "createReference");
4790 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4791 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4792 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4793 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4794 QVERIFY(first1 != 0);
4795 QVERIFY(second1 != 0);
4796 QVERIFY(first2 != 0);
4797 QVERIFY(second2 != 0);
4798 first1->addReference(QQmlData::get(second2)->v8object); // create reference across engines
4799 // now we have to reparent second2 and make second2 owned by JS.
4800 second2->setParent(0);
4801 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4803 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4804 QCoreApplication::processEvents();
4805 QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
4808 hrmEngine1.collectGarbage();
4809 hrmEngine2.collectGarbage();
4810 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4811 QCoreApplication::processEvents();
4812 QCOMPARE(dtorCount, 6);
4817 // multiple engine interaction - circular reference
4818 QQmlEngine hrmEngine1;
4819 QQmlEngine hrmEngine2;
4820 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4821 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4822 QObject *object1 = component1.create();
4823 QObject *object2 = component2.create();
4824 QVERIFY(object1 != 0);
4825 QVERIFY(object2 != 0);
4826 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4827 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4830 crh1->setEngine(&hrmEngine1);
4831 crh2->setEngine(&hrmEngine2);
4832 crh1->setDtorCount(&dtorCount);
4833 crh2->setDtorCount(&dtorCount);
4834 QMetaObject::invokeMethod(object1, "createReference");
4835 QMetaObject::invokeMethod(object2, "createReference");
4836 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4837 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4838 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4839 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4840 QVERIFY(first1 != 0);
4841 QVERIFY(second1 != 0);
4842 QVERIFY(first2 != 0);
4843 QVERIFY(second2 != 0);
4844 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
4845 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
4846 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
4847 first2->addReference(QQmlData::get(first1)->v8object); // close the loop - circular ref across engines
4848 // now we have to reparent and change ownership to JS.
4849 first1->setParent(0);
4850 second1->setParent(0);
4851 first2->setParent(0);
4852 second2->setParent(0);
4853 QQmlEngine::setObjectOwnership(first1, QQmlEngine::JavaScriptOwnership);
4854 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
4855 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
4856 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4858 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4859 QCoreApplication::processEvents();
4860 QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
4863 hrmEngine1.collectGarbage();
4864 hrmEngine2.collectGarbage();
4865 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4866 QCoreApplication::processEvents();
4867 QCOMPARE(dtorCount, 6);
4872 // multiple engine interaction - linear reference with engine deletion
4873 QQmlEngine *hrmEngine1 = new QQmlEngine;
4874 QQmlEngine *hrmEngine2 = new QQmlEngine;
4875 QQmlComponent component1(hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4876 QQmlComponent component2(hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4877 QObject *object1 = component1.create();
4878 QObject *object2 = component2.create();
4879 QVERIFY(object1 != 0);
4880 QVERIFY(object2 != 0);
4881 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4882 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4885 crh1->setEngine(hrmEngine1);
4886 crh2->setEngine(hrmEngine2);
4887 crh1->setDtorCount(&dtorCount);
4888 crh2->setDtorCount(&dtorCount);
4889 QMetaObject::invokeMethod(object1, "createReference");
4890 QMetaObject::invokeMethod(object2, "createReference");
4891 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4892 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4893 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4894 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4895 QVERIFY(first1 != 0);
4896 QVERIFY(second1 != 0);
4897 QVERIFY(first2 != 0);
4898 QVERIFY(second2 != 0);
4899 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
4900 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
4901 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
4902 // now we have to reparent and change ownership to JS.
4903 first1->setParent(crh1);
4904 second1->setParent(0);
4905 first2->setParent(0);
4906 second2->setParent(0);
4907 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
4908 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
4909 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4912 QCOMPARE(dtorCount, 0);
4913 delete hrmEngine2; // should trigger deletion of objects with JS ownership tracked by this engine
4915 QCOMPARE(dtorCount, 2); // first2 and second2 should have been deleted.
4919 QCOMPARE(dtorCount, 6); // deleting object1 and object2 should trigger deletion of first1 and first2.
4921 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4922 QCoreApplication::processEvents();
4923 QCOMPARE(dtorCount, 6); // all objects should have been cleaned up prior to deleting hrmEngine1.
4927 void tst_qqmlecmascript::stringArg()
4929 QQmlComponent component(&engine, testFileUrl("stringArg.qml"));
4930 QObject *object = component.create();
4931 QVERIFY(object != 0);
4932 QMetaObject::invokeMethod(object, "success");
4933 QVERIFY(object->property("returnValue").toBool());
4935 QString w1 = testFileUrl("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
4936 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
4937 QMetaObject::invokeMethod(object, "failure");
4938 QVERIFY(object->property("returnValue").toBool());
4943 void tst_qqmlecmascript::readonlyDeclaration()
4945 QQmlComponent component(&engine, testFileUrl("readonlyDeclaration.qml"));
4947 QObject *object = component.create();
4948 QVERIFY(object != 0);
4950 QCOMPARE(object->property("test").toBool(), true);
4955 Q_DECLARE_METATYPE(QList<int>)
4956 Q_DECLARE_METATYPE(QList<qreal>)
4957 Q_DECLARE_METATYPE(QList<bool>)
4958 Q_DECLARE_METATYPE(QList<QString>)
4959 Q_DECLARE_METATYPE(QList<QUrl>)
4960 void tst_qqmlecmascript::sequenceConversionRead()
4963 QUrl qmlFile = testFileUrl("sequenceConversion.read.qml");
4964 QQmlComponent component(&engine, qmlFile);
4965 QObject *object = component.create();
4966 QVERIFY(object != 0);
4967 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4970 QMetaObject::invokeMethod(object, "readSequences");
4971 QList<int> intList; intList << 1 << 2 << 3 << 4;
4972 QCOMPARE(object->property("intListLength").toInt(), intList.length());
4973 QCOMPARE(object->property("intList").value<QList<int> >(), intList);
4974 QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
4975 QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
4976 QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
4977 QList<bool> boolList; boolList << true << false << true << false;
4978 QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
4979 QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
4980 QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
4981 QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
4982 QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
4983 QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
4984 QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
4985 QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
4986 QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
4987 QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
4988 QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
4990 QMetaObject::invokeMethod(object, "readSequenceElements");
4991 QCOMPARE(object->property("intVal").toInt(), 2);
4992 QCOMPARE(object->property("qrealVal").toReal(), 2.2);
4993 QCOMPARE(object->property("boolVal").toBool(), false);
4994 QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
4995 QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
4996 QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
4998 QMetaObject::invokeMethod(object, "enumerateSequenceElements");
4999 QCOMPARE(object->property("enumerationMatches").toBool(), true);
5001 intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
5002 QQmlProperty seqProp(seq, "intListProperty");
5003 QCOMPARE(seqProp.read().value<QList<int> >(), intList);
5004 QQmlProperty seqProp2(seq, "intListProperty", &engine);
5005 QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
5007 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5008 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5014 QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml");
5015 QQmlComponent component(&engine, qmlFile);
5016 QObject *object = component.create();
5017 QVERIFY(object != 0);
5018 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5021 // we haven't registered QList<QPoint> as a sequence type.
5022 QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5023 QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
5024 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5025 QTest::ignoreMessage(QtWarningMsg, warningTwo.toLatin1().constData());
5027 QMetaObject::invokeMethod(object, "performTest");
5029 // QList<QPoint> has not been registered as a sequence type.
5030 QCOMPARE(object->property("pointListLength").toInt(), 0);
5031 QVERIFY(!object->property("pointList").isValid());
5032 QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5033 QQmlProperty seqProp(seq, "pointListProperty", &engine);
5034 QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
5040 void tst_qqmlecmascript::sequenceConversionWrite()
5043 QUrl qmlFile = testFileUrl("sequenceConversion.write.qml");
5044 QQmlComponent component(&engine, qmlFile);
5045 QObject *object = component.create();
5046 QVERIFY(object != 0);
5047 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5050 QMetaObject::invokeMethod(object, "writeSequences");
5051 QCOMPARE(object->property("success").toBool(), true);
5053 QMetaObject::invokeMethod(object, "writeSequenceElements");
5054 QCOMPARE(object->property("success").toBool(), true);
5056 QMetaObject::invokeMethod(object, "writeOtherElements");
5057 QCOMPARE(object->property("success").toBool(), true);
5059 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5060 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5066 QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml");
5067 QQmlComponent component(&engine, qmlFile);
5068 QObject *object = component.create();
5069 QVERIFY(object != 0);
5070 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5073 // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
5074 QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to an unregistered type");
5075 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5077 QMetaObject::invokeMethod(object, "performTest");
5079 QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
5080 QCOMPARE(seq->pointListProperty(), pointList);
5086 void tst_qqmlecmascript::sequenceConversionArray()
5088 // ensure that in JS the returned sequences act just like normal JS Arrays.
5089 QUrl qmlFile = testFileUrl("sequenceConversion.array.qml");
5090 QQmlComponent component(&engine, qmlFile);
5091 QObject *object = component.create();
5092 QVERIFY(object != 0);
5093 QMetaObject::invokeMethod(object, "indexedAccess");
5094 QVERIFY(object->property("success").toBool());
5095 QMetaObject::invokeMethod(object, "arrayOperations");
5096 QVERIFY(object->property("success").toBool());
5097 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5098 QVERIFY(object->property("success").toBool());
5099 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5100 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5105 void tst_qqmlecmascript::sequenceConversionIndexes()
5107 // ensure that we gracefully fail if unsupported index values are specified.
5108 // Qt container classes only support non-negative, signed integer index values.
5109 QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
5110 QQmlComponent component(&engine, qmlFile);
5111 QObject *object = component.create();
5112 QVERIFY(object != 0);
5113 QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
5114 QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
5115 QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
5116 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
5117 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
5118 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
5119 QMetaObject::invokeMethod(object, "indexedAccess");
5120 QVERIFY(object->property("success").toBool());
5124 void tst_qqmlecmascript::sequenceConversionThreads()
5126 // ensure that sequence conversion operations work correctly in a worker thread
5127 // and that serialisation between the main and worker thread succeeds.
5128 QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml");
5129 QQmlComponent component(&engine, qmlFile);
5130 QObject *object = component.create();
5131 QVERIFY(object != 0);
5133 QMetaObject::invokeMethod(object, "testIntSequence");
5134 QTRY_VERIFY(object->property("finished").toBool());
5135 QVERIFY(object->property("success").toBool());
5137 QMetaObject::invokeMethod(object, "testQrealSequence");
5138 QTRY_VERIFY(object->property("finished").toBool());
5139 QVERIFY(object->property("success").toBool());
5141 QMetaObject::invokeMethod(object, "testBoolSequence");
5142 QTRY_VERIFY(object->property("finished").toBool());
5143 QVERIFY(object->property("success").toBool());
5145 QMetaObject::invokeMethod(object, "testStringSequence");
5146 QTRY_VERIFY(object->property("finished").toBool());
5147 QVERIFY(object->property("success").toBool());
5149 QMetaObject::invokeMethod(object, "testQStringSequence");
5150 QTRY_VERIFY(object->property("finished").toBool());
5151 QVERIFY(object->property("success").toBool());
5153 QMetaObject::invokeMethod(object, "testUrlSequence");
5154 QTRY_VERIFY(object->property("finished").toBool());
5155 QVERIFY(object->property("success").toBool());
5157 QMetaObject::invokeMethod(object, "testVariantSequence");
5158 QTRY_VERIFY(object->property("finished").toBool());
5159 QVERIFY(object->property("success").toBool());
5164 void tst_qqmlecmascript::sequenceConversionBindings()
5167 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml");
5168 QQmlComponent component(&engine, qmlFile);
5169 QObject *object = component.create();
5170 QVERIFY(object != 0);
5171 QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
5172 QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
5173 QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
5174 QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
5175 QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
5180 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml");
5181 QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
5182 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5183 QQmlComponent component(&engine, qmlFile);
5184 QObject *object = component.create();
5185 QVERIFY(object != 0);
5190 void tst_qqmlecmascript::sequenceConversionCopy()
5192 QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml");
5193 QQmlComponent component(&engine, qmlFile);
5194 QObject *object = component.create();
5195 QVERIFY(object != 0);
5196 QMetaObject::invokeMethod(object, "testCopySequences");
5197 QCOMPARE(object->property("success").toBool(), true);
5198 QMetaObject::invokeMethod(object, "readSequenceCopyElements");
5199 QCOMPARE(object->property("success").toBool(), true);
5200 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5201 QCOMPARE(object->property("success").toBool(), true);
5205 void tst_qqmlecmascript::assignSequenceTypes()
5207 // test binding array to sequence type property
5209 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml"));
5210 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5211 QVERIFY(object != 0);
5212 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5213 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5214 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5215 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5216 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5217 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5221 // test binding literal to sequence type property
5223 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml"));
5224 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5225 QVERIFY(object != 0);
5226 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5227 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5228 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5229 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5230 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5231 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5235 // test binding single value to sequence type property
5237 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml"));
5238 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5239 QVERIFY(object != 0);
5240 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5241 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5242 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5243 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5247 // test assigning array to sequence type property in js function
5249 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml"));
5250 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5251 QVERIFY(object != 0);
5252 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5253 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5254 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5255 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5256 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5257 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5261 // test assigning literal to sequence type property in js function
5263 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml"));
5264 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5265 QVERIFY(object != 0);
5266 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5267 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5268 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5269 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5270 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5271 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5275 // test assigning single value to sequence type property in js function
5277 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml"));
5278 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5279 QVERIFY(object != 0);
5280 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5281 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5282 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5283 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5287 // test QList<QUrl> literal assignment and binding assignment causes url resolution when required
5289 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml"));
5290 QObject *object = component.create();
5291 QVERIFY(object != 0);
5292 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
5293 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
5294 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
5295 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
5296 MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco5"));
5297 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0);
5298 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5299 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5300 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5301 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5302 QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5307 // Test that assigning a null object works
5308 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
5309 void tst_qqmlecmascript::nullObjectBinding()
5311 QQmlComponent component(&engine, testFileUrl("nullObjectBinding.qml"));
5313 QObject *object = component.create();
5314 QVERIFY(object != 0);
5316 QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
5321 // Test that bindings don't evaluate once the engine has been destroyed
5322 void tst_qqmlecmascript::deletedEngine()
5324 QQmlEngine *engine = new QQmlEngine;
5325 QQmlComponent component(engine, testFileUrl("deletedEngine.qml"));
5327 QObject *object = component.create();
5328 QVERIFY(object != 0);
5330 QCOMPARE(object->property("a").toInt(), 39);
5331 object->setProperty("b", QVariant(9));
5332 QCOMPARE(object->property("a").toInt(), 117);
5336 QCOMPARE(object->property("a").toInt(), 117);
5337 object->setProperty("b", QVariant(10));
5338 QCOMPARE(object->property("a").toInt(), 117);
5343 // Test the crashing part of QTBUG-9705
5344 void tst_qqmlecmascript::libraryScriptAssert()
5346 QQmlComponent component(&engine, testFileUrl("libraryScriptAssert.qml"));
5348 QObject *object = component.create();
5349 QVERIFY(object != 0);
5354 void tst_qqmlecmascript::variantsAssignedUndefined()
5356 QQmlComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml"));
5358 QObject *object = component.create();
5359 QVERIFY(object != 0);
5361 QCOMPARE(object->property("test1").toInt(), 10);
5362 QCOMPARE(object->property("test2").toInt(), 11);
5364 object->setProperty("runTest", true);
5366 QCOMPARE(object->property("test1"), QVariant());
5367 QCOMPARE(object->property("test2"), QVariant());
5373 void tst_qqmlecmascript::qtbug_9792()
5375 QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml"));
5377 QQmlContext *context = new QQmlContext(engine.rootContext());
5379 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
5380 QVERIFY(object != 0);
5382 QTest::ignoreMessage(QtDebugMsg, "Hello world!");
5383 object->basicSignal();
5387 transientErrorsMsgCount = 0;
5388 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5390 object->basicSignal();
5392 qInstallMsgHandler(old);
5394 QCOMPARE(transientErrorsMsgCount, 0);
5399 // Verifies that QQmlGuard<>s used in the vmemetaobject are cleaned correctly
5400 void tst_qqmlecmascript::qtcreatorbug_1289()
5402 QQmlComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml"));
5404 QObject *o = component.create();
5407 QObject *nested = qvariant_cast<QObject *>(o->property("object"));
5408 QVERIFY(nested != 0);
5410 QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
5413 nested = qvariant_cast<QObject *>(o->property("object"));
5414 QVERIFY(nested == 0);
5416 // If the bug is present, the next line will crash
5420 // Test that we shut down without stupid warnings
5421 void tst_qqmlecmascript::noSpuriousWarningsAtShutdown()
5424 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml"));
5426 QObject *o = component.create();
5428 transientErrorsMsgCount = 0;
5429 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5433 qInstallMsgHandler(old);
5435 QCOMPARE(transientErrorsMsgCount, 0);
5440 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml"));
5442 QObject *o = component.create();
5444 transientErrorsMsgCount = 0;
5445 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5449 qInstallMsgHandler(old);
5451 QCOMPARE(transientErrorsMsgCount, 0);
5455 void tst_qqmlecmascript::canAssignNullToQObject()
5458 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml"));
5460 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5463 QVERIFY(o->objectProperty() != 0);
5465 o->setProperty("runTest", true);
5467 QVERIFY(o->objectProperty() == 0);
5473 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml"));
5475 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5478 QVERIFY(o->objectProperty() == 0);
5484 void tst_qqmlecmascript::functionAssignment_fromBinding()
5486 QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml"));
5488 QString url = component.url().toString();
5489 QString w1 = url + ":4:25: Unable to assign a function to a property of any type other than var.";
5490 QString w2 = url + ":5:25: Invalid use of Qt.binding() in a binding declaration.";
5491 QString w3 = url + ":6:21: Invalid use of Qt.binding() in a binding declaration.";
5492 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5493 QTest::ignoreMessage(QtWarningMsg, w2.toLatin1().constData());
5494 QTest::ignoreMessage(QtWarningMsg, w3.toLatin1().constData());
5496 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5499 QVERIFY(!o->property("a").isValid());
5504 void tst_qqmlecmascript::functionAssignment_fromJS()
5506 QFETCH(QString, triggerProperty);
5508 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5509 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5511 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5513 QVERIFY(!o->property("a").isValid());
5515 o->setProperty("aNumber", QVariant(5));
5516 o->setProperty(triggerProperty.toUtf8().constData(), true);
5517 QCOMPARE(o->property("a"), QVariant(50));
5519 o->setProperty("aNumber", QVariant(10));
5520 QCOMPARE(o->property("a"), QVariant(100));
5525 void tst_qqmlecmascript::functionAssignment_fromJS_data()
5527 QTest::addColumn<QString>("triggerProperty");
5529 QTest::newRow("assign to property") << "assignToProperty";
5530 QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
5532 QTest::newRow("assign to value type") << "assignToValueType";
5534 QTest::newRow("use 'this'") << "assignWithThis";
5535 QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
5538 void tst_qqmlecmascript::functionAssignmentfromJS_invalid()
5540 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5541 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5543 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5545 QVERIFY(!o->property("a").isValid());
5547 o->setProperty("assignFuncWithoutReturn", true);
5548 QVERIFY(!o->property("a").isValid());
5550 QString url = component.url().toString();
5551 QString warning = url + ":67:17: Unable to assign QString to int";
5552 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5553 o->setProperty("assignWrongType", true);
5555 warning = url + ":71:29: Unable to assign QString to int";
5556 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5557 o->setProperty("assignWrongTypeToValueType", true);
5562 void tst_qqmlecmascript::functionAssignment_afterBinding()
5564 QQmlComponent component(&engine, testFileUrl("functionAssignment.3.qml"));
5566 QString url = component.url().toString();
5567 //QString w1 = url + ":16: Error: Cannot assign JavaScript function to int"; // for now, function assignment = binding assignment
5568 QString w1 = QLatin1String("WARNING: function assignment is DEPRECATED and will be removed! Wrap RHS in Qt.binding(): ") + url + QLatin1String(":16");
5569 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5571 QObject *o = component.create();
5573 QCOMPARE(o->property("t1"), QVariant::fromValue<int>(4)); // should have bound
5574 //QCOMPARE(o->property("t2"), QVariant::fromValue<int>(2)); // should not have changed
5575 QCOMPARE(o->property("t2"), QVariant::fromValue<int>(4)); // for now, function assignment = binding assignment
5580 void tst_qqmlecmascript::eval()
5582 QQmlComponent component(&engine, testFileUrl("eval.qml"));
5584 QObject *o = component.create();
5587 QCOMPARE(o->property("test1").toBool(), true);
5588 QCOMPARE(o->property("test2").toBool(), true);
5589 QCOMPARE(o->property("test3").toBool(), true);
5590 QCOMPARE(o->property("test4").toBool(), true);
5591 QCOMPARE(o->property("test5").toBool(), true);
5596 void tst_qqmlecmascript::function()
5598 QQmlComponent component(&engine, testFileUrl("function.qml"));
5600 QObject *o = component.create();
5603 QCOMPARE(o->property("test1").toBool(), true);
5604 QCOMPARE(o->property("test2").toBool(), true);
5605 QCOMPARE(o->property("test3").toBool(), true);
5610 void tst_qqmlecmascript::functionException()
5612 // QTBUG-24037 - shouldn't crash.
5613 QString errstr = testFileUrl("v8functionException.qml").toString() + QLatin1String(":13: SyntaxError: Unexpected token ILLEGAL");
5614 QTest::ignoreMessage(QtWarningMsg, qPrintable(errstr));
5615 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Exception occurred during compilation of function: dynamicSlot()");
5616 QQmlComponent component(&engine, testFileUrl("v8functionException.qml"));
5617 QObject *o = component.create();
5619 QMetaObject::invokeMethod(o, "dynamicSlot");
5623 // Test the "Qt.include" method
5624 void tst_qqmlecmascript::include()
5626 // Non-library relative include
5628 QQmlComponent component(&engine, testFileUrl("include.qml"));
5629 QObject *o = component.create();
5632 QCOMPARE(o->property("test0").toInt(), 99);
5633 QCOMPARE(o->property("test1").toBool(), true);
5634 QCOMPARE(o->property("test2").toBool(), true);
5635 QCOMPARE(o->property("test2_1").toBool(), true);
5636 QCOMPARE(o->property("test3").toBool(), true);
5637 QCOMPARE(o->property("test3_1").toBool(), true);
5642 // Library relative include
5644 QQmlComponent component(&engine, testFileUrl("include_shared.qml"));
5645 QObject *o = component.create();
5648 QCOMPARE(o->property("test0").toInt(), 99);
5649 QCOMPARE(o->property("test1").toBool(), true);
5650 QCOMPARE(o->property("test2").toBool(), true);
5651 QCOMPARE(o->property("test2_1").toBool(), true);
5652 QCOMPARE(o->property("test3").toBool(), true);
5653 QCOMPARE(o->property("test3_1").toBool(), true);
5660 QQmlComponent component(&engine, testFileUrl("include_callback.qml"));
5661 QObject *o = component.create();
5664 QCOMPARE(o->property("test1").toBool(), true);
5665 QCOMPARE(o->property("test2").toBool(), true);
5666 QCOMPARE(o->property("test3").toBool(), true);
5667 QCOMPARE(o->property("test4").toBool(), true);
5668 QCOMPARE(o->property("test5").toBool(), true);
5669 QCOMPARE(o->property("test6").toBool(), true);
5674 // Including file with ".pragma library"
5676 QQmlComponent component(&engine, testFileUrl("include_pragma.qml"));
5677 QObject *o = component.create();
5679 QCOMPARE(o->property("test1").toInt(), 100);
5686 TestHTTPServer server(8111);
5687 QVERIFY(server.isValid());
5688 server.serveDirectory(dataDirectory());
5690 QQmlComponent component(&engine, testFileUrl("include_remote.qml"));
5691 QObject *o = component.create();
5694 QTRY_VERIFY(o->property("done").toBool() == true);
5695 QTRY_VERIFY(o->property("done2").toBool() == true);
5697 QCOMPARE(o->property("test1").toBool(), true);
5698 QCOMPARE(o->property("test2").toBool(), true);
5699 QCOMPARE(o->property("test3").toBool(), true);
5700 QCOMPARE(o->property("test4").toBool(), true);
5701 QCOMPARE(o->property("test5").toBool(), true);
5703 QCOMPARE(o->property("test6").toBool(), true);
5704 QCOMPARE(o->property("test7").toBool(), true);
5705 QCOMPARE(o->property("test8").toBool(), true);
5706 QCOMPARE(o->property("test9").toBool(), true);
5707 QCOMPARE(o->property("test10").toBool(), true);
5714 TestHTTPServer server(8111);
5715 QVERIFY(server.isValid());
5716 server.serveDirectory(dataDirectory());
5718 QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml"));
5719 QObject *o = component.create();
5722 QTRY_VERIFY(o->property("done").toBool() == true);
5724 QCOMPARE(o->property("test1").toBool(), true);
5725 QCOMPARE(o->property("test2").toBool(), true);
5726 QCOMPARE(o->property("test3").toBool(), true);
5732 void tst_qqmlecmascript::signalHandlers()
5734 QQmlComponent component(&engine, testFileUrl("signalHandlers.qml"));
5735 QObject *o = component.create();
5738 QVERIFY(o->property("count").toInt() == 0);
5739 QMetaObject::invokeMethod(o, "testSignalCall");
5740 QCOMPARE(o->property("count").toInt(), 1);
5742 QMetaObject::invokeMethod(o, "testSignalHandlerCall");
5743 QCOMPARE(o->property("count").toInt(), 1);
5744 QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
5746 QVERIFY(o->property("funcCount").toInt() == 0);
5747 QMetaObject::invokeMethod(o, "testSignalConnection");
5748 QCOMPARE(o->property("funcCount").toInt(), 1);
5750 QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
5751 QCOMPARE(o->property("funcCount").toInt(), 2);
5753 QMetaObject::invokeMethod(o, "testSignalDefined");
5754 QCOMPARE(o->property("definedResult").toBool(), true);
5756 QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
5757 QCOMPARE(o->property("definedHandlerResult").toBool(), true);
5762 void tst_qqmlecmascript::qtbug_10696()
5764 QQmlComponent component(&engine, testFileUrl("qtbug_10696.qml"));
5765 QObject *o = component.create();
5770 void tst_qqmlecmascript::qtbug_11606()
5772 QQmlComponent component(&engine, testFileUrl("qtbug_11606.qml"));
5773 QObject *o = component.create();
5775 QCOMPARE(o->property("test").toBool(), true);
5779 void tst_qqmlecmascript::qtbug_11600()
5781 QQmlComponent component(&engine, testFileUrl("qtbug_11600.qml"));
5782 QObject *o = component.create();
5784 QCOMPARE(o->property("test").toBool(), true);
5788 void tst_qqmlecmascript::qtbug_21864()
5790 QQmlComponent component(&engine, testFileUrl("qtbug_21864.qml"));
5791 QObject *o = component.create();
5793 QCOMPARE(o->property("test").toBool(), true);
5797 void tst_qqmlecmascript::rewriteMultiLineStrings()
5801 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml"));
5802 QObject *o = component.create();
5804 QTRY_COMPARE(o->property("test").toBool(), true);
5809 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings_crlf.1.qml"));
5810 QObject *o = component.create();
5816 void tst_qqmlecmascript::qobjectConnectionListExceptionHandling()
5819 QQmlComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml"));
5820 QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined");
5821 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5822 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5823 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5824 QObject *o = component.create();
5826 QCOMPARE(o->property("test").toBool(), true);
5830 // Reading and writing non-scriptable properties should fail
5831 void tst_qqmlecmascript::nonscriptable()
5833 QQmlComponent component(&engine, testFileUrl("nonscriptable.qml"));
5834 QObject *o = component.create();
5836 QCOMPARE(o->property("readOk").toBool(), true);
5837 QCOMPARE(o->property("writeOk").toBool(), true);
5841 // deleteLater() should not be callable from QML
5842 void tst_qqmlecmascript::deleteLater()
5844 QQmlComponent component(&engine, testFileUrl("deleteLater.qml"));
5845 QObject *o = component.create();
5847 QCOMPARE(o->property("test").toBool(), true);
5851 void tst_qqmlecmascript::in()
5853 QQmlComponent component(&engine, testFileUrl("in.qml"));
5854 QObject *o = component.create();
5856 QCOMPARE(o->property("test1").toBool(), true);
5857 QCOMPARE(o->property("test2").toBool(), true);
5861 void tst_qqmlecmascript::typeOf()
5863 QQmlComponent component(&engine, testFileUrl("typeOf.qml"));
5865 QObject *o = component.create();
5868 QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
5869 QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
5870 QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
5871 QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
5872 QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
5873 QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
5874 QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
5875 QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
5876 QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
5881 void tst_qqmlecmascript::qtbug_24448()
5883 QQmlComponent component(&engine, testFileUrl("qtbug_24448.qml"));
5884 QScopedPointer<QObject> o(component.create());
5886 QVERIFY(o->property("test").toBool());
5889 void tst_qqmlecmascript::sharedAttachedObject()
5891 QQmlComponent component(&engine, testFileUrl("sharedAttachedObject.qml"));
5892 QObject *o = component.create();
5894 QCOMPARE(o->property("test1").toBool(), true);
5895 QCOMPARE(o->property("test2").toBool(), true);
5900 void tst_qqmlecmascript::objectName()
5902 QQmlComponent component(&engine, testFileUrl("objectName.qml"));
5903 QObject *o = component.create();
5906 QCOMPARE(o->property("test1").toString(), QString("hello"));
5907 QCOMPARE(o->property("test2").toString(), QString("ell"));
5909 o->setObjectName("world");
5911 QCOMPARE(o->property("test1").toString(), QString("world"));
5912 QCOMPARE(o->property("test2").toString(), QString("orl"));
5917 void tst_qqmlecmascript::writeRemovesBinding()
5919 QQmlComponent component(&engine, testFileUrl("writeRemovesBinding.qml"));
5920 QObject *o = component.create();
5923 QCOMPARE(o->property("test").toBool(), true);
5928 // Test bindings assigned to alias properties actually assign to the alias' target
5929 void tst_qqmlecmascript::aliasBindingsAssignCorrectly()
5931 QQmlComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml"));
5932 QObject *o = component.create();
5935 QCOMPARE(o->property("test").toBool(), true);
5940 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
5941 void tst_qqmlecmascript::aliasBindingsOverrideTarget()
5944 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml"));
5945 QObject *o = component.create();
5948 QCOMPARE(o->property("test").toBool(), true);
5954 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml"));
5955 QObject *o = component.create();
5958 QCOMPARE(o->property("test").toBool(), true);
5964 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml"));
5965 QObject *o = component.create();
5968 QCOMPARE(o->property("test").toBool(), true);
5974 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
5975 void tst_qqmlecmascript::aliasWritesOverrideBindings()
5978 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml"));
5979 QObject *o = component.create();
5982 QCOMPARE(o->property("test").toBool(), true);
5988 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml"));
5989 QObject *o = component.create();
5992 QCOMPARE(o->property("test").toBool(), true);
5998 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml"));
5999 QObject *o = component.create();
6002 QCOMPARE(o->property("test").toBool(), true);
6008 // Allow an alais to a composite element
6010 void tst_qqmlecmascript::aliasToCompositeElement()
6012 QQmlComponent component(&engine, testFileUrl("aliasToCompositeElement.qml"));
6014 QObject *object = component.create();
6015 QVERIFY(object != 0);
6020 void tst_qqmlecmascript::qtbug_20344()
6022 QQmlComponent component(&engine, testFileUrl("qtbug_20344.qml"));
6024 QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
6025 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6027 QObject *object = component.create();
6028 QVERIFY(object != 0);
6033 void tst_qqmlecmascript::revisionErrors()
6036 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml"));
6037 QString url = component.url().toString();
6039 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6040 QString warning2 = url + ":11: ReferenceError: prop2 is not defined";
6041 QString warning3 = url + ":13: ReferenceError: method2 is not defined";
6043 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6044 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6045 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6046 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6047 QVERIFY(object != 0);
6051 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml"));
6052 QString url = component.url().toString();
6054 // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
6055 // method2, prop2 from MyRevisionedClass not available
6056 // method4, prop4 from MyRevisionedSubclass not available
6057 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6058 QString warning2 = url + ":14: ReferenceError: prop2 is not defined";
6059 QString warning3 = url + ":10: ReferenceError: prop4 is not defined";
6060 QString warning4 = url + ":16: ReferenceError: prop4 is not defined";
6061 QString warning5 = url + ":20: ReferenceError: method2 is not defined";
6063 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6064 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6065 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6066 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
6067 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
6068 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6069 QVERIFY(object != 0);
6073 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml"));
6074 QString url = component.url().toString();
6076 // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
6077 // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
6078 QString warning1 = url + ":30: ReferenceError: methodD is not defined";
6079 QString warning2 = url + ":10: ReferenceError: propD is not defined";
6080 QString warning3 = url + ":20: ReferenceError: propD is not defined";
6081 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6082 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6083 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6084 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6085 QVERIFY(object != 0);
6090 void tst_qqmlecmascript::revision()
6093 QQmlComponent component(&engine, testFileUrl("metaobjectRevision.qml"));
6094 QString url = component.url().toString();
6096 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6097 QVERIFY(object != 0);
6101 QQmlComponent component(&engine, testFileUrl("metaobjectRevision2.qml"));
6102 QString url = component.url().toString();
6104 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6105 QVERIFY(object != 0);
6109 QQmlComponent component(&engine, testFileUrl("metaobjectRevision3.qml"));
6110 QString url = component.url().toString();
6112 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6113 QVERIFY(object != 0);
6116 // Test that non-root classes can resolve revisioned methods
6118 QQmlComponent component(&engine, testFileUrl("metaobjectRevision4.qml"));
6120 QObject *object = component.create();
6121 QVERIFY(object != 0);
6122 QCOMPARE(object->property("test").toReal(), 11.);
6127 void tst_qqmlecmascript::realToInt()
6129 QQmlComponent component(&engine, testFileUrl("realToInt.qml"));
6130 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6131 QVERIFY(object != 0);
6133 QMetaObject::invokeMethod(object, "test1");
6134 QCOMPARE(object->value(), int(4));
6135 QMetaObject::invokeMethod(object, "test2");
6136 QCOMPARE(object->value(), int(8));
6139 void tst_qqmlecmascript::urlProperty()
6142 QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml"));
6143 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6144 QVERIFY(object != 0);
6145 object->setStringProperty("http://qt-project.org");
6146 QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
6147 QCOMPARE(object->intProperty(), 123);
6148 QCOMPARE(object->value(), 1);
6149 QCOMPARE(object->property("result").toBool(), true);
6153 void tst_qqmlecmascript::urlPropertyWithEncoding()
6156 QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml"));
6157 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6158 QVERIFY(object != 0);
6159 object->setStringProperty("http://qt-project.org");
6161 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6162 QCOMPARE(object->urlProperty(), encoded);
6163 QCOMPARE(object->value(), 0); // Interpreting URL as string yields canonicalised version
6164 QCOMPARE(object->property("result").toBool(), true);
6168 void tst_qqmlecmascript::urlListPropertyWithEncoding()
6171 QQmlComponent component(&engine, testFileUrl("urlListProperty.qml"));
6172 QObject *object = component.create();
6173 QVERIFY(object != 0);
6174 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
6175 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
6176 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
6177 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
6178 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0);
6180 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6181 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded));
6182 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded));
6183 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6184 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6189 void tst_qqmlecmascript::dynamicString()
6191 QQmlComponent component(&engine, testFileUrl("dynamicString.qml"));
6192 QObject *object = component.create();
6193 QVERIFY(object != 0);
6194 QCOMPARE(object->property("stringProperty").toString(),
6195 QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
6198 void tst_qqmlecmascript::deleteLaterObjectMethodCall()
6200 QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml"));
6201 QObject *object = component.create();
6202 QVERIFY(object != 0);
6205 void tst_qqmlecmascript::automaticSemicolon()
6207 QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml"));
6208 QObject *object = component.create();
6209 QVERIFY(object != 0);
6212 void tst_qqmlecmascript::unaryExpression()
6214 QQmlComponent component(&engine, testFileUrl("unaryExpression.qml"));
6215 QObject *object = component.create();
6216 QVERIFY(object != 0);
6219 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
6220 void tst_qqmlecmascript::doubleEvaluate()
6222 QQmlComponent component(&engine, testFileUrl("doubleEvaluate.qml"));
6223 QObject *object = component.create();
6224 QVERIFY(object != 0);
6225 WriteCounter *wc = qobject_cast<WriteCounter *>(object);
6227 QCOMPARE(wc->count(), 1);
6229 wc->setProperty("x", 9);
6231 QCOMPARE(wc->count(), 2);
6236 static QStringList messages;
6237 static void captureMsgHandler(QtMsgType, const char *msg)
6239 messages.append(QLatin1String(msg));
6242 void tst_qqmlecmascript::nonNotifyable()
6244 QV4Compiler::enableV4(false);
6245 QQmlComponent component(&engine, testFileUrl("nonNotifyable.qml"));
6246 QV4Compiler::enableV4(true);
6248 QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
6250 QObject *object = component.create();
6251 qInstallMsgHandler(old);
6253 QVERIFY(object != 0);
6255 QString expected1 = QLatin1String("QQmlExpression: Expression ") +
6256 component.url().toString() +
6257 QLatin1String(":5 depends on non-NOTIFYable properties:");
6258 QString expected2 = QLatin1String(" ") +
6259 QLatin1String(object->metaObject()->className()) +
6260 QLatin1String("::value");
6262 QCOMPARE(messages.length(), 2);
6263 QCOMPARE(messages.at(0), expected1);
6264 QCOMPARE(messages.at(1), expected2);
6269 void tst_qqmlecmascript::forInLoop()
6271 QQmlComponent component(&engine, testFileUrl("forInLoop.qml"));
6272 QObject *object = component.create();
6273 QVERIFY(object != 0);
6275 QMetaObject::invokeMethod(object, "listProperty");
6277 QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
6278 QCOMPARE(r.size(), 3);
6279 QCOMPARE(r[0],QLatin1String("0=obj1"));
6280 QCOMPARE(r[1],QLatin1String("1=obj2"));
6281 QCOMPARE(r[2],QLatin1String("2=obj3"));
6283 //TODO: should test for in loop for other objects (such as QObjects) as well.
6288 // An object the binding depends on is deleted while the binding is still running
6289 void tst_qqmlecmascript::deleteWhileBindingRunning()
6291 QQmlComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml"));
6292 QObject *object = component.create();
6293 QVERIFY(object != 0);
6297 void tst_qqmlecmascript::qtbug_22679()
6300 object.setStringProperty(QLatin1String("Please work correctly"));
6301 engine.rootContext()->setContextProperty("contextProp", &object);
6303 QQmlComponent component(&engine, testFileUrl("qtbug_22679.qml"));
6304 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6305 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6307 QObject *o = component.create();
6309 QCOMPARE(warningsSpy.count(), 0);
6313 void tst_qqmlecmascript::qtbug_22843_data()
6315 QTest::addColumn<bool>("library");
6317 QTest::newRow("without .pragma library") << false;
6318 QTest::newRow("with .pragma library") << true;
6321 void tst_qqmlecmascript::qtbug_22843()
6323 QFETCH(bool, library);
6325 QString fileName("qtbug_22843");
6327 fileName += QLatin1String(".library");
6328 fileName += QLatin1String(".qml");
6330 QQmlComponent component(&engine, testFileUrl(fileName));
6331 QString url = component.url().toString();
6332 QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )");
6333 QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'");
6335 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6336 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6337 for (int x = 0; x < 3; ++x) {
6338 warningsSpy.clear();
6339 // For libraries, only the first import attempt should produce a
6340 // SyntaxError warning; subsequent component creation should not
6341 // attempt to reload the script.
6342 bool expectSyntaxError = !library || (x == 0);
6343 if (expectSyntaxError)
6344 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
6345 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
6346 QObject *object = component.create();
6347 QVERIFY(object != 0);
6348 QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
6354 void tst_qqmlecmascript::switchStatement()
6357 QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml"));
6358 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6359 QVERIFY(object != 0);
6361 // `object->value()' is the number of executed statements
6363 object->setStringProperty("A");
6364 QCOMPARE(object->value(), 5);
6366 object->setStringProperty("S");
6367 QCOMPARE(object->value(), 3);
6369 object->setStringProperty("D");
6370 QCOMPARE(object->value(), 3);
6372 object->setStringProperty("F");
6373 QCOMPARE(object->value(), 4);
6375 object->setStringProperty("something else");
6376 QCOMPARE(object->value(), 1);
6380 QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml"));
6381 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6382 QVERIFY(object != 0);
6384 // `object->value()' is the number of executed statements
6386 object->setStringProperty("A");
6387 QCOMPARE(object->value(), 5);
6389 object->setStringProperty("S");
6390 QCOMPARE(object->value(), 3);
6392 object->setStringProperty("D");
6393 QCOMPARE(object->value(), 3);
6395 object->setStringProperty("F");
6396 QCOMPARE(object->value(), 3);
6398 object->setStringProperty("something else");
6399 QCOMPARE(object->value(), 4);
6403 QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml"));
6404 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6405 QVERIFY(object != 0);
6407 // `object->value()' is the number of executed statements
6409 object->setStringProperty("A");
6410 QCOMPARE(object->value(), 5);
6412 object->setStringProperty("S");
6413 QCOMPARE(object->value(), 3);
6415 object->setStringProperty("D");
6416 QCOMPARE(object->value(), 3);
6418 object->setStringProperty("F");
6419 QCOMPARE(object->value(), 3);
6421 object->setStringProperty("something else");
6422 QCOMPARE(object->value(), 6);
6426 QQmlComponent component(&engine, testFileUrl("switchStatement.4.qml"));
6428 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
6429 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6431 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6432 QVERIFY(object != 0);
6434 // `object->value()' is the number of executed statements
6436 object->setStringProperty("A");
6437 QCOMPARE(object->value(), 5);
6439 object->setStringProperty("S");
6440 QCOMPARE(object->value(), 3);
6442 object->setStringProperty("D");
6443 QCOMPARE(object->value(), 3);
6445 object->setStringProperty("F");
6446 QCOMPARE(object->value(), 3);
6448 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6450 object->setStringProperty("something else");
6454 QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml"));
6455 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6456 QVERIFY(object != 0);
6458 // `object->value()' is the number of executed statements
6460 object->setStringProperty("A");
6461 QCOMPARE(object->value(), 1);
6463 object->setStringProperty("S");
6464 QCOMPARE(object->value(), 1);
6466 object->setStringProperty("D");
6467 QCOMPARE(object->value(), 1);
6469 object->setStringProperty("F");
6470 QCOMPARE(object->value(), 1);
6472 object->setStringProperty("something else");
6473 QCOMPARE(object->value(), 1);
6477 QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml"));
6478 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6479 QVERIFY(object != 0);
6481 // `object->value()' is the number of executed statements
6483 object->setStringProperty("A");
6484 QCOMPARE(object->value(), 123);
6486 object->setStringProperty("S");
6487 QCOMPARE(object->value(), 123);
6489 object->setStringProperty("D");
6490 QCOMPARE(object->value(), 321);
6492 object->setStringProperty("F");
6493 QCOMPARE(object->value(), 321);
6495 object->setStringProperty("something else");
6496 QCOMPARE(object->value(), 0);
6500 void tst_qqmlecmascript::withStatement()
6503 QQmlComponent component(&engine, testFileUrl("withStatement.1.qml"));
6504 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6505 QVERIFY(object != 0);
6507 QCOMPARE(object->value(), 123);
6511 void tst_qqmlecmascript::tryStatement()
6514 QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml"));
6515 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6516 QVERIFY(object != 0);
6518 QCOMPARE(object->value(), 123);
6522 QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml"));
6523 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6524 QVERIFY(object != 0);
6526 QCOMPARE(object->value(), 321);
6530 QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml"));
6531 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6532 QVERIFY(object != 0);
6534 QCOMPARE(object->value(), 1);
6538 QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml"));
6539 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6540 QVERIFY(object != 0);
6542 QCOMPARE(object->value(), 1);
6546 class CppInvokableWithQObjectDerived : public QObject
6550 CppInvokableWithQObjectDerived() {}
6551 ~CppInvokableWithQObjectDerived() {}
6553 Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data)
6555 MyQmlObject *obj = new MyQmlObject();
6556 obj->setStringProperty(data);
6560 Q_INVOKABLE QString getStringProperty(MyQmlObject *obj)
6562 return obj->stringProperty();
6566 void tst_qqmlecmascript::invokableWithQObjectDerived()
6568 CppInvokableWithQObjectDerived invokable;
6572 engine.rootContext()->setContextProperty("invokable", &invokable);
6574 QQmlComponent component(&engine, testFileUrl("qobjectDerivedArgument.qml"));
6576 QObject *object = component.create();
6578 QVERIFY(object != 0);
6579 QVERIFY(object->property("result").value<bool>() == true);
6585 void tst_qqmlecmascript::realTypePrecision()
6587 // Properties and signal parameters of type real should have double precision.
6588 QQmlComponent component(&engine, testFileUrl("realTypePrecision.qml"));
6589 QScopedPointer<QObject> object(component.create());
6590 QVERIFY(object != 0);
6591 QCOMPARE(object->property("test").toDouble(), 1234567890.);
6592 QCOMPARE(object->property("test2").toDouble(), 1234567890.);
6593 QCOMPARE(object->property("test3").toDouble(), 1234567890.);
6594 QCOMPARE(object->property("test4").toDouble(), 1234567890.);
6595 QCOMPARE(object->property("test5").toDouble(), 1234567890.);
6596 QCOMPARE(object->property("test6").toDouble(), 1234567890.*2);
6599 void tst_qqmlecmascript::registeredFlagMethod()
6602 QQmlComponent component(&engine, testFileUrl("registeredFlagMethod.qml"));
6603 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6604 QVERIFY(object != 0);
6606 QCOMPARE(object->buttons(), 0);
6607 emit object->basicSignal();
6608 QCOMPARE(object->buttons(), Qt::RightButton);
6614 void tst_qqmlecmascript::replaceBinding()
6617 QQmlComponent c(&engine, testFileUrl("replaceBinding.qml"));
6618 QObject *obj = c.create();
6621 QVERIFY(obj->property("success").toBool());
6625 void tst_qqmlecmascript::deleteRootObjectInCreation()
6628 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.qml"));
6629 QObject *obj = c.create();
6631 QVERIFY(obj->property("rootIndestructible").toBool());
6632 QVERIFY(!obj->property("childDestructible").toBool());
6634 QVERIFY(obj->property("childDestructible").toBool());
6638 void tst_qqmlecmascript::onDestruction()
6641 // Delete object manually to invoke the associated handlers,
6642 // prior to engine destruction.
6644 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
6645 QObject *obj = c.create();
6651 // In this case, the teardown of the engine causes deletion
6652 // of contexts and child items. This triggers the
6653 // onDestruction handler of a (previously .destroy()ed)
6654 // component instance. This shouldn't crash.
6656 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
6657 QObject *obj = c.create();
6662 struct EventProcessor : public QObject
6666 Q_INVOKABLE void process()
6668 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
6669 QCoreApplication::processEvents();
6673 void tst_qqmlecmascript::bindingSuppression()
6676 EventProcessor processor;
6677 engine.rootContext()->setContextProperty("pendingEvents", &processor);
6679 transientErrorsMsgCount = 0;
6680 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
6682 QQmlComponent c(&engine, testFileUrl("bindingSuppression.qml"));
6683 QObject *obj = c.create();
6687 qInstallMsgHandler(old);
6688 QCOMPARE(transientErrorsMsgCount, 0);
6691 QTEST_MAIN(tst_qqmlecmascript)
6693 #include "tst_qqmlecmascript.moc"