1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
41 #include <QtTest/QtTest>
42 #include <QtQml/qqmlcomponent.h>
43 #include <QtQml/qqmlengine.h>
44 #include <QtQml/qqmlexpression.h>
45 #include <QtQml/qqmlcontext.h>
46 #include <QtCore/qfileinfo.h>
47 #include <QtCore/qdebug.h>
48 #include <QtQml/private/qqmlguard_p.h>
49 #include <QtCore/qdir.h>
50 #include <QtCore/qnumeric.h>
51 #include <private/qqmlengine_p.h>
52 #include <private/qqmlvmemetaobject_p.h>
53 #include <private/qv4compiler_p.h>
54 #include "testtypes.h"
55 #include "testhttpserver.h"
56 #include "../../shared/util.h"
59 This test covers evaluation of ECMAScript expressions and bindings from within
60 QML. This does not include static QML language issues.
62 Static QML language issues are covered in qmllanguage
65 class tst_qqmlecmascript : public QQmlDataTest
69 tst_qqmlecmascript() {}
73 void assignBasicTypes();
74 void assignDate_data();
76 void exportDate_data();
78 void idShortcutInvalidates();
79 void boolPropertiesEvaluateAsBool();
81 void signalAssignment();
83 void basicExpressions();
84 void basicExpressions_data();
85 void arrayExpressions();
86 void contextPropertiesTriggerReeval();
87 void objectPropertiesTriggerReeval();
88 void deferredProperties();
89 void deferredPropertiesErrors();
90 void extensionObjects();
91 void overrideExtensionProperties();
92 void attachedProperties();
94 void valueTypeFunctions();
95 void constantsOverrideBindings();
96 void outerBindingOverridesInnerBinding();
97 void aliasPropertyAndBinding();
98 void aliasPropertyReset();
99 void nonExistentAttachedObject();
102 void signalParameterTypes();
103 void objectsCompareAsEqual();
104 void componentCreation_data();
105 void componentCreation();
106 void dynamicCreation_data();
107 void dynamicCreation();
108 void dynamicDestruction();
109 void objectToString();
110 void objectHasOwnProperty();
111 void selfDeletingBinding();
112 void extendedObjectPropertyLookup();
113 void extendedObjectPropertyLookup2();
115 void functionErrors();
116 void propertyAssignmentErrors();
117 void signalTriggeredBindings();
118 void listProperties();
119 void exceptionClearsOnReeval();
120 void exceptionSlotProducesWarning();
121 void exceptionBindingProducesWarning();
122 void compileInvalidBinding();
123 void transientErrors();
124 void shutdownErrors();
125 void compositePropertyType();
127 void undefinedResetsProperty();
128 void listToVariant();
129 void listAssignment();
130 void multiEngineObject();
131 void deletedObject();
132 void attachedPropertyScope();
133 void scriptConnect();
134 void scriptDisconnect();
136 void cppOwnershipReturnValue();
137 void ownershipCustomReturnValue();
138 void ownershipRootObject();
139 void ownershipConsistency();
140 void ownershipQmlIncubated();
141 void qlistqobjectMethods();
142 void strictlyEquals();
144 void numberAssignment();
145 void propertySplicing();
146 void signalWithUnknownTypes();
147 void signalWithJSValueInVariant_data();
148 void signalWithJSValueInVariant();
149 void signalWithJSValueInVariant_twoEngines_data();
150 void signalWithJSValueInVariant_twoEngines();
151 void signalWithQJSValue_data();
152 void signalWithQJSValue();
153 void moduleApi_data();
155 void importScripts_data();
156 void importScripts();
157 void scarceResources();
158 void scarceResources_data();
159 void scarceResources_other();
160 void propertyChangeSlots();
161 void propertyVar_data();
163 void propertyQJSValue_data();
164 void propertyQJSValue();
165 void propertyVarCpp();
166 void propertyVarOwnership();
167 void propertyVarImplicitOwnership();
168 void propertyVarReparent();
169 void propertyVarReparentNullContext();
170 void propertyVarCircular();
171 void propertyVarCircular2();
172 void propertyVarInheritance();
173 void propertyVarInheritance2();
174 void elementAssign();
175 void objectPassThroughSignals();
176 void objectConversion();
177 void booleanConversion();
178 void handleReferenceManagement();
180 void readonlyDeclaration();
181 void sequenceConversionRead();
182 void sequenceConversionWrite();
183 void sequenceConversionArray();
184 void sequenceConversionIndexes();
185 void sequenceConversionThreads();
186 void sequenceConversionBindings();
187 void sequenceConversionCopy();
188 void assignSequenceTypes();
191 void singleV8BindingDestroyedDuringEvaluation();
193 #ifndef QT_NO_WIDGETS
196 void dynamicCreationCrash();
197 void dynamicCreationOwnership();
199 void nullObjectBinding();
200 void deletedEngine();
201 void libraryScriptAssert();
202 void variantsAssignedUndefined();
204 void qtcreatorbug_1289();
205 void noSpuriousWarningsAtShutdown();
206 void canAssignNullToQObject();
207 void functionAssignment_fromBinding();
208 void functionAssignment_fromJS();
209 void functionAssignment_fromJS_data();
210 void functionAssignmentfromJS_invalid();
211 void functionAssignment_afterBinding();
214 void functionException();
219 void qobjectConnectionListExceptionHandling();
220 void nonscriptable();
222 void objectNameChangedSignal();
223 void destroyedSignal();
227 void sharedAttachedObject();
229 void writeRemovesBinding();
230 void aliasBindingsAssignCorrectly();
231 void aliasBindingsOverrideTarget();
232 void aliasWritesOverrideBindings();
233 void aliasToCompositeElement();
236 void urlPropertyWithEncoding();
237 void urlListPropertyWithEncoding();
238 void dynamicString();
240 void signalHandlers();
241 void doubleEvaluate();
243 void nonNotifyable();
244 void deleteWhileBindingRunning();
245 void callQtInvokables();
246 void invokableObjectArg();
247 void invokableObjectRet();
250 void qtbug_22843_data();
252 void rewriteMultiLineStrings();
253 void revisionErrors();
255 void invokableWithQObjectDerived();
256 void realTypePrecision();
257 void registeredFlagMethod();
258 void deleteLaterObjectMethodCall();
259 void automaticSemicolon();
260 void unaryExpression();
261 void switchStatement();
262 void withStatement();
264 void replaceBinding();
265 void deleteRootObjectInCreation();
266 void onDestruction();
267 void bindingSuppression();
268 void signalEmitted();
270 void qqmldataDestroyed();
273 void overrideDataAssert();
276 static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
280 void tst_qqmlecmascript::initTestCase()
282 QQmlDataTest::initTestCase();
285 QString dataDir(dataDirectory() + QLatin1Char('/') + QLatin1String("lib"));
286 engine.addImportPath(dataDir);
289 void tst_qqmlecmascript::assignBasicTypes()
292 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.qml"));
293 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
294 QVERIFY(object != 0);
295 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
296 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
297 QCOMPARE(object->stringProperty(), QString("Hello World!"));
298 QCOMPARE(object->uintProperty(), uint(10));
299 QCOMPARE(object->intProperty(), -19);
300 QCOMPARE((float)object->realProperty(), float(23.2));
301 QCOMPARE((float)object->doubleProperty(), float(-19.75));
302 QCOMPARE((float)object->floatProperty(), float(8.5));
303 QCOMPARE(object->colorProperty(), QColor("red"));
304 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
305 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
306 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
307 QCOMPARE(object->pointProperty(), QPoint(99,13));
308 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
309 QCOMPARE(object->sizeProperty(), QSize(99, 13));
310 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
311 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
312 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
313 QCOMPARE(object->boolProperty(), true);
314 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
315 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
316 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
320 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.2.qml"));
321 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
322 QVERIFY(object != 0);
323 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
324 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
325 QCOMPARE(object->stringProperty(), QString("Hello World!"));
326 QCOMPARE(object->uintProperty(), uint(10));
327 QCOMPARE(object->intProperty(), -19);
328 QCOMPARE((float)object->realProperty(), float(23.2));
329 QCOMPARE((float)object->doubleProperty(), float(-19.75));
330 QCOMPARE((float)object->floatProperty(), float(8.5));
331 QCOMPARE(object->colorProperty(), QColor("red"));
332 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
333 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
334 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
335 QCOMPARE(object->pointProperty(), QPoint(99,13));
336 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
337 QCOMPARE(object->sizeProperty(), QSize(99, 13));
338 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
339 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
340 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
341 QCOMPARE(object->boolProperty(), true);
342 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
343 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
344 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
349 void tst_qqmlecmascript::assignDate_data()
351 QTest::addColumn<QUrl>("source");
353 QTest::newRow("Component.onComplete JS Parse") << testFileUrl("assignDate.qml");
354 QTest::newRow("Component.onComplete JS") << testFileUrl("assignDate.1.qml");
355 QTest::newRow("Binding JS") << testFileUrl("assignDate.2.qml");
356 QTest::newRow("Binding UTC") << testFileUrl("assignDate.3.qml");
357 QTest::newRow("Binding JS UTC") << testFileUrl("assignDate.4.qml");
358 QTest::newRow("Binding UTC+2") << testFileUrl("assignDate.5.qml");
359 QTest::newRow("Binding JS UTC+2 ") << testFileUrl("assignDate.6.qml");
362 void tst_qqmlecmascript::assignDate()
364 QFETCH(QUrl, source);
366 QQmlComponent component(&engine, source);
367 QScopedPointer<QObject> obj(component.create());
368 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
369 QVERIFY(object != 0);
371 // Dates received from JS are automatically converted to local time
372 QDate expectedDate(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 0), Qt::UTC).toLocalTime().date());
373 QDateTime expectedDateTime(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 1), Qt::UTC).toLocalTime());
374 QDateTime expectedDateTime2(QDateTime(QDate(2009, 5, 12), QTime(23, 59, 59), Qt::UTC).toLocalTime());
376 QCOMPARE(object->dateProperty(), expectedDate);
377 QCOMPARE(object->dateTimeProperty(), expectedDateTime);
378 QCOMPARE(object->dateTimeProperty2(), expectedDateTime2);
379 QCOMPARE(object->boolProperty(), true);
382 void tst_qqmlecmascript::exportDate_data()
384 QTest::addColumn<QUrl>("source");
385 QTest::addColumn<QDateTime>("datetime");
387 // Verify that we can export datetime information to QML and that consumers can access
388 // the data correctly provided they know the TZ info associated with the value
390 const QDate date(2009, 5, 12);
391 const QTime early(0, 0, 1);
392 const QTime late(23, 59, 59);
393 const int offset(((11 * 60) + 30) * 60);
395 QTest::newRow("Localtime early") << testFileUrl("exportDate.qml") << QDateTime(date, early, Qt::LocalTime);
396 QTest::newRow("Localtime late") << testFileUrl("exportDate.2.qml") << QDateTime(date, late, Qt::LocalTime);
397 QTest::newRow("UTC early") << testFileUrl("exportDate.3.qml") << QDateTime(date, early, Qt::UTC);
398 QTest::newRow("UTC late") << testFileUrl("exportDate.4.qml") << QDateTime(date, late, Qt::UTC);
400 QDateTime dt(date, early, Qt::OffsetFromUTC);
401 dt.setUtcOffset(offset);
402 QTest::newRow("+11:30 early") << testFileUrl("exportDate.5.qml") << dt;
405 QDateTime dt(date, late, Qt::OffsetFromUTC);
406 dt.setUtcOffset(offset);
407 QTest::newRow("+11:30 late") << testFileUrl("exportDate.6.qml") << dt;
410 QDateTime dt(date, early, Qt::OffsetFromUTC);
411 dt.setUtcOffset(-offset);
412 QTest::newRow("-11:30 early") << testFileUrl("exportDate.7.qml") << dt;
415 QDateTime dt(date, late, Qt::OffsetFromUTC);
416 dt.setUtcOffset(-offset);
417 QTest::newRow("-11:30 late") << testFileUrl("exportDate.8.qml") << dt;
421 void tst_qqmlecmascript::exportDate()
423 QFETCH(QUrl, source);
424 QFETCH(QDateTime, datetime);
426 DateTimeExporter exporter(datetime);
429 e.rootContext()->setContextProperty("datetimeExporter", &exporter);
431 QQmlComponent component(&e, source);
432 QScopedPointer<QObject> obj(component.create());
433 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
434 QVERIFY(object != 0);
435 QCOMPARE(object->boolProperty(), true);
438 void tst_qqmlecmascript::idShortcutInvalidates()
441 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.qml"));
442 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
443 QVERIFY(object != 0);
444 QVERIFY(object->objectProperty() != 0);
445 delete object->objectProperty();
446 QVERIFY(object->objectProperty() == 0);
451 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.1.qml"));
452 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
453 QVERIFY(object != 0);
454 QVERIFY(object->objectProperty() != 0);
455 delete object->objectProperty();
456 QVERIFY(object->objectProperty() == 0);
461 void tst_qqmlecmascript::boolPropertiesEvaluateAsBool()
464 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.1.qml"));
465 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
466 QVERIFY(object != 0);
467 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
471 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.2.qml"));
472 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
473 QVERIFY(object != 0);
474 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
479 void tst_qqmlecmascript::signalAssignment()
482 QQmlComponent component(&engine, testFileUrl("signalAssignment.1.qml"));
483 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
484 QVERIFY(object != 0);
485 QCOMPARE(object->string(), QString());
486 emit object->basicSignal();
487 QCOMPARE(object->string(), QString("pass"));
492 QQmlComponent component(&engine, testFileUrl("signalAssignment.2.qml"));
493 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
494 QVERIFY(object != 0);
495 QCOMPARE(object->string(), QString());
496 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
497 QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
502 QQmlComponent component(&engine, testFileUrl("signalAssignment.3.qml"));
503 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
504 QVERIFY(object != 0);
505 QCOMPARE(object->string(), QString());
506 emit object->unnamedArgumentSignal(19, 10.25, "Hello world!");
507 QEXPECT_FAIL("", "QTBUG-24481", Continue);
508 QCOMPARE(object->string(), QString("pass 19 Hello world!"));
513 QQmlComponent component(&engine, testFileUrl("signalAssignment.4.qml"));
514 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
515 QVERIFY(object != 0);
516 QCOMPARE(object->string(), QString());
517 emit object->signalWithGlobalName(19);
518 QCOMPARE(object->string(), QString("pass 5"));
523 void tst_qqmlecmascript::methods()
526 QQmlComponent component(&engine, testFileUrl("methods.1.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(), true);
533 QCOMPARE(object->methodIntCalled(), false);
538 QQmlComponent component(&engine, testFileUrl("methods.2.qml"));
539 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
540 QVERIFY(object != 0);
541 QCOMPARE(object->methodCalled(), false);
542 QCOMPARE(object->methodIntCalled(), false);
543 emit object->basicSignal();
544 QCOMPARE(object->methodCalled(), false);
545 QCOMPARE(object->methodIntCalled(), true);
550 QQmlComponent component(&engine, testFileUrl("methods.3.qml"));
551 QObject *object = component.create();
552 QVERIFY(object != 0);
553 QCOMPARE(object->property("test").toInt(), 19);
558 QQmlComponent component(&engine, testFileUrl("methods.4.qml"));
559 QObject *object = component.create();
560 QVERIFY(object != 0);
561 QCOMPARE(object->property("test").toInt(), 19);
562 QCOMPARE(object->property("test2").toInt(), 17);
563 QCOMPARE(object->property("test3").toInt(), 16);
568 QQmlComponent component(&engine, testFileUrl("methods.5.qml"));
569 QObject *object = component.create();
570 QVERIFY(object != 0);
571 QCOMPARE(object->property("test").toInt(), 9);
576 void tst_qqmlecmascript::bindingLoop()
578 QQmlComponent component(&engine, testFileUrl("bindingLoop.qml"));
579 QString warning = component.url().toString() + ":5:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\"";
580 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
581 QObject *object = component.create();
582 QVERIFY(object != 0);
586 void tst_qqmlecmascript::basicExpressions_data()
588 QTest::addColumn<QString>("expression");
589 QTest::addColumn<QVariant>("result");
590 QTest::addColumn<bool>("nest");
592 QTest::newRow("Syntax error (self test)") << "{console.log({'a':1'}.a)}" << QVariant() << false;
593 QTest::newRow("Context property") << "a" << QVariant(1944) << false;
594 QTest::newRow("Context property") << "a" << QVariant(1944) << true;
595 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << false;
596 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << true;
597 QTest::newRow("Overridden context property") << "b" << QVariant("Milk") << false;
598 QTest::newRow("Overridden context property") << "b" << QVariant("Cow") << true;
599 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << false;
600 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << true;
601 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object2") << false;
602 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object3") << true;
603 QTest::newRow("Default object property") << "horseLegs" << QVariant(4) << false;
604 QTest::newRow("Default object property") << "antLegs" << QVariant(6) << false;
605 QTest::newRow("Default object property") << "emuLegs" << QVariant(2) << false;
606 QTest::newRow("Nested default object property") << "horseLegs" << QVariant(4) << true;
607 QTest::newRow("Nested default object property") << "antLegs" << QVariant(7) << true;
608 QTest::newRow("Nested default object property") << "emuLegs" << QVariant(2) << true;
609 QTest::newRow("Nested default object property") << "humanLegs" << QVariant(2) << true;
610 QTest::newRow("Context property override default object property") << "millipedeLegs" << QVariant(100) << true;
613 void tst_qqmlecmascript::basicExpressions()
615 QFETCH(QString, expression);
616 QFETCH(QVariant, result);
622 MyDefaultObject1 default1;
623 MyDefaultObject3 default3;
624 object1.setStringProperty("Object1");
625 object2.setStringProperty("Object2");
626 object3.setStringProperty("Object3");
628 QQmlContext context(engine.rootContext());
629 QQmlContext nestedContext(&context);
631 context.setContextObject(&default1);
632 context.setContextProperty("a", QVariant(1944));
633 context.setContextProperty("b", QVariant("Milk"));
634 context.setContextProperty("object", &object1);
635 context.setContextProperty("objectOverride", &object2);
636 nestedContext.setContextObject(&default3);
637 nestedContext.setContextProperty("b", QVariant("Cow"));
638 nestedContext.setContextProperty("objectOverride", &object3);
639 nestedContext.setContextProperty("millipedeLegs", QVariant(100));
641 MyExpression expr(nest?&nestedContext:&context, expression);
642 QCOMPARE(expr.evaluate(), result);
645 void tst_qqmlecmascript::arrayExpressions()
651 QQmlContext context(engine.rootContext());
652 context.setContextProperty("a", &obj1);
653 context.setContextProperty("b", &obj2);
654 context.setContextProperty("c", &obj3);
656 MyExpression expr(&context, "[a, b, c, 10]");
657 QVariant result = expr.evaluate();
658 QCOMPARE(result.userType(), qMetaTypeId<QList<QObject *> >());
659 QList<QObject *> list = qvariant_cast<QList<QObject *> >(result);
660 QCOMPARE(list.count(), 4);
661 QCOMPARE(list.at(0), &obj1);
662 QCOMPARE(list.at(1), &obj2);
663 QCOMPARE(list.at(2), &obj3);
664 QCOMPARE(list.at(3), (QObject *)0);
667 // Tests that modifying a context property will reevaluate expressions
668 void tst_qqmlecmascript::contextPropertiesTriggerReeval()
670 QQmlContext context(engine.rootContext());
673 MyQmlObject *object3 = new MyQmlObject;
675 object1.setStringProperty("Hello");
676 object2.setStringProperty("World");
678 context.setContextProperty("testProp", QVariant(1));
679 context.setContextProperty("testObj", &object1);
680 context.setContextProperty("testObj2", object3);
683 MyExpression expr(&context, "testProp + 1");
684 QCOMPARE(expr.changed, false);
685 QCOMPARE(expr.evaluate(), QVariant(2));
687 context.setContextProperty("testProp", QVariant(2));
688 QCOMPARE(expr.changed, true);
689 QCOMPARE(expr.evaluate(), QVariant(3));
693 MyExpression expr(&context, "testProp + testProp + testProp");
694 QCOMPARE(expr.changed, false);
695 QCOMPARE(expr.evaluate(), QVariant(6));
697 context.setContextProperty("testProp", QVariant(4));
698 QCOMPARE(expr.changed, true);
699 QCOMPARE(expr.evaluate(), QVariant(12));
703 MyExpression expr(&context, "testObj.stringProperty");
704 QCOMPARE(expr.changed, false);
705 QCOMPARE(expr.evaluate(), QVariant("Hello"));
707 context.setContextProperty("testObj", &object2);
708 QCOMPARE(expr.changed, true);
709 QCOMPARE(expr.evaluate(), QVariant("World"));
713 MyExpression expr(&context, "testObj.stringProperty /**/");
714 QCOMPARE(expr.changed, false);
715 QCOMPARE(expr.evaluate(), QVariant("World"));
717 context.setContextProperty("testObj", &object1);
718 QCOMPARE(expr.changed, true);
719 QCOMPARE(expr.evaluate(), QVariant("Hello"));
723 MyExpression expr(&context, "testObj2");
724 QCOMPARE(expr.changed, false);
725 QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3));
731 void tst_qqmlecmascript::objectPropertiesTriggerReeval()
733 QQmlContext context(engine.rootContext());
737 context.setContextProperty("testObj", &object1);
739 object1.setStringProperty(QLatin1String("Hello"));
740 object2.setStringProperty(QLatin1String("Dog"));
741 object3.setStringProperty(QLatin1String("Cat"));
744 MyExpression expr(&context, "testObj.stringProperty");
745 QCOMPARE(expr.changed, false);
746 QCOMPARE(expr.evaluate(), QVariant("Hello"));
748 object1.setStringProperty(QLatin1String("World"));
749 QCOMPARE(expr.changed, true);
750 QCOMPARE(expr.evaluate(), QVariant("World"));
754 MyExpression expr(&context, "testObj.objectProperty.stringProperty");
755 QCOMPARE(expr.changed, false);
756 QCOMPARE(expr.evaluate(), QVariant());
758 object1.setObjectProperty(&object2);
759 QCOMPARE(expr.changed, true);
760 expr.changed = false;
761 QCOMPARE(expr.evaluate(), QVariant("Dog"));
763 object1.setObjectProperty(&object3);
764 QCOMPARE(expr.changed, true);
765 expr.changed = false;
766 QCOMPARE(expr.evaluate(), QVariant("Cat"));
768 object1.setObjectProperty(0);
769 QCOMPARE(expr.changed, true);
770 expr.changed = false;
771 QCOMPARE(expr.evaluate(), QVariant());
773 object1.setObjectProperty(&object3);
774 QCOMPARE(expr.changed, true);
775 expr.changed = false;
776 QCOMPARE(expr.evaluate(), QVariant("Cat"));
778 object3.setStringProperty("Donkey");
779 QCOMPARE(expr.changed, true);
780 expr.changed = false;
781 QCOMPARE(expr.evaluate(), QVariant("Donkey"));
785 void tst_qqmlecmascript::deferredProperties()
787 QQmlComponent component(&engine, testFileUrl("deferredProperties.qml"));
788 MyDeferredObject *object =
789 qobject_cast<MyDeferredObject *>(component.create());
790 QVERIFY(object != 0);
791 QCOMPARE(object->value(), 0);
792 QVERIFY(object->objectProperty() == 0);
793 QVERIFY(object->objectProperty2() != 0);
794 qmlExecuteDeferred(object);
795 QCOMPARE(object->value(), 10);
796 QVERIFY(object->objectProperty() != 0);
797 MyQmlObject *qmlObject =
798 qobject_cast<MyQmlObject *>(object->objectProperty());
799 QVERIFY(qmlObject != 0);
800 QCOMPARE(qmlObject->value(), 10);
801 object->setValue(19);
802 QCOMPARE(qmlObject->value(), 19);
807 // Check errors on deferred properties are correctly emitted
808 void tst_qqmlecmascript::deferredPropertiesErrors()
810 QQmlComponent component(&engine, testFileUrl("deferredPropertiesErrors.qml"));
811 MyDeferredObject *object =
812 qobject_cast<MyDeferredObject *>(component.create());
813 QVERIFY(object != 0);
814 QCOMPARE(object->value(), 0);
815 QVERIFY(object->objectProperty() == 0);
816 QVERIFY(object->objectProperty2() == 0);
818 QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject*";
819 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
821 qmlExecuteDeferred(object);
826 void tst_qqmlecmascript::extensionObjects()
828 QQmlComponent component(&engine, testFileUrl("extensionObjects.qml"));
829 MyExtendedObject *object =
830 qobject_cast<MyExtendedObject *>(component.create());
831 QVERIFY(object != 0);
832 QCOMPARE(object->baseProperty(), 13);
833 QCOMPARE(object->coreProperty(), 9);
834 object->setProperty("extendedProperty", QVariant(11));
835 object->setProperty("baseExtendedProperty", QVariant(92));
836 QCOMPARE(object->coreProperty(), 11);
837 QCOMPARE(object->baseProperty(), 92);
839 MyExtendedObject *nested = qobject_cast<MyExtendedObject*>(qvariant_cast<QObject *>(object->property("nested")));
841 QCOMPARE(nested->baseProperty(), 13);
842 QCOMPARE(nested->coreProperty(), 9);
843 nested->setProperty("extendedProperty", QVariant(11));
844 nested->setProperty("baseExtendedProperty", QVariant(92));
845 QCOMPARE(nested->coreProperty(), 11);
846 QCOMPARE(nested->baseProperty(), 92);
851 void tst_qqmlecmascript::overrideExtensionProperties()
853 QQmlComponent component(&engine, testFileUrl("extensionObjectsPropertyOverride.qml"));
854 OverrideDefaultPropertyObject *object =
855 qobject_cast<OverrideDefaultPropertyObject *>(component.create());
856 QVERIFY(object != 0);
857 QVERIFY(object->secondProperty() != 0);
858 QVERIFY(object->firstProperty() == 0);
863 void tst_qqmlecmascript::attachedProperties()
866 QQmlComponent component(&engine, testFileUrl("attachedProperty.qml"));
867 QObject *object = component.create();
868 QVERIFY(object != 0);
869 QCOMPARE(object->property("a").toInt(), 19);
870 QCOMPARE(object->property("b").toInt(), 19);
871 QCOMPARE(object->property("c").toInt(), 19);
872 QCOMPARE(object->property("d").toInt(), 19);
877 QQmlComponent component(&engine, testFileUrl("attachedProperty.2.qml"));
878 QObject *object = component.create();
879 QVERIFY(object != 0);
880 QCOMPARE(object->property("a").toInt(), 26);
881 QCOMPARE(object->property("b").toInt(), 26);
882 QCOMPARE(object->property("c").toInt(), 26);
883 QCOMPARE(object->property("d").toInt(), 26);
889 QQmlComponent component(&engine, testFileUrl("writeAttachedProperty.qml"));
890 QObject *object = component.create();
891 QVERIFY(object != 0);
893 QMetaObject::invokeMethod(object, "writeValue2");
895 MyQmlAttachedObject *attached =
896 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
897 QVERIFY(attached != 0);
899 QCOMPARE(attached->value2(), 9);
904 void tst_qqmlecmascript::enums()
908 QQmlComponent component(&engine, testFileUrl("enums.1.qml"));
909 QObject *object = component.create();
910 QVERIFY(object != 0);
912 QCOMPARE(object->property("a").toInt(), 0);
913 QCOMPARE(object->property("b").toInt(), 1);
914 QCOMPARE(object->property("c").toInt(), 2);
915 QCOMPARE(object->property("d").toInt(), 3);
916 QCOMPARE(object->property("e").toInt(), 0);
917 QCOMPARE(object->property("f").toInt(), 1);
918 QCOMPARE(object->property("g").toInt(), 2);
919 QCOMPARE(object->property("h").toInt(), 3);
920 QCOMPARE(object->property("i").toInt(), 19);
921 QCOMPARE(object->property("j").toInt(), 19);
925 // Non-existent enums
927 QUrl file = testFileUrl("enums.2.qml");
928 QString w1 = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'MyEnum' for property 'MyUnregisteredEnumTypeObject::enumProperty'");
929 QString w2 = QLatin1String("QQmlExpression: Expression ") + testFileUrl("enums.2.qml").toString() + QLatin1String(":9 depends on non-NOTIFYable properties:");
930 QString w3 = QLatin1String(" MyUnregisteredEnumTypeObject::enumProperty");
931 QString w4 = file.toString() + ":7: Unable to assign [undefined] to int";
932 QString w5 = file.toString() + ":8: Unable to assign [undefined] to int";
933 QString w6 = file.toString() + ":9: Unable to assign [undefined] to int";
934 QString w7 = file.toString() + ":13: Unable to assign [undefined] to [unknown property type]";
935 QString w8 = file.toString() + ":31: Unable to assign int to [unknown property type]";
936 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
937 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
938 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
939 QTest::ignoreMessage(QtWarningMsg, qPrintable(w4));
940 QTest::ignoreMessage(QtWarningMsg, qPrintable(w5));
941 QTest::ignoreMessage(QtWarningMsg, qPrintable(w6));
942 QTest::ignoreMessage(QtWarningMsg, qPrintable(w7));
943 QTest::ignoreMessage(QtWarningMsg, qPrintable(w8));
945 QQmlComponent component(&engine, testFileUrl("enums.2.qml"));
946 QObject *object = component.create();
947 QVERIFY(object != 0);
948 QCOMPARE(object->property("a").toInt(), 0);
949 QCOMPARE(object->property("b").toInt(), 0);
950 QCOMPARE(object->property("c").toInt(), 0);
952 QString w9 = file.toString() + ":18: Error: Cannot assign JavaScript function to [unknown property type]";
953 QTest::ignoreMessage(QtWarningMsg, qPrintable(w9));
954 QMetaObject::invokeMethod(object, "testAssignmentOne");
956 QString w10 = file.toString() + ":21: Error: Cannot assign [undefined] to [unknown property type]";
957 QTest::ignoreMessage(QtWarningMsg, qPrintable(w10));
958 QMetaObject::invokeMethod(object, "testAssignmentTwo");
960 QString w11 = file.toString() + ":24: Error: Cannot assign [undefined] to [unknown property type]";
961 QTest::ignoreMessage(QtWarningMsg, qPrintable(w11));
962 QMetaObject::invokeMethod(object, "testAssignmentThree");
964 QString w12 = file.toString() + ":34: Error: Cannot assign int to an unregistered type";
965 QTest::ignoreMessage(QtWarningMsg, qPrintable(w12));
966 QMetaObject::invokeMethod(object, "testAssignmentFour");
972 QQmlComponent component(&engine, testFileUrl("enums.3.qml"));
973 QObject *object = component.create();
974 QVERIFY(object != 0);
976 // check the values are what we expect
977 QCOMPARE(object->property("a").toInt(), 4);
978 QCOMPARE(object->property("b").toInt(), 5);
979 QCOMPARE(object->property("c").toInt(), 9);
980 QCOMPARE(object->property("d").toInt(), 13);
981 QCOMPARE(object->property("e").toInt(), 2);
982 QCOMPARE(object->property("f").toInt(), 3);
983 QCOMPARE(object->property("h").toInt(), 2);
984 QCOMPARE(object->property("i").toInt(), 3);
986 // count of change signals
987 QCOMPARE(object->property("ac").toInt(), 0);
988 QCOMPARE(object->property("bc").toInt(), 0);
989 QCOMPARE(object->property("cc").toInt(), 0);
990 QCOMPARE(object->property("dc").toInt(), 0);
991 QCOMPARE(object->property("ec").toInt(), 0);
992 QCOMPARE(object->property("fc").toInt(), 0);
993 QCOMPARE(object->property("hc").toInt(), 1); // namespace -> binding
994 QCOMPARE(object->property("ic").toInt(), 1); // namespace -> binding
1000 void tst_qqmlecmascript::valueTypeFunctions()
1002 QQmlComponent component(&engine, testFileUrl("valueTypeFunctions.qml"));
1003 MyTypeObject *obj = qobject_cast<MyTypeObject*>(component.create());
1005 QCOMPARE(obj->rectProperty(), QRect(0,0,100,100));
1006 QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5));
1012 Tests that writing a constant to a property with a binding on it disables the
1015 void tst_qqmlecmascript::constantsOverrideBindings()
1019 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.1.qml"));
1020 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1021 QVERIFY(object != 0);
1023 QCOMPARE(object->property("c2").toInt(), 0);
1024 object->setProperty("c1", QVariant(9));
1025 QCOMPARE(object->property("c2").toInt(), 9);
1027 emit object->basicSignal();
1029 QCOMPARE(object->property("c2").toInt(), 13);
1030 object->setProperty("c1", QVariant(8));
1031 QCOMPARE(object->property("c2").toInt(), 13);
1036 // During construction
1038 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.2.qml"));
1039 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1040 QVERIFY(object != 0);
1042 QCOMPARE(object->property("c1").toInt(), 0);
1043 QCOMPARE(object->property("c2").toInt(), 10);
1044 object->setProperty("c1", QVariant(9));
1045 QCOMPARE(object->property("c1").toInt(), 9);
1046 QCOMPARE(object->property("c2").toInt(), 10);
1054 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.3.qml"));
1055 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1056 QVERIFY(object != 0);
1058 QCOMPARE(object->property("c2").toInt(), 0);
1059 object->setProperty("c1", QVariant(9));
1060 QCOMPARE(object->property("c2").toInt(), 9);
1062 object->setProperty("c2", QVariant(13));
1063 QCOMPARE(object->property("c2").toInt(), 13);
1064 object->setProperty("c1", QVariant(7));
1065 QCOMPARE(object->property("c1").toInt(), 7);
1066 QCOMPARE(object->property("c2").toInt(), 13);
1074 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.4.qml"));
1075 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1076 QVERIFY(object != 0);
1078 QCOMPARE(object->property("c1").toInt(), 0);
1079 QCOMPARE(object->property("c3").toInt(), 10);
1080 object->setProperty("c1", QVariant(9));
1081 QCOMPARE(object->property("c1").toInt(), 9);
1082 QCOMPARE(object->property("c3").toInt(), 10);
1089 Tests that assigning a binding to a property that already has a binding causes
1090 the original binding to be disabled.
1092 void tst_qqmlecmascript::outerBindingOverridesInnerBinding()
1094 QQmlComponent component(&engine,
1095 testFileUrl("outerBindingOverridesInnerBinding.qml"));
1096 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1097 QVERIFY(object != 0);
1099 QCOMPARE(object->property("c1").toInt(), 0);
1100 QCOMPARE(object->property("c2").toInt(), 0);
1101 QCOMPARE(object->property("c3").toInt(), 0);
1103 object->setProperty("c1", QVariant(9));
1104 QCOMPARE(object->property("c1").toInt(), 9);
1105 QCOMPARE(object->property("c2").toInt(), 0);
1106 QCOMPARE(object->property("c3").toInt(), 0);
1108 object->setProperty("c3", QVariant(8));
1109 QCOMPARE(object->property("c1").toInt(), 9);
1110 QCOMPARE(object->property("c2").toInt(), 8);
1111 QCOMPARE(object->property("c3").toInt(), 8);
1117 Access a non-existent attached object.
1119 Tests for a regression where this used to crash.
1121 void tst_qqmlecmascript::nonExistentAttachedObject()
1123 QQmlComponent component(&engine, testFileUrl("nonExistentAttachedObject.qml"));
1125 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString";
1126 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
1128 QObject *object = component.create();
1129 QVERIFY(object != 0);
1134 void tst_qqmlecmascript::scope()
1137 QQmlComponent component(&engine, testFileUrl("scope.qml"));
1138 QObject *object = component.create();
1139 QVERIFY(object != 0);
1141 QCOMPARE(object->property("test1").toInt(), 1);
1142 QCOMPARE(object->property("test2").toInt(), 2);
1143 QCOMPARE(object->property("test3").toString(), QString("1Test"));
1144 QCOMPARE(object->property("test4").toString(), QString("2Test"));
1145 QCOMPARE(object->property("test5").toInt(), 1);
1146 QCOMPARE(object->property("test6").toInt(), 1);
1147 QCOMPARE(object->property("test7").toInt(), 2);
1148 QCOMPARE(object->property("test8").toInt(), 2);
1149 QCOMPARE(object->property("test9").toInt(), 1);
1150 QCOMPARE(object->property("test10").toInt(), 3);
1156 QQmlComponent component(&engine, testFileUrl("scope.2.qml"));
1157 QObject *object = component.create();
1158 QVERIFY(object != 0);
1160 QCOMPARE(object->property("test1").toInt(), 19);
1161 QCOMPARE(object->property("test2").toInt(), 19);
1162 QCOMPARE(object->property("test3").toInt(), 14);
1163 QCOMPARE(object->property("test4").toInt(), 14);
1164 QCOMPARE(object->property("test5").toInt(), 24);
1165 QCOMPARE(object->property("test6").toInt(), 24);
1171 QQmlComponent component(&engine, testFileUrl("scope.3.qml"));
1172 QObject *object = component.create();
1173 QVERIFY(object != 0);
1175 QCOMPARE(object->property("test1").toBool(), true);
1176 QCOMPARE(object->property("test2").toBool(), true);
1177 QCOMPARE(object->property("test3").toBool(), true);
1182 // Signal argument scope
1184 QQmlComponent component(&engine, testFileUrl("scope.4.qml"));
1185 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1186 QVERIFY(object != 0);
1188 QCOMPARE(object->property("test").toInt(), 0);
1189 QCOMPARE(object->property("test2").toString(), QString());
1191 emit object->argumentSignal(13, "Argument Scope", 9, MyQmlObject::EnumValue4, Qt::RightButton);
1193 QCOMPARE(object->property("test").toInt(), 13);
1194 QCOMPARE(object->property("test2").toString(), QString("Argument Scope"));
1200 QQmlComponent component(&engine, testFileUrl("scope.5.qml"));
1201 QObject *object = component.create();
1202 QVERIFY(object != 0);
1204 QCOMPARE(object->property("test1").toBool(), true);
1205 QCOMPARE(object->property("test2").toBool(), true);
1211 QQmlComponent component(&engine, testFileUrl("scope.6.qml"));
1212 QObject *object = component.create();
1213 QVERIFY(object != 0);
1215 QCOMPARE(object->property("test").toBool(), true);
1221 // In 4.7, non-library javascript files that had no imports shared the imports of their
1222 // importing context
1223 void tst_qqmlecmascript::importScope()
1225 QQmlComponent component(&engine, testFileUrl("importScope.qml"));
1226 QObject *o = component.create();
1229 QCOMPARE(o->property("test").toInt(), 240);
1235 Tests that "any" type passes through a synthesized signal parameter. This
1236 is essentially a test of QQmlMetaType::copy()
1238 void tst_qqmlecmascript::signalParameterTypes()
1240 QQmlComponent component(&engine, testFileUrl("signalParameterTypes.qml"));
1241 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1242 QVERIFY(object != 0);
1244 emit object->basicSignal();
1246 QCOMPARE(object->property("intProperty").toInt(), 10);
1247 QCOMPARE(object->property("realProperty").toReal(), 19.2);
1248 QVERIFY(object->property("colorProperty").value<QColor>() == QColor(255, 255, 0, 255));
1249 QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255)));
1250 QVERIFY(object->property("enumProperty") == MyQmlObject::EnumValue3);
1251 QVERIFY(object->property("qtEnumProperty") == Qt::LeftButton);
1257 Test that two JS objects for the same QObject compare as equal.
1259 void tst_qqmlecmascript::objectsCompareAsEqual()
1261 QQmlComponent component(&engine, testFileUrl("objectsCompareAsEqual.qml"));
1262 QObject *object = component.create();
1263 QVERIFY(object != 0);
1265 QCOMPARE(object->property("test1").toBool(), true);
1266 QCOMPARE(object->property("test2").toBool(), true);
1267 QCOMPARE(object->property("test3").toBool(), true);
1268 QCOMPARE(object->property("test4").toBool(), true);
1269 QCOMPARE(object->property("test5").toBool(), true);
1275 Confirm bindings and alias properties can coexist.
1277 Tests for a regression where the binding would not reevaluate.
1279 void tst_qqmlecmascript::aliasPropertyAndBinding()
1281 QQmlComponent component(&engine, testFileUrl("aliasPropertyAndBinding.qml"));
1282 QObject *object = component.create();
1283 QVERIFY(object != 0);
1285 QCOMPARE(object->property("c2").toInt(), 3);
1286 QCOMPARE(object->property("c3").toInt(), 3);
1288 object->setProperty("c2", QVariant(19));
1290 QCOMPARE(object->property("c2").toInt(), 19);
1291 QCOMPARE(object->property("c3").toInt(), 19);
1297 Ensure that we can write undefined value to an alias property,
1298 and that the aliased property is reset correctly if possible.
1300 void tst_qqmlecmascript::aliasPropertyReset()
1302 QObject *object = 0;
1304 // test that a manual write (of undefined) to a resettable aliased property succeeds
1305 QQmlComponent c1(&engine, testFileUrl("aliasreset/aliasPropertyReset.1.qml"));
1306 object = c1.create();
1307 QVERIFY(object != 0);
1308 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1309 QCOMPARE(object->property("aliasIsUndefined"), QVariant(false));
1310 QMetaObject::invokeMethod(object, "resetAliased");
1311 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1312 QCOMPARE(object->property("aliasIsUndefined"), QVariant(true));
1315 // test that a manual write (of undefined) to a resettable alias property succeeds
1316 QQmlComponent c2(&engine, testFileUrl("aliasreset/aliasPropertyReset.2.qml"));
1317 object = c2.create();
1318 QVERIFY(object != 0);
1319 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1320 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false));
1321 QMetaObject::invokeMethod(object, "resetAlias");
1322 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1323 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(true));
1326 // test that an alias to a bound property works correctly
1327 QQmlComponent c3(&engine, testFileUrl("aliasreset/aliasPropertyReset.3.qml"));
1328 object = c3.create();
1329 QVERIFY(object != 0);
1330 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1331 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false));
1332 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1333 QMetaObject::invokeMethod(object, "resetAlias");
1334 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1335 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(true));
1336 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1339 // test that a manual write (of undefined) to a resettable alias property
1340 // whose aliased property's object has been deleted, does not crash.
1341 QQmlComponent c4(&engine, testFileUrl("aliasreset/aliasPropertyReset.4.qml"));
1342 object = c4.create();
1343 QVERIFY(object != 0);
1344 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1345 QObject *loader = object->findChild<QObject*>("loader");
1346 QVERIFY(loader != 0);
1348 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // deletion should have caused value unset.
1349 QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash.
1350 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1351 QMetaObject::invokeMethod(object, "setAlias"); // shouldn't crash, and shouldn't change value (since it's no longer referencing anything).
1352 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1355 // test that binding an alias property to an undefined value works correctly
1356 QQmlComponent c5(&engine, testFileUrl("aliasreset/aliasPropertyReset.5.qml"));
1357 object = c5.create();
1358 QVERIFY(object != 0);
1359 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // bound to undefined value.
1362 // test that a manual write (of undefined) to a non-resettable property fails properly
1363 QUrl url = testFileUrl("aliasreset/aliasPropertyReset.error.1.qml");
1364 QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int");
1365 QQmlComponent e1(&engine, url);
1366 object = e1.create();
1367 QVERIFY(object != 0);
1368 QCOMPARE(object->property("intAlias").value<int>(), 12);
1369 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1370 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1371 QMetaObject::invokeMethod(object, "resetAlias");
1372 QCOMPARE(object->property("intAlias").value<int>(), 12);
1373 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1377 void tst_qqmlecmascript::componentCreation_data()
1379 QTest::addColumn<QString>("method");
1380 QTest::addColumn<QString>("creationError");
1381 QTest::addColumn<QString>("createdParent");
1383 QTest::newRow("url")
1387 QTest::newRow("urlMode")
1391 QTest::newRow("urlParent")
1395 QTest::newRow("urlNullParent")
1399 QTest::newRow("urlModeParent")
1403 QTest::newRow("urlModeNullParent")
1404 << "urlModeNullParent"
1407 QTest::newRow("invalidSecondArg")
1408 << "invalidSecondArg"
1409 << ":40: Error: Qt.createComponent(): Invalid arguments"
1411 QTest::newRow("invalidThirdArg")
1412 << "invalidThirdArg"
1413 << ":45: Error: Qt.createComponent(): Invalid parent object"
1415 QTest::newRow("invalidMode")
1417 << ":50: Error: Qt.createComponent(): Invalid arguments"
1422 Test using createComponent to dynamically generate a component.
1424 void tst_qqmlecmascript::componentCreation()
1426 QFETCH(QString, method);
1427 QFETCH(QString, creationError);
1428 QFETCH(QString, createdParent);
1430 QUrl testUrl(testFileUrl("componentCreation.qml"));
1432 if (!creationError.isEmpty()) {
1433 QString warning = testUrl.toString() + creationError;
1434 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1437 QQmlComponent component(&engine, testUrl);
1438 MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create());
1439 QVERIFY(object != 0);
1441 QMetaObject::invokeMethod(object, method.toUtf8());
1442 QQmlComponent *created = object->componentProperty();
1444 if (creationError.isEmpty()) {
1447 QObject *expectedParent;
1448 if (createdParent == QLatin1String("obj")) {
1449 expectedParent = object;
1450 } else if ((createdParent == QLatin1String("null")) || createdParent.isEmpty()) {
1453 QCOMPARE(created->parent(), expectedParent);
1457 void tst_qqmlecmascript::dynamicCreation_data()
1459 QTest::addColumn<QString>("method");
1460 QTest::addColumn<QString>("createdName");
1462 QTest::newRow("One") << "createOne" << "objectOne";
1463 QTest::newRow("Two") << "createTwo" << "objectTwo";
1464 QTest::newRow("Three") << "createThree" << "objectThree";
1468 Test using createQmlObject to dynamically generate an item
1469 Also using createComponent is tested.
1471 void tst_qqmlecmascript::dynamicCreation()
1473 QFETCH(QString, method);
1474 QFETCH(QString, createdName);
1476 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
1477 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1478 QVERIFY(object != 0);
1480 QMetaObject::invokeMethod(object, method.toUtf8());
1481 QObject *created = object->objectProperty();
1483 QCOMPARE(created->objectName(), createdName);
1489 Tests the destroy function
1491 void tst_qqmlecmascript::dynamicDestruction()
1494 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.qml"));
1495 QQmlGuard<MyQmlObject> object = qobject_cast<MyQmlObject*>(component.create());
1496 QVERIFY(object != 0);
1497 QQmlGuard<QObject> createdQmlObject = 0;
1499 QMetaObject::invokeMethod(object, "create");
1500 createdQmlObject = object->objectProperty();
1501 QVERIFY(createdQmlObject);
1502 QCOMPARE(createdQmlObject->objectName(), QString("emptyObject"));
1504 QMetaObject::invokeMethod(object, "killOther");
1505 QVERIFY(createdQmlObject);
1507 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1508 QCoreApplication::processEvents();
1509 QVERIFY(createdQmlObject);
1510 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1511 if (createdQmlObject) {
1513 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1514 QCoreApplication::processEvents();
1517 QVERIFY(!createdQmlObject);
1519 QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
1520 QMetaObject::invokeMethod(object, "killMe");
1522 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1523 QCoreApplication::processEvents();
1528 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.2.qml"));
1529 QObject *o = component.create();
1532 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1534 QMetaObject::invokeMethod(o, "create");
1536 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) != 0);
1538 QMetaObject::invokeMethod(o, "destroy");
1540 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1541 QCoreApplication::processEvents();
1543 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1550 QQmlGuard<QObject> createdQmlObject = 0;
1551 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.3.qml"));
1552 QObject *o = component.create();
1554 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1555 QMetaObject::invokeMethod(o, "create");
1556 createdQmlObject = qvariant_cast<QObject*>(o->property("objectProperty"));
1557 QVERIFY(createdQmlObject);
1558 QMetaObject::invokeMethod(o, "destroy");
1559 QVERIFY(qvariant_cast<bool>(o->property("test")) == false);
1560 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1562 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1563 QCoreApplication::processEvents();
1565 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1566 QVERIFY(qvariant_cast<bool>(o->property("test")) == true);
1572 tests that id.toString() works
1574 void tst_qqmlecmascript::objectToString()
1576 QQmlComponent component(&engine, testFileUrl("qmlToString.qml"));
1577 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1578 QVERIFY(object != 0);
1579 QMetaObject::invokeMethod(object, "testToString");
1580 QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_"));
1581 QVERIFY(object->stringProperty().endsWith(", \"objName\")"));
1587 tests that id.hasOwnProperty() works
1589 void tst_qqmlecmascript::objectHasOwnProperty()
1591 QUrl url = testFileUrl("qmlHasOwnProperty.qml");
1592 QString warning1 = url.toString() + ":59: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1593 QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1594 QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1596 QQmlComponent component(&engine, url);
1597 QObject *object = component.create();
1598 QVERIFY(object != 0);
1600 // test QObjects in QML
1601 QMetaObject::invokeMethod(object, "testHasOwnPropertySuccess");
1602 QVERIFY(object->property("result").value<bool>() == true);
1603 QMetaObject::invokeMethod(object, "testHasOwnPropertyFailure");
1604 QVERIFY(object->property("result").value<bool>() == false);
1606 // now test other types in QML
1607 QObject *child = object->findChild<QObject*>("typeObj");
1608 QVERIFY(child != 0);
1609 QMetaObject::invokeMethod(child, "testHasOwnPropertySuccess");
1610 QCOMPARE(child->property("valueTypeHasOwnProperty").toBool(), true);
1611 QCOMPARE(child->property("valueTypeHasOwnProperty2").toBool(), true);
1612 QCOMPARE(child->property("variantTypeHasOwnProperty").toBool(), true);
1613 QCOMPARE(child->property("stringTypeHasOwnProperty").toBool(), true);
1614 QCOMPARE(child->property("listTypeHasOwnProperty").toBool(), true);
1615 QCOMPARE(child->property("emptyListTypeHasOwnProperty").toBool(), true);
1616 QCOMPARE(child->property("enumTypeHasOwnProperty").toBool(), true);
1617 QCOMPARE(child->property("typenameHasOwnProperty").toBool(), true);
1618 QCOMPARE(child->property("typenameHasOwnProperty2").toBool(), true);
1619 QCOMPARE(child->property("moduleApiTypeHasOwnProperty").toBool(), true);
1620 QCOMPARE(child->property("moduleApiPropertyTypeHasOwnProperty").toBool(), true);
1622 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1623 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureOne");
1624 QCOMPARE(child->property("enumNonValueHasOwnProperty").toBool(), false);
1625 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1626 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureTwo");
1627 QCOMPARE(child->property("moduleApiNonPropertyHasOwnProperty").toBool(), false);
1628 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1629 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureThree");
1630 QCOMPARE(child->property("listAtInvalidHasOwnProperty").toBool(), false);
1636 Tests bindings that indirectly cause their own deletion work.
1638 This test is best run under valgrind to ensure no invalid memory access occur.
1640 void tst_qqmlecmascript::selfDeletingBinding()
1643 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.qml"));
1644 QObject *object = component.create();
1645 QVERIFY(object != 0);
1646 object->setProperty("triggerDelete", true);
1651 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.2.qml"));
1652 QObject *object = component.create();
1653 QVERIFY(object != 0);
1654 object->setProperty("triggerDelete", true);
1660 Test that extended object properties can be accessed.
1662 This test a regression where this used to crash. The issue was specificially
1663 for extended objects that did not include a synthesized meta object (so non-root
1664 and no synthesiszed properties).
1666 void tst_qqmlecmascript::extendedObjectPropertyLookup()
1668 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup.qml"));
1669 QObject *object = component.create();
1670 QVERIFY(object != 0);
1675 Test that extended object properties can be accessed correctly.
1677 void tst_qqmlecmascript::extendedObjectPropertyLookup2()
1679 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup2.qml"));
1680 QObject *object = component.create();
1681 QVERIFY(object != 0);
1683 QVariant returnValue;
1684 QVERIFY(QMetaObject::invokeMethod(object, "getValue", Q_RETURN_ARG(QVariant, returnValue)));
1685 QCOMPARE(returnValue.toInt(), 42);
1690 Test file/lineNumbers for binding/Script errors.
1692 void tst_qqmlecmascript::scriptErrors()
1694 QQmlComponent component(&engine, testFileUrl("scriptErrors.qml"));
1695 QString url = component.url().toString();
1697 QString warning1 = url.left(url.length() - 3) + "js:2: Error: Invalid write to global property \"a\"";
1698 QString warning2 = url + ":5: ReferenceError: a is not defined";
1699 QString warning3 = url.left(url.length() - 3) + "js:4: Error: Invalid write to global property \"a\"";
1700 QString warning4 = url + ":13: ReferenceError: a is not defined";
1701 QString warning5 = url + ":11: ReferenceError: a is not defined";
1702 QString warning6 = url + ":10: Unable to assign [undefined] to int";
1703 QString warning7 = url + ":15: Error: Cannot assign to read-only property \"trueProperty\"";
1704 QString warning8 = url + ":16: Error: Cannot assign to non-existent property \"fakeProperty\"";
1706 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1707 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1708 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1709 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
1710 QTest::ignoreMessage(QtWarningMsg, warning6.toLatin1().constData());
1711 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1712 QVERIFY(object != 0);
1714 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
1715 emit object->basicSignal();
1717 QTest::ignoreMessage(QtWarningMsg, warning7.toLatin1().constData());
1718 emit object->anotherBasicSignal();
1720 QTest::ignoreMessage(QtWarningMsg, warning8.toLatin1().constData());
1721 emit object->thirdBasicSignal();
1727 Test file/lineNumbers for inline functions.
1729 void tst_qqmlecmascript::functionErrors()
1731 QQmlComponent component(&engine, testFileUrl("functionErrors.qml"));
1732 QString url = component.url().toString();
1734 QString warning = url + ":5: Error: Invalid write to global property \"a\"";
1736 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1738 QObject *object = component.create();
1739 QVERIFY(object != 0);
1742 // test that if an exception occurs while invoking js function from cpp, it is reported as expected.
1743 QQmlComponent componentTwo(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
1744 url = componentTwo.url().toString();
1745 object = componentTwo.create();
1746 QVERIFY(object != 0);
1748 QString srpname = object->property("srp_name").toString();
1750 warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srpname
1751 + QLatin1String(" is not a function");
1752 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed.
1753 QMetaObject::invokeMethod(object, "retrieveScarceResource");
1758 Test various errors that can occur when assigning a property from script
1760 void tst_qqmlecmascript::propertyAssignmentErrors()
1762 QQmlComponent component(&engine, testFileUrl("propertyAssignmentErrors.qml"));
1764 QString url = component.url().toString();
1766 QObject *object = component.create();
1767 QVERIFY(object != 0);
1769 QCOMPARE(object->property("test1").toBool(), true);
1770 QCOMPARE(object->property("test2").toBool(), true);
1776 Test bindings still work when the reeval is triggered from within
1779 void tst_qqmlecmascript::signalTriggeredBindings()
1781 QQmlComponent component(&engine, testFileUrl("signalTriggeredBindings.qml"));
1782 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1783 QVERIFY(object != 0);
1785 QCOMPARE(object->property("base").toReal(), 50.);
1786 QCOMPARE(object->property("test1").toReal(), 50.);
1787 QCOMPARE(object->property("test2").toReal(), 50.);
1789 object->basicSignal();
1791 QCOMPARE(object->property("base").toReal(), 200.);
1792 QCOMPARE(object->property("test1").toReal(), 200.);
1793 QCOMPARE(object->property("test2").toReal(), 200.);
1795 object->argumentSignal(10, QString(), 10, MyQmlObject::EnumValue4, Qt::RightButton);
1797 QCOMPARE(object->property("base").toReal(), 400.);
1798 QCOMPARE(object->property("test1").toReal(), 400.);
1799 QCOMPARE(object->property("test2").toReal(), 400.);
1805 Test that list properties can be iterated from ECMAScript
1807 void tst_qqmlecmascript::listProperties()
1809 QQmlComponent component(&engine, testFileUrl("listProperties.qml"));
1810 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1811 QVERIFY(object != 0);
1813 QCOMPARE(object->property("test1").toInt(), 21);
1814 QCOMPARE(object->property("test2").toInt(), 2);
1815 QCOMPARE(object->property("test3").toBool(), true);
1816 QCOMPARE(object->property("test4").toBool(), true);
1821 void tst_qqmlecmascript::exceptionClearsOnReeval()
1823 QQmlComponent component(&engine, testFileUrl("exceptionClearsOnReeval.qml"));
1824 QString url = component.url().toString();
1826 QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null";
1828 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1829 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1830 QVERIFY(object != 0);
1832 QCOMPARE(object->property("test").toBool(), false);
1834 MyQmlObject object2;
1835 MyQmlObject object3;
1836 object2.setObjectProperty(&object3);
1837 object->setObjectProperty(&object2);
1839 QCOMPARE(object->property("test").toBool(), true);
1844 void tst_qqmlecmascript::exceptionSlotProducesWarning()
1846 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning.qml"));
1847 QString url = component.url().toString();
1849 QString warning = component.url().toString() + ":6: Error: JS exception";
1851 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1852 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1853 QVERIFY(object != 0);
1857 void tst_qqmlecmascript::exceptionBindingProducesWarning()
1859 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning2.qml"));
1860 QString url = component.url().toString();
1862 QString warning = component.url().toString() + ":5: Error: JS exception";
1864 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1865 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1866 QVERIFY(object != 0);
1870 void tst_qqmlecmascript::compileInvalidBinding()
1872 // QTBUG-23387: ensure that invalid bindings don't cause a crash.
1873 QQmlComponent component(&engine, testFileUrl("v8bindingException.qml"));
1874 QObject *object = component.create();
1875 QVERIFY(object != 0);
1879 static int transientErrorsMsgCount = 0;
1880 static void transientErrorsMsgHandler(QtMsgType, const char *)
1882 ++transientErrorsMsgCount;
1885 // Check that transient binding errors are not displayed
1886 void tst_qqmlecmascript::transientErrors()
1889 QQmlComponent component(&engine, testFileUrl("transientErrors.qml"));
1891 transientErrorsMsgCount = 0;
1892 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1894 QObject *object = component.create();
1895 QVERIFY(object != 0);
1897 qInstallMsgHandler(old);
1899 QCOMPARE(transientErrorsMsgCount, 0);
1904 // One binding erroring multiple times, but then resolving
1906 QQmlComponent component(&engine, testFileUrl("transientErrors.2.qml"));
1908 transientErrorsMsgCount = 0;
1909 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1911 QObject *object = component.create();
1912 QVERIFY(object != 0);
1914 qInstallMsgHandler(old);
1916 QCOMPARE(transientErrorsMsgCount, 0);
1922 // Check that errors during shutdown are minimized
1923 void tst_qqmlecmascript::shutdownErrors()
1925 QQmlComponent component(&engine, testFileUrl("shutdownErrors.qml"));
1926 QObject *object = component.create();
1927 QVERIFY(object != 0);
1929 transientErrorsMsgCount = 0;
1930 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1934 qInstallMsgHandler(old);
1935 QCOMPARE(transientErrorsMsgCount, 0);
1938 void tst_qqmlecmascript::compositePropertyType()
1940 QQmlComponent component(&engine, testFileUrl("compositePropertyType.qml"));
1942 QTest::ignoreMessage(QtDebugMsg, "hello world");
1943 QObject *object = qobject_cast<QObject *>(component.create());
1948 void tst_qqmlecmascript::jsObject()
1950 QQmlComponent component(&engine, testFileUrl("jsObject.qml"));
1951 QObject *object = component.create();
1952 QVERIFY(object != 0);
1954 QCOMPARE(object->property("test").toInt(), 92);
1959 void tst_qqmlecmascript::undefinedResetsProperty()
1962 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.qml"));
1963 QObject *object = component.create();
1964 QVERIFY(object != 0);
1966 QCOMPARE(object->property("resettableProperty").toInt(), 92);
1968 object->setProperty("setUndefined", true);
1970 QCOMPARE(object->property("resettableProperty").toInt(), 13);
1972 object->setProperty("setUndefined", false);
1974 QCOMPARE(object->property("resettableProperty").toInt(), 92);
1979 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.2.qml"));
1980 QObject *object = component.create();
1981 QVERIFY(object != 0);
1983 QCOMPARE(object->property("resettableProperty").toInt(), 19);
1985 QMetaObject::invokeMethod(object, "doReset");
1987 QCOMPARE(object->property("resettableProperty").toInt(), 13);
1993 // Aliases to variant properties should work
1994 void tst_qqmlecmascript::qtbug_22464()
1996 QQmlComponent component(&engine, testFileUrl("qtbug_22464.qml"));
1997 QObject *object = component.create();
1998 QVERIFY(object != 0);
2000 QCOMPARE(object->property("test").toBool(), true);
2005 void tst_qqmlecmascript::qtbug_21580()
2007 QQmlComponent component(&engine, testFileUrl("qtbug_21580.qml"));
2009 QObject *object = component.create();
2010 QVERIFY(object != 0);
2012 QCOMPARE(object->property("test").toBool(), true);
2017 // Causes a v8 binding, but not all v8 bindings to be destroyed during evaluation
2018 void tst_qqmlecmascript::singleV8BindingDestroyedDuringEvaluation()
2020 QQmlComponent component(&engine, testFileUrl("singleV8BindingDestroyedDuringEvaluation.qml"));
2022 QObject *object = component.create();
2023 QVERIFY(object != 0);
2028 void tst_qqmlecmascript::bug1()
2030 QQmlComponent component(&engine, testFileUrl("bug.1.qml"));
2031 QObject *object = component.create();
2032 QVERIFY(object != 0);
2034 QCOMPARE(object->property("test").toInt(), 14);
2036 object->setProperty("a", 11);
2038 QCOMPARE(object->property("test").toInt(), 3);
2040 object->setProperty("b", true);
2042 QCOMPARE(object->property("test").toInt(), 9);
2047 #ifndef QT_NO_WIDGETS
2048 void tst_qqmlecmascript::bug2()
2050 QQmlComponent component(&engine);
2051 component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl());
2053 QObject *object = component.create();
2054 QVERIFY(object != 0);
2060 // Don't crash in createObject when the component has errors.
2061 void tst_qqmlecmascript::dynamicCreationCrash()
2063 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
2064 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2065 QVERIFY(object != 0);
2067 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2068 QMetaObject::invokeMethod(object, "dontCrash");
2069 QObject *created = object->objectProperty();
2070 QVERIFY(created == 0);
2075 // ownership transferred to JS, ensure that GC runs the dtor
2076 void tst_qqmlecmascript::dynamicCreationOwnership()
2079 int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
2081 // allow the engine to go out of scope too.
2083 QQmlEngine dcoEngine;
2084 QQmlComponent component(&dcoEngine, testFileUrl("dynamicCreationOwnership.qml"));
2085 QObject *object = component.create();
2086 QVERIFY(object != 0);
2087 MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo");
2088 QVERIFY(mdcdo != 0);
2089 mdcdo->setDtorCount(&dtorCount);
2091 for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
2092 QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
2094 // we do this once manually, but it should be done automatically
2095 // when the engine goes out of scope (since it should gc in dtor)
2096 QMetaObject::invokeMethod(object, "performGc");
2099 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2100 QCoreApplication::processEvents();
2106 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2107 QCoreApplication::processEvents();
2108 QCOMPARE(dtorCount, expectedDtorCount);
2111 void tst_qqmlecmascript::regExpBug()
2115 QQmlComponent component(&engine, testFileUrl("regExp.qml"));
2116 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2117 QVERIFY(object != 0);
2118 QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]"));
2124 QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString());
2125 QQmlComponent component(&engine, testFileUrl("regExp.2.qml"));
2126 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2127 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2129 QCOMPARE(component.errorString(), err);
2133 static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source)
2135 QString functionSource = QLatin1String("(function(object) { return ") +
2136 QLatin1String(source) + QLatin1String(" })");
2138 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2141 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2142 if (function.IsEmpty())
2144 v8::Handle<v8::Value> args[] = { o };
2145 function->Call(engine->global(), 1, args);
2146 return tc.HasCaught();
2149 static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o,
2150 const char *source, v8::Handle<v8::Value> result)
2152 QString functionSource = QLatin1String("(function(object) { return ") +
2153 QLatin1String(source) + QLatin1String(" })");
2155 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2158 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2159 if (function.IsEmpty())
2161 v8::Handle<v8::Value> args[] = { o };
2163 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2168 return value->StrictEquals(result);
2171 static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o,
2174 QString functionSource = QLatin1String("(function(object) { return ") +
2175 QLatin1String(source) + QLatin1String(" })");
2177 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2179 return v8::Handle<v8::Value>();
2180 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2181 if (function.IsEmpty())
2182 return v8::Handle<v8::Value>();
2183 v8::Handle<v8::Value> args[] = { o };
2185 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2188 return v8::Handle<v8::Value>();
2192 #define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
2193 #define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result)
2194 #define EVALUATE(source) evaluate(engine, object, source)
2196 void tst_qqmlecmascript::callQtInvokables()
2198 MyInvokableObject o;
2200 QQmlEngine qmlengine;
2201 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine);
2203 QV8Engine *engine = ep->v8engine();
2205 v8::HandleScope handle_scope;
2206 v8::Context::Scope scope(engine->context());
2208 v8::Local<v8::Object> object = engine->newQObject(&o)->ToObject();
2210 // Non-existent methods
2212 QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
2213 QCOMPARE(o.error(), false);
2214 QCOMPARE(o.invoked(), -1);
2215 QCOMPARE(o.actuals().count(), 0);
2218 QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)"));
2219 QCOMPARE(o.error(), false);
2220 QCOMPARE(o.invoked(), -1);
2221 QCOMPARE(o.actuals().count(), 0);
2223 // Insufficient arguments
2225 QVERIFY(EVALUATE_ERROR("object.method_int()"));
2226 QCOMPARE(o.error(), false);
2227 QCOMPARE(o.invoked(), -1);
2228 QCOMPARE(o.actuals().count(), 0);
2231 QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
2232 QCOMPARE(o.error(), false);
2233 QCOMPARE(o.invoked(), -1);
2234 QCOMPARE(o.actuals().count(), 0);
2236 // Excessive arguments
2238 QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", v8::Undefined()));
2239 QCOMPARE(o.error(), false);
2240 QCOMPARE(o.invoked(), 8);
2241 QCOMPARE(o.actuals().count(), 1);
2242 QCOMPARE(o.actuals().at(0), QVariant(10));
2245 QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", v8::Undefined()));
2246 QCOMPARE(o.error(), false);
2247 QCOMPARE(o.invoked(), 9);
2248 QCOMPARE(o.actuals().count(), 2);
2249 QCOMPARE(o.actuals().at(0), QVariant(10));
2250 QCOMPARE(o.actuals().at(1), QVariant(11));
2252 // Test return types
2254 QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", v8::Undefined()));
2255 QCOMPARE(o.error(), false);
2256 QCOMPARE(o.invoked(), 0);
2257 QCOMPARE(o.actuals().count(), 0);
2260 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", v8::Integer::New(6)));
2261 QCOMPARE(o.error(), false);
2262 QCOMPARE(o.invoked(), 1);
2263 QCOMPARE(o.actuals().count(), 0);
2266 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", v8::Number::New(19.75)));
2267 QCOMPARE(o.error(), false);
2268 QCOMPARE(o.invoked(), 2);
2269 QCOMPARE(o.actuals().count(), 0);
2273 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QPointF()");
2274 QVERIFY(!ret.IsEmpty());
2275 QCOMPARE(engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5)));
2276 QCOMPARE(o.error(), false);
2277 QCOMPARE(o.invoked(), 3);
2278 QCOMPARE(o.actuals().count(), 0);
2283 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QObject()");
2284 QCOMPARE(engine->toQObject(ret), (QObject *)&o);
2285 QCOMPARE(o.error(), false);
2286 QCOMPARE(o.invoked(), 4);
2287 QCOMPARE(o.actuals().count(), 0);
2291 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_unknown()", v8::Undefined()));
2292 QCOMPARE(o.error(), false);
2293 QCOMPARE(o.invoked(), 5);
2294 QCOMPARE(o.actuals().count(), 0);
2298 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QScriptValue()");
2299 QVERIFY(ret->IsString());
2300 QCOMPARE(engine->toString(ret), QString("Hello world"));
2301 QCOMPARE(o.error(), false);
2302 QCOMPARE(o.invoked(), 6);
2303 QCOMPARE(o.actuals().count(), 0);
2307 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks")));
2308 QCOMPARE(o.error(), false);
2309 QCOMPARE(o.invoked(), 7);
2310 QCOMPARE(o.actuals().count(), 0);
2314 QVERIFY(EVALUATE_VALUE("object.method_int(94)", v8::Undefined()));
2315 QCOMPARE(o.error(), false);
2316 QCOMPARE(o.invoked(), 8);
2317 QCOMPARE(o.actuals().count(), 1);
2318 QCOMPARE(o.actuals().at(0), QVariant(94));
2321 QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", v8::Undefined()));
2322 QCOMPARE(o.error(), false);
2323 QCOMPARE(o.invoked(), 8);
2324 QCOMPARE(o.actuals().count(), 1);
2325 QCOMPARE(o.actuals().at(0), QVariant(94));
2328 QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", v8::Undefined()));
2329 QCOMPARE(o.error(), false);
2330 QCOMPARE(o.invoked(), 8);
2331 QCOMPARE(o.actuals().count(), 1);
2332 QCOMPARE(o.actuals().at(0), QVariant(0));
2335 QVERIFY(EVALUATE_VALUE("object.method_int(null)", v8::Undefined()));
2336 QCOMPARE(o.error(), false);
2337 QCOMPARE(o.invoked(), 8);
2338 QCOMPARE(o.actuals().count(), 1);
2339 QCOMPARE(o.actuals().at(0), QVariant(0));
2342 QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", v8::Undefined()));
2343 QCOMPARE(o.error(), false);
2344 QCOMPARE(o.invoked(), 8);
2345 QCOMPARE(o.actuals().count(), 1);
2346 QCOMPARE(o.actuals().at(0), QVariant(0));
2349 QVERIFY(EVALUATE_VALUE("object.method_int(object)", v8::Undefined()));
2350 QCOMPARE(o.error(), false);
2351 QCOMPARE(o.invoked(), 8);
2352 QCOMPARE(o.actuals().count(), 1);
2353 QCOMPARE(o.actuals().at(0), QVariant(0));
2356 QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", v8::Undefined()));
2357 QCOMPARE(o.error(), false);
2358 QCOMPARE(o.invoked(), 9);
2359 QCOMPARE(o.actuals().count(), 2);
2360 QCOMPARE(o.actuals().at(0), QVariant(122));
2361 QCOMPARE(o.actuals().at(1), QVariant(9));
2364 QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", v8::Undefined()));
2365 QCOMPARE(o.error(), false);
2366 QCOMPARE(o.invoked(), 10);
2367 QCOMPARE(o.actuals().count(), 1);
2368 QCOMPARE(o.actuals().at(0), QVariant(94.3));
2371 QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", v8::Undefined()));
2372 QCOMPARE(o.error(), false);
2373 QCOMPARE(o.invoked(), 10);
2374 QCOMPARE(o.actuals().count(), 1);
2375 QCOMPARE(o.actuals().at(0), QVariant(94.3));
2378 QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", v8::Undefined()));
2379 QCOMPARE(o.error(), false);
2380 QCOMPARE(o.invoked(), 10);
2381 QCOMPARE(o.actuals().count(), 1);
2382 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2385 QVERIFY(EVALUATE_VALUE("object.method_real(null)", v8::Undefined()));
2386 QCOMPARE(o.error(), false);
2387 QCOMPARE(o.invoked(), 10);
2388 QCOMPARE(o.actuals().count(), 1);
2389 QCOMPARE(o.actuals().at(0), QVariant(0));
2392 QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", v8::Undefined()));
2393 QCOMPARE(o.error(), false);
2394 QCOMPARE(o.invoked(), 10);
2395 QCOMPARE(o.actuals().count(), 1);
2396 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2399 QVERIFY(EVALUATE_VALUE("object.method_real(object)", v8::Undefined()));
2400 QCOMPARE(o.error(), false);
2401 QCOMPARE(o.invoked(), 10);
2402 QCOMPARE(o.actuals().count(), 1);
2403 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2406 QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", v8::Undefined()));
2407 QCOMPARE(o.error(), false);
2408 QCOMPARE(o.invoked(), 11);
2409 QCOMPARE(o.actuals().count(), 1);
2410 QCOMPARE(o.actuals().at(0), QVariant("Hello world"));
2413 QVERIFY(EVALUATE_VALUE("object.method_QString(19)", v8::Undefined()));
2414 QCOMPARE(o.error(), false);
2415 QCOMPARE(o.invoked(), 11);
2416 QCOMPARE(o.actuals().count(), 1);
2417 QCOMPARE(o.actuals().at(0), QVariant("19"));
2421 QString expected = "MyInvokableObject(0x" + QString::number((quintptr)&o, 16) + ")";
2422 QVERIFY(EVALUATE_VALUE("object.method_QString(object)", v8::Undefined()));
2423 QCOMPARE(o.error(), false);
2424 QCOMPARE(o.invoked(), 11);
2425 QCOMPARE(o.actuals().count(), 1);
2426 QCOMPARE(o.actuals().at(0), QVariant(expected));
2430 QVERIFY(EVALUATE_VALUE("object.method_QString(null)", v8::Undefined()));
2431 QCOMPARE(o.error(), false);
2432 QCOMPARE(o.invoked(), 11);
2433 QCOMPARE(o.actuals().count(), 1);
2434 QCOMPARE(o.actuals().at(0), QVariant(QString()));
2437 QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", v8::Undefined()));
2438 QCOMPARE(o.error(), false);
2439 QCOMPARE(o.invoked(), 11);
2440 QCOMPARE(o.actuals().count(), 1);
2441 QCOMPARE(o.actuals().at(0), QVariant(QString()));
2444 QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", v8::Undefined()));
2445 QCOMPARE(o.error(), false);
2446 QCOMPARE(o.invoked(), 12);
2447 QCOMPARE(o.actuals().count(), 1);
2448 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2451 QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", v8::Undefined()));
2452 QCOMPARE(o.error(), false);
2453 QCOMPARE(o.invoked(), 12);
2454 QCOMPARE(o.actuals().count(), 1);
2455 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2458 QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", v8::Undefined()));
2459 QCOMPARE(o.error(), false);
2460 QCOMPARE(o.invoked(), 12);
2461 QCOMPARE(o.actuals().count(), 1);
2462 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2465 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", v8::Undefined()));
2466 QCOMPARE(o.error(), false);
2467 QCOMPARE(o.invoked(), 12);
2468 QCOMPARE(o.actuals().count(), 1);
2469 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2472 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", v8::Undefined()));
2473 QCOMPARE(o.error(), false);
2474 QCOMPARE(o.invoked(), 12);
2475 QCOMPARE(o.actuals().count(), 1);
2476 QCOMPARE(o.actuals().at(0), QVariant(QPointF(99.3, -10.2)));
2479 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", v8::Undefined()));
2480 QCOMPARE(o.error(), false);
2481 QCOMPARE(o.invoked(), 12);
2482 QCOMPARE(o.actuals().count(), 1);
2483 QCOMPARE(o.actuals().at(0), QVariant(QPointF(9, 12)));
2486 QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", v8::Undefined()));
2487 QCOMPARE(o.error(), false);
2488 QCOMPARE(o.invoked(), 13);
2489 QCOMPARE(o.actuals().count(), 1);
2490 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2493 QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", v8::Undefined()));
2494 QCOMPARE(o.error(), false);
2495 QCOMPARE(o.invoked(), 13);
2496 QCOMPARE(o.actuals().count(), 1);
2497 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2500 QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", v8::Undefined()));
2501 QCOMPARE(o.error(), false);
2502 QCOMPARE(o.invoked(), 13);
2503 QCOMPARE(o.actuals().count(), 1);
2504 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2507 QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", v8::Undefined()));
2508 QCOMPARE(o.error(), false);
2509 QCOMPARE(o.invoked(), 13);
2510 QCOMPARE(o.actuals().count(), 1);
2511 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2514 QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", v8::Undefined()));
2515 QCOMPARE(o.error(), false);
2516 QCOMPARE(o.invoked(), 13);
2517 QCOMPARE(o.actuals().count(), 1);
2518 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)&o));
2521 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined()));
2522 QCOMPARE(o.error(), false);
2523 QCOMPARE(o.invoked(), 14);
2524 QCOMPARE(o.actuals().count(), 1);
2525 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isNull());
2528 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined()));
2529 QCOMPARE(o.error(), false);
2530 QCOMPARE(o.invoked(), 14);
2531 QCOMPARE(o.actuals().count(), 1);
2532 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isUndefined());
2535 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined()));
2536 QCOMPARE(o.error(), false);
2537 QCOMPARE(o.invoked(), 14);
2538 QCOMPARE(o.actuals().count(), 1);
2539 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).strictlyEquals(QJSValue(19)));
2542 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined()));
2543 QCOMPARE(o.error(), false);
2544 QCOMPARE(o.invoked(), 14);
2545 QCOMPARE(o.actuals().count(), 1);
2546 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isArray());
2549 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined()));
2550 QCOMPARE(o.error(), false);
2551 QCOMPARE(o.invoked(), 15);
2552 QCOMPARE(o.actuals().count(), 2);
2553 QCOMPARE(o.actuals().at(0), QVariant(4));
2554 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isNull());
2557 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined()));
2558 QCOMPARE(o.error(), false);
2559 QCOMPARE(o.invoked(), 15);
2560 QCOMPARE(o.actuals().count(), 2);
2561 QCOMPARE(o.actuals().at(0), QVariant(8));
2562 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isUndefined());
2565 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined()));
2566 QCOMPARE(o.error(), false);
2567 QCOMPARE(o.invoked(), 15);
2568 QCOMPARE(o.actuals().count(), 2);
2569 QCOMPARE(o.actuals().at(0), QVariant(3));
2570 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).strictlyEquals(QJSValue(19)));
2573 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined()));
2574 QCOMPARE(o.error(), false);
2575 QCOMPARE(o.invoked(), 15);
2576 QCOMPARE(o.actuals().count(), 2);
2577 QCOMPARE(o.actuals().at(0), QVariant(44));
2578 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isArray());
2581 QVERIFY(EVALUATE_ERROR("object.method_overload()"));
2582 QCOMPARE(o.error(), false);
2583 QCOMPARE(o.invoked(), -1);
2584 QCOMPARE(o.actuals().count(), 0);
2587 QVERIFY(EVALUATE_VALUE("object.method_overload(10)", v8::Undefined()));
2588 QCOMPARE(o.error(), false);
2589 QCOMPARE(o.invoked(), 16);
2590 QCOMPARE(o.actuals().count(), 1);
2591 QCOMPARE(o.actuals().at(0), QVariant(10));
2594 QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", v8::Undefined()));
2595 QCOMPARE(o.error(), false);
2596 QCOMPARE(o.invoked(), 17);
2597 QCOMPARE(o.actuals().count(), 2);
2598 QCOMPARE(o.actuals().at(0), QVariant(10));
2599 QCOMPARE(o.actuals().at(1), QVariant(11));
2602 QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", v8::Undefined()));
2603 QCOMPARE(o.error(), false);
2604 QCOMPARE(o.invoked(), 18);
2605 QCOMPARE(o.actuals().count(), 1);
2606 QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2609 QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", v8::Undefined()));
2610 QCOMPARE(o.error(), false);
2611 QCOMPARE(o.invoked(), 19);
2612 QCOMPARE(o.actuals().count(), 1);
2613 QCOMPARE(o.actuals().at(0), QVariant(9));
2616 QVERIFY(EVALUATE_VALUE("object.method_default(10)", v8::Integer::New(19)));
2617 QCOMPARE(o.error(), false);
2618 QCOMPARE(o.invoked(), 20);
2619 QCOMPARE(o.actuals().count(), 2);
2620 QCOMPARE(o.actuals().at(0), QVariant(10));
2621 QCOMPARE(o.actuals().at(1), QVariant(19));
2624 QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", v8::Integer::New(13)));
2625 QCOMPARE(o.error(), false);
2626 QCOMPARE(o.invoked(), 20);
2627 QCOMPARE(o.actuals().count(), 2);
2628 QCOMPARE(o.actuals().at(0), QVariant(10));
2629 QCOMPARE(o.actuals().at(1), QVariant(13));
2632 QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", v8::Undefined()));
2633 QCOMPARE(o.error(), false);
2634 QCOMPARE(o.invoked(), -3);
2635 QCOMPARE(o.actuals().count(), 1);
2636 QCOMPARE(o.actuals().at(0), QVariant(9));
2639 QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", v8::Undefined()));
2640 QCOMPARE(o.error(), false);
2641 QCOMPARE(o.invoked(), 21);
2642 QCOMPARE(o.actuals().count(), 2);
2643 QCOMPARE(o.actuals().at(0), QVariant(9));
2644 QCOMPARE(o.actuals().at(1), QVariant());
2647 QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", v8::Undefined()));
2648 QCOMPARE(o.error(), false);
2649 QCOMPARE(o.invoked(), 21);
2650 QCOMPARE(o.actuals().count(), 2);
2651 QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2652 QCOMPARE(o.actuals().at(1), QVariant(QString("World")));
2655 QVERIFY(EVALUATE_VALUE("object.method_QJsonObject({foo:123})", v8::Undefined()));
2656 QCOMPARE(o.error(), false);
2657 QCOMPARE(o.invoked(), 22);
2658 QCOMPARE(o.actuals().count(), 1);
2659 QCOMPARE(qvariant_cast<QJsonObject>(o.actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2662 QVERIFY(EVALUATE_VALUE("object.method_QJsonArray([123])", v8::Undefined()));
2663 QCOMPARE(o.error(), false);
2664 QCOMPARE(o.invoked(), 23);
2665 QCOMPARE(o.actuals().count(), 1);
2666 QCOMPARE(qvariant_cast<QJsonArray>(o.actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2669 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(123)", v8::Undefined()));
2670 QCOMPARE(o.error(), false);
2671 QCOMPARE(o.invoked(), 24);
2672 QCOMPARE(o.actuals().count(), 1);
2673 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(123));
2676 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(42.35)", v8::Undefined()));
2677 QCOMPARE(o.error(), false);
2678 QCOMPARE(o.invoked(), 24);
2679 QCOMPARE(o.actuals().count(), 1);
2680 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(42.35));
2683 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue('ciao')", v8::Undefined()));
2684 QCOMPARE(o.error(), false);
2685 QCOMPARE(o.invoked(), 24);
2686 QCOMPARE(o.actuals().count(), 1);
2687 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QStringLiteral("ciao")));
2690 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(true)", v8::Undefined()));
2691 QCOMPARE(o.error(), false);
2692 QCOMPARE(o.invoked(), 24);
2693 QCOMPARE(o.actuals().count(), 1);
2694 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(true));
2697 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(false)", v8::Undefined()));
2698 QCOMPARE(o.error(), false);
2699 QCOMPARE(o.invoked(), 24);
2700 QCOMPARE(o.actuals().count(), 1);
2701 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(false));
2704 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(null)", v8::Undefined()));
2705 QCOMPARE(o.error(), false);
2706 QCOMPARE(o.invoked(), 24);
2707 QCOMPARE(o.actuals().count(), 1);
2708 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Null));
2711 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(undefined)", v8::Undefined()));
2712 QCOMPARE(o.error(), false);
2713 QCOMPARE(o.invoked(), 24);
2714 QCOMPARE(o.actuals().count(), 1);
2715 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2718 QVERIFY(EVALUATE_VALUE("object.method_overload({foo:123})", v8::Undefined()));
2719 QCOMPARE(o.error(), false);
2720 QCOMPARE(o.invoked(), 25);
2721 QCOMPARE(o.actuals().count(), 1);
2722 QCOMPARE(qvariant_cast<QJsonObject>(o.actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2725 QVERIFY(EVALUATE_VALUE("object.method_overload([123])", v8::Undefined()));
2726 QCOMPARE(o.error(), false);
2727 QCOMPARE(o.invoked(), 26);
2728 QCOMPARE(o.actuals().count(), 1);
2729 QCOMPARE(qvariant_cast<QJsonArray>(o.actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2732 QVERIFY(EVALUATE_VALUE("object.method_overload(null)", v8::Undefined()));
2733 QCOMPARE(o.error(), false);
2734 QCOMPARE(o.invoked(), 27);
2735 QCOMPARE(o.actuals().count(), 1);
2736 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Null));
2739 QVERIFY(EVALUATE_VALUE("object.method_overload(undefined)", v8::Undefined()));
2740 QCOMPARE(o.error(), false);
2741 QCOMPARE(o.invoked(), 27);
2742 QCOMPARE(o.actuals().count(), 1);
2743 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2746 // QTBUG-13047 (check that you can pass registered object types as args)
2747 void tst_qqmlecmascript::invokableObjectArg()
2749 QQmlComponent component(&engine, testFileUrl("invokableObjectArg.qml"));
2751 QObject *o = component.create();
2753 MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o);
2755 QCOMPARE(qmlobject->myinvokableObject, qmlobject);
2760 // QTBUG-13047 (check that you can return registered object types from methods)
2761 void tst_qqmlecmascript::invokableObjectRet()
2763 QQmlComponent component(&engine, testFileUrl("invokableObjectRet.qml"));
2765 QObject *o = component.create();
2767 QCOMPARE(o->property("test").toBool(), true);
2772 void tst_qqmlecmascript::listToVariant()
2774 QQmlComponent component(&engine, testFileUrl("listToVariant.qml"));
2776 MyQmlContainer container;
2778 QQmlContext context(engine.rootContext());
2779 context.setContextObject(&container);
2781 QObject *object = component.create(&context);
2782 QVERIFY(object != 0);
2784 QVariant v = object->property("test");
2785 QCOMPARE(v.userType(), qMetaTypeId<QQmlListReference>());
2786 QVERIFY(qvariant_cast<QQmlListReference>(v).object() == &container);
2792 Q_DECLARE_METATYPE(QQmlListProperty<MyQmlObject>)
2793 void tst_qqmlecmascript::listAssignment()
2795 QQmlComponent component(&engine, testFileUrl("listAssignment.qml"));
2796 QObject *obj = component.create();
2797 QCOMPARE(obj->property("list1length").toInt(), 2);
2798 QQmlListProperty<MyQmlObject> list1 = obj->property("list1").value<QQmlListProperty<MyQmlObject> >();
2799 QQmlListProperty<MyQmlObject> list2 = obj->property("list2").value<QQmlListProperty<MyQmlObject> >();
2800 QCOMPARE(list1.count(&list1), list2.count(&list2));
2801 QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0));
2802 QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1));
2807 void tst_qqmlecmascript::multiEngineObject()
2810 obj.setStringProperty("Howdy planet");
2813 e1.rootContext()->setContextProperty("thing", &obj);
2814 QQmlComponent c1(&e1, testFileUrl("multiEngineObject.qml"));
2817 e2.rootContext()->setContextProperty("thing", &obj);
2818 QQmlComponent c2(&e2, testFileUrl("multiEngineObject.qml"));
2820 QObject *o1 = c1.create();
2821 QObject *o2 = c2.create();
2823 QCOMPARE(o1->property("test").toString(), QString("Howdy planet"));
2824 QCOMPARE(o2->property("test").toString(), QString("Howdy planet"));
2830 // Test that references to QObjects are cleanup when the object is destroyed
2831 void tst_qqmlecmascript::deletedObject()
2833 QQmlComponent component(&engine, testFileUrl("deletedObject.qml"));
2835 QObject *object = component.create();
2837 QCOMPARE(object->property("test1").toBool(), true);
2838 QCOMPARE(object->property("test2").toBool(), true);
2839 QCOMPARE(object->property("test3").toBool(), true);
2840 QCOMPARE(object->property("test4").toBool(), true);
2845 void tst_qqmlecmascript::attachedPropertyScope()
2847 QQmlComponent component(&engine, testFileUrl("attachedPropertyScope.qml"));
2849 QObject *object = component.create();
2850 QVERIFY(object != 0);
2852 MyQmlAttachedObject *attached =
2853 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
2854 QVERIFY(attached != 0);
2856 QCOMPARE(object->property("value2").toInt(), 0);
2858 attached->emitMySignal();
2860 QCOMPARE(object->property("value2").toInt(), 9);
2865 void tst_qqmlecmascript::scriptConnect()
2868 QQmlComponent component(&engine, testFileUrl("scriptConnect.1.qml"));
2870 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2871 QVERIFY(object != 0);
2873 QCOMPARE(object->property("test").toBool(), false);
2874 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2875 QCOMPARE(object->property("test").toBool(), true);
2881 QQmlComponent component(&engine, testFileUrl("scriptConnect.2.qml"));
2883 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2884 QVERIFY(object != 0);
2886 QCOMPARE(object->property("test").toBool(), false);
2887 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2888 QCOMPARE(object->property("test").toBool(), true);
2894 QQmlComponent component(&engine, testFileUrl("scriptConnect.3.qml"));
2896 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2897 QVERIFY(object != 0);
2899 QCOMPARE(object->property("test").toBool(), false);
2900 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2901 QCOMPARE(object->property("test").toBool(), true);
2907 QQmlComponent component(&engine, testFileUrl("scriptConnect.4.qml"));
2909 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2910 QVERIFY(object != 0);
2912 QCOMPARE(object->methodCalled(), false);
2913 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2914 QCOMPARE(object->methodCalled(), true);
2920 QQmlComponent component(&engine, testFileUrl("scriptConnect.5.qml"));
2922 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2923 QVERIFY(object != 0);
2925 QCOMPARE(object->methodCalled(), false);
2926 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2927 QCOMPARE(object->methodCalled(), true);
2933 QQmlComponent component(&engine, testFileUrl("scriptConnect.6.qml"));
2935 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2936 QVERIFY(object != 0);
2938 QCOMPARE(object->property("test").toInt(), 0);
2939 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2940 QCOMPARE(object->property("test").toInt(), 2);
2946 void tst_qqmlecmascript::scriptDisconnect()
2949 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.1.qml"));
2951 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2952 QVERIFY(object != 0);
2954 QCOMPARE(object->property("test").toInt(), 0);
2955 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2956 QCOMPARE(object->property("test").toInt(), 1);
2957 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2958 QCOMPARE(object->property("test").toInt(), 2);
2959 emit object->basicSignal();
2960 QCOMPARE(object->property("test").toInt(), 2);
2961 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2962 QCOMPARE(object->property("test").toInt(), 2);
2968 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.2.qml"));
2970 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2971 QVERIFY(object != 0);
2973 QCOMPARE(object->property("test").toInt(), 0);
2974 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2975 QCOMPARE(object->property("test").toInt(), 1);
2976 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2977 QCOMPARE(object->property("test").toInt(), 2);
2978 emit object->basicSignal();
2979 QCOMPARE(object->property("test").toInt(), 2);
2980 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2981 QCOMPARE(object->property("test").toInt(), 2);
2987 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.3.qml"));
2989 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2990 QVERIFY(object != 0);
2992 QCOMPARE(object->property("test").toInt(), 0);
2993 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2994 QCOMPARE(object->property("test").toInt(), 1);
2995 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2996 QCOMPARE(object->property("test").toInt(), 2);
2997 emit object->basicSignal();
2998 QCOMPARE(object->property("test").toInt(), 2);
2999 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3000 QCOMPARE(object->property("test").toInt(), 3);
3005 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.4.qml"));
3007 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3008 QVERIFY(object != 0);
3010 QCOMPARE(object->property("test").toInt(), 0);
3011 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3012 QCOMPARE(object->property("test").toInt(), 1);
3013 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3014 QCOMPARE(object->property("test").toInt(), 2);
3015 emit object->basicSignal();
3016 QCOMPARE(object->property("test").toInt(), 2);
3017 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3018 QCOMPARE(object->property("test").toInt(), 3);
3024 class OwnershipObject : public QObject
3028 OwnershipObject() { object = new QObject; }
3030 QPointer<QObject> object;
3033 QObject *getObject() { return object; }
3036 void tst_qqmlecmascript::ownership()
3038 OwnershipObject own;
3039 QQmlContext *context = new QQmlContext(engine.rootContext());
3040 context->setContextObject(&own);
3043 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3045 QVERIFY(own.object != 0);
3047 QObject *object = component.create(context);
3049 engine.collectGarbage();
3051 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3052 QCoreApplication::processEvents();
3054 QVERIFY(own.object == 0);
3059 own.object = new QObject(&own);
3062 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3064 QVERIFY(own.object != 0);
3066 QObject *object = component.create(context);
3068 engine.collectGarbage();
3070 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3071 QCoreApplication::processEvents();
3073 QVERIFY(own.object != 0);
3081 class CppOwnershipReturnValue : public QObject
3085 CppOwnershipReturnValue() : value(0) {}
3086 ~CppOwnershipReturnValue() { delete value; }
3088 Q_INVOKABLE QObject *create() {
3089 value = new QObject;
3090 QQmlEngine::setObjectOwnership(value, QQmlEngine::CppOwnership);
3094 Q_INVOKABLE MyQmlObject *createQmlObject() {
3095 MyQmlObject *rv = new MyQmlObject;
3100 QPointer<QObject> value;
3104 // Test setObjectOwnership(CppOwnership) works even when there is no QQmlData
3105 void tst_qqmlecmascript::cppOwnershipReturnValue()
3107 CppOwnershipReturnValue source;
3111 engine.rootContext()->setContextProperty("source", &source);
3113 QVERIFY(source.value == 0);
3115 QQmlComponent component(&engine);
3116 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl());
3118 QObject *object = component.create();
3120 QVERIFY(object != 0);
3121 QVERIFY(source.value != 0);
3126 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3127 QCoreApplication::processEvents();
3129 QVERIFY(source.value != 0);
3133 void tst_qqmlecmascript::ownershipCustomReturnValue()
3135 CppOwnershipReturnValue source;
3139 engine.rootContext()->setContextProperty("source", &source);
3141 QVERIFY(source.value == 0);
3143 QQmlComponent component(&engine);
3144 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl());
3146 QObject *object = component.create();
3148 QVERIFY(object != 0);
3149 QVERIFY(source.value != 0);
3154 engine.collectGarbage();
3155 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3156 QCoreApplication::processEvents();
3158 QVERIFY(source.value == 0);
3161 //the return value from getObject will be JS ownership,
3162 //unless strong Cpp ownership has been set
3163 class OwnershipChangingObject : public QObject
3167 OwnershipChangingObject(): object(0) { }
3169 QPointer<QObject> object;
3172 QObject *getObject() { return object; }
3173 void setObject(QObject *obj) { object = obj; }
3176 void tst_qqmlecmascript::ownershipRootObject()
3178 OwnershipChangingObject own;
3179 QQmlContext *context = new QQmlContext(engine.rootContext());
3180 context->setContextObject(&own);
3182 QQmlComponent component(&engine, testFileUrl("ownershipRootObject.qml"));
3183 QQmlGuard<QObject> object = component.create(context);
3186 engine.collectGarbage();
3188 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3189 QCoreApplication::processEvents();
3191 QVERIFY(own.object != 0);
3197 void tst_qqmlecmascript::ownershipConsistency()
3199 OwnershipChangingObject own;
3200 QQmlContext *context = new QQmlContext(engine.rootContext());
3201 context->setContextObject(&own);
3203 QString expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":19: Error: Invalid attempt to destroy() an indestructible object");
3204 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3205 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":15: Error: Invalid attempt to destroy() an indestructible object");
3206 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3207 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":6: Error: Invalid attempt to destroy() an indestructible object");
3208 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3209 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":10: Error: Invalid attempt to destroy() an indestructible object");
3210 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3212 QQmlComponent component(&engine, testFileUrl("ownershipConsistency.qml"));
3213 QQmlGuard<QObject> object = component.create(context);
3216 engine.collectGarbage();
3218 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3219 QCoreApplication::processEvents();
3221 QVERIFY(own.object != 0);
3227 void tst_qqmlecmascript::ownershipQmlIncubated()
3229 QQmlComponent component(&engine, testFileUrl("ownershipQmlIncubated.qml"));
3230 QObject *object = component.create();
3233 QTRY_VERIFY(object->property("incubatedItem").value<QObject*>() != 0);
3235 QMetaObject::invokeMethod(object, "deleteIncubatedItem");
3237 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3238 QCoreApplication::processEvents();
3240 QVERIFY(object->property("incubatedItem").value<QObject*>() == 0);
3245 class QListQObjectMethodsObject : public QObject
3249 QListQObjectMethodsObject() {
3250 m_objects.append(new MyQmlObject());
3251 m_objects.append(new MyQmlObject());
3254 ~QListQObjectMethodsObject() {
3255 qDeleteAll(m_objects);
3259 QList<QObject *> getObjects() { return m_objects; }
3262 QList<QObject *> m_objects;
3265 // Tests that returning a QList<QObject*> from a method works
3266 void tst_qqmlecmascript::qlistqobjectMethods()
3268 QListQObjectMethodsObject obj;
3269 QQmlContext *context = new QQmlContext(engine.rootContext());
3270 context->setContextObject(&obj);
3272 QQmlComponent component(&engine, testFileUrl("qlistqobjectMethods.qml"));
3274 QObject *object = component.create(context);
3276 QCOMPARE(object->property("test").toInt(), 2);
3277 QCOMPARE(object->property("test2").toBool(), true);
3284 void tst_qqmlecmascript::strictlyEquals()
3286 QQmlComponent component(&engine, testFileUrl("strictlyEquals.qml"));
3288 QObject *object = component.create();
3289 QVERIFY(object != 0);
3291 QCOMPARE(object->property("test1").toBool(), true);
3292 QCOMPARE(object->property("test2").toBool(), true);
3293 QCOMPARE(object->property("test3").toBool(), true);
3294 QCOMPARE(object->property("test4").toBool(), true);
3295 QCOMPARE(object->property("test5").toBool(), true);
3296 QCOMPARE(object->property("test6").toBool(), true);
3297 QCOMPARE(object->property("test7").toBool(), true);
3298 QCOMPARE(object->property("test8").toBool(), true);
3303 void tst_qqmlecmascript::compiled()
3305 QQmlComponent component(&engine, testFileUrl("compiled.qml"));
3307 QObject *object = component.create();
3308 QVERIFY(object != 0);
3310 QCOMPARE(object->property("test1").toReal(), qreal(15.7));
3311 QCOMPARE(object->property("test2").toReal(), qreal(-6.7));
3312 QCOMPARE(object->property("test3").toBool(), true);
3313 QCOMPARE(object->property("test4").toBool(), false);
3314 QCOMPARE(object->property("test5").toBool(), false);
3315 QCOMPARE(object->property("test6").toBool(), true);
3317 QCOMPARE(object->property("test7").toInt(), 185);
3318 QCOMPARE(object->property("test8").toInt(), 167);
3319 QCOMPARE(object->property("test9").toBool(), true);
3320 QCOMPARE(object->property("test10").toBool(), false);
3321 QCOMPARE(object->property("test11").toBool(), false);
3322 QCOMPARE(object->property("test12").toBool(), true);
3324 QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld"));
3325 QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World"));
3326 QCOMPARE(object->property("test15").toBool(), false);
3327 QCOMPARE(object->property("test16").toBool(), true);
3329 QCOMPARE(object->property("test17").toInt(), 5);
3330 QCOMPARE(object->property("test18").toReal(), qreal(176));
3331 QCOMPARE(object->property("test19").toInt(), 7);
3332 QCOMPARE(object->property("test20").toReal(), qreal(6.7));
3333 QCOMPARE(object->property("test21").toString(), QLatin1String("6.7"));
3334 QCOMPARE(object->property("test22").toString(), QLatin1String("!"));
3335 QCOMPARE(object->property("test23").toBool(), true);
3336 QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33));
3337 QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA));
3342 // Test that numbers assigned in bindings as strings work consistently
3343 void tst_qqmlecmascript::numberAssignment()
3345 QQmlComponent component(&engine, testFileUrl("numberAssignment.qml"));
3347 QObject *object = component.create();
3348 QVERIFY(object != 0);
3350 QCOMPARE(object->property("test1"), QVariant((qreal)6.7));
3351 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3352 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3353 QCOMPARE(object->property("test3"), QVariant((qreal)6));
3354 QCOMPARE(object->property("test4"), QVariant((qreal)6));
3356 QCOMPARE(object->property("test5"), QVariant((int)7));
3357 QCOMPARE(object->property("test6"), QVariant((int)7));
3358 QCOMPARE(object->property("test7"), QVariant((int)6));
3359 QCOMPARE(object->property("test8"), QVariant((int)6));
3361 QCOMPARE(object->property("test9"), QVariant((unsigned int)7));
3362 QCOMPARE(object->property("test10"), QVariant((unsigned int)7));
3363 QCOMPARE(object->property("test11"), QVariant((unsigned int)6));
3364 QCOMPARE(object->property("test12"), QVariant((unsigned int)6));
3369 void tst_qqmlecmascript::propertySplicing()
3371 QQmlComponent component(&engine, testFileUrl("propertySplicing.qml"));
3373 QObject *object = component.create();
3374 QVERIFY(object != 0);
3376 QCOMPARE(object->property("test").toBool(), true);
3382 void tst_qqmlecmascript::signalWithUnknownTypes()
3384 QQmlComponent component(&engine, testFileUrl("signalWithUnknownTypes.qml"));
3386 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3387 QVERIFY(object != 0);
3389 MyQmlObject::MyType type;
3390 type.value = 0x8971123;
3391 emit object->signalWithUnknownType(type);
3393 MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant());
3395 QCOMPARE(result.value, type.value);
3401 void tst_qqmlecmascript::signalWithJSValueInVariant_data()
3403 QTest::addColumn<QString>("expression");
3404 QTest::addColumn<QString>("compare");
3406 QString compareStrict("(function(a, b) { return a === b; })");
3407 QTest::newRow("true") << "true" << compareStrict;
3408 QTest::newRow("undefined") << "undefined" << compareStrict;
3409 QTest::newRow("null") << "null" << compareStrict;
3410 QTest::newRow("123") << "123" << compareStrict;
3411 QTest::newRow("'ciao'") << "'ciao'" << compareStrict;
3413 QString comparePropertiesStrict(
3415 " if (typeof b != 'object')"
3417 " var props = Object.getOwnPropertyNames(b);"
3418 " for (var i = 0; i < props.length; ++i) {"
3419 " var p = props[i];"
3420 " return arguments.callee(a[p], b[p]);"
3423 QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })" << comparePropertiesStrict;
3424 QTest::newRow("[10,20,30]") << "[10,20,30]" << comparePropertiesStrict;
3427 void tst_qqmlecmascript::signalWithJSValueInVariant()
3429 QFETCH(QString, expression);
3430 QFETCH(QString, compare);
3432 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3433 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3434 QVERIFY(object != 0);
3436 QJSValue value = engine.evaluate(expression);
3437 QVERIFY(!value.isError());
3438 object->setProperty("expression", expression);
3439 object->setProperty("compare", compare);
3440 object->setProperty("pass", false);
3442 emit object->signalWithVariant(QVariant::fromValue(value));
3443 QVERIFY(object->property("pass").toBool());
3446 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines_data()
3448 signalWithJSValueInVariant_data();
3451 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines()
3453 QFETCH(QString, expression);
3454 QFETCH(QString, compare);
3456 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3457 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3458 QVERIFY(object != 0);
3461 QJSValue value = engine2.evaluate(expression);
3462 QVERIFY(!value.isError());
3463 object->setProperty("expression", expression);
3464 object->setProperty("compare", compare);
3465 object->setProperty("pass", false);
3467 QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine.");
3468 emit object->signalWithVariant(QVariant::fromValue(value));
3469 QVERIFY(!object->property("pass").toBool());
3472 void tst_qqmlecmascript::signalWithQJSValue_data()
3474 signalWithJSValueInVariant_data();
3477 void tst_qqmlecmascript::signalWithQJSValue()
3479 QFETCH(QString, expression);
3480 QFETCH(QString, compare);
3482 QQmlComponent component(&engine, testFileUrl("signalWithQJSValue.qml"));
3483 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3484 QVERIFY(object != 0);
3486 QJSValue value = engine.evaluate(expression);
3487 QVERIFY(!value.isError());
3488 object->setProperty("expression", expression);
3489 object->setProperty("compare", compare);
3490 object->setProperty("pass", false);
3492 emit object->signalWithQJSValue(value);
3494 QVERIFY(object->property("pass").toBool());
3495 QVERIFY(object->qjsvalue().strictlyEquals(value));
3498 void tst_qqmlecmascript::moduleApi_data()
3500 QTest::addColumn<QUrl>("testfile");
3501 QTest::addColumn<QString>("errorMessage");
3502 QTest::addColumn<QStringList>("warningMessages");
3503 QTest::addColumn<QStringList>("readProperties");
3504 QTest::addColumn<QVariantList>("readExpectedValues");
3505 QTest::addColumn<QStringList>("writeProperties");
3506 QTest::addColumn<QVariantList>("writeValues");
3507 QTest::addColumn<QStringList>("readBackProperties");
3508 QTest::addColumn<QVariantList>("readBackExpectedValues");
3510 QTest::newRow("qobject, register + read + method")
3511 << testFileUrl("moduleapi/qobjectModuleApi.qml")
3514 << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
3515 << "qobjectMinorVersionTest" << "qobjectMajorVersionTest" << "qobjectParentedTest")
3516 << (QVariantList() << 20 << 20 << 1 << 20 << 20 << 26)
3522 QTest::newRow("script, register + read")
3523 << testFileUrl("moduleapi/scriptModuleApi.qml")
3526 << (QStringList() << "scriptTest")
3527 << (QVariantList() << 13)
3533 QTest::newRow("qobject, caching + read")
3534 << testFileUrl("moduleapi/qobjectModuleApiCaching.qml")
3537 << (QStringList() << "existingUriTest" << "qobjectParentedTest")
3538 << (QVariantList() << 20 << 26) // 26, shouldn't have incremented to 27.
3544 QTest::newRow("script, caching + read")
3545 << testFileUrl("moduleapi/scriptModuleApiCaching.qml")
3548 << (QStringList() << "scriptTest")
3549 << (QVariantList() << 13) // 13, shouldn't have incremented to 14.
3555 QTest::newRow("qobject, writing + readonly constraints")
3556 << testFileUrl("moduleapi/qobjectModuleApiWriting.qml")
3558 << (QStringList() << QString(testFileUrl("moduleapi/qobjectModuleApiWriting.qml").toString() + QLatin1String(":15: Error: Cannot assign to read-only property \"qobjectTestProperty\"")))
3559 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3560 << (QVariantList() << 20 << 50 << 10)
3561 << (QStringList() << "firstProperty" << "secondProperty")
3562 << (QVariantList() << 30 << 30)
3563 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3564 << (QVariantList() << 20 << 30 << 30);
3566 QTest::newRow("script, writing + readonly constraints")
3567 << testFileUrl("moduleapi/scriptModuleApiWriting.qml")
3569 << (QStringList() << QString(testFileUrl("moduleapi/scriptModuleApiWriting.qml").toString() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\"")))
3570 << (QStringList() << "readBack" << "unchanged")
3571 << (QVariantList() << 13 << 42)
3572 << (QStringList() << "firstProperty" << "secondProperty")
3573 << (QVariantList() << 30 << 30)
3574 << (QStringList() << "readBack" << "unchanged")
3575 << (QVariantList() << 30 << 42);
3577 QTest::newRow("qobject module API enum values in JS")
3578 << testFileUrl("moduleapi/qobjectModuleApiEnums.qml")
3581 << (QStringList() << "enumValue" << "enumMethod")
3582 << (QVariantList() << 42 << 30)
3588 QTest::newRow("qobject, invalid major version fail")
3589 << testFileUrl("moduleapi/moduleApiMajorVersionFail.qml")
3590 << QString("QQmlComponent: Component is not ready")
3599 QTest::newRow("qobject, invalid minor version fail")
3600 << testFileUrl("moduleapi/moduleApiMinorVersionFail.qml")
3601 << QString("QQmlComponent: Component is not ready")
3610 QTest::newRow("legacy module api registration")
3611 << testFileUrl("moduleapi/qobjectModuleApiLegacy.qml")
3613 << QStringList() // warning doesn't occur in the test, but in registerTypes()
3614 << (QStringList() << "legacyModulePropertyTest" << "legacyModuleMethodTest")
3615 << (QVariantList() << 20 << 2)
3622 void tst_qqmlecmascript::moduleApi()
3624 QFETCH(QUrl, testfile);
3625 QFETCH(QString, errorMessage);
3626 QFETCH(QStringList, warningMessages);
3627 QFETCH(QStringList, readProperties);
3628 QFETCH(QVariantList, readExpectedValues);
3629 QFETCH(QStringList, writeProperties);
3630 QFETCH(QVariantList, writeValues);
3631 QFETCH(QStringList, readBackProperties);
3632 QFETCH(QVariantList, readBackExpectedValues);
3634 QQmlComponent component(&engine, testfile);
3636 if (!errorMessage.isEmpty())
3637 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3639 if (warningMessages.size())
3640 foreach (const QString &warning, warningMessages)
3641 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3643 QObject *object = component.create();
3644 if (!errorMessage.isEmpty()) {
3645 QVERIFY(object == 0);
3647 QVERIFY(object != 0);
3648 for (int i = 0; i < readProperties.size(); ++i)
3649 QCOMPARE(object->property(readProperties.at(i).toLatin1().constData()), readExpectedValues.at(i));
3650 for (int i = 0; i < writeProperties.size(); ++i)
3651 QVERIFY(object->setProperty(writeProperties.at(i).toLatin1().constData(), writeValues.at(i)));
3652 for (int i = 0; i < readBackProperties.size(); ++i)
3653 QCOMPARE(object->property(readBackProperties.at(i).toLatin1().constData()), readBackExpectedValues.at(i));
3658 void tst_qqmlecmascript::importScripts_data()
3660 QTest::addColumn<QUrl>("testfile");
3661 QTest::addColumn<QString>("errorMessage");
3662 QTest::addColumn<QStringList>("warningMessages");
3663 QTest::addColumn<QStringList>("propertyNames");
3664 QTest::addColumn<QVariantList>("propertyValues");
3666 QTest::newRow("basic functionality")
3667 << testFileUrl("jsimport/testImport.qml")
3670 << (QStringList() << QLatin1String("importedScriptStringValue")
3671 << QLatin1String("importedScriptFunctionValue")
3672 << QLatin1String("importedModuleAttachedPropertyValue")
3673 << QLatin1String("importedModuleEnumValue"))
3674 << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3679 QTest::newRow("import scoping")
3680 << testFileUrl("jsimport/testImportScoping.qml")
3683 << (QStringList() << QLatin1String("componentError"))
3684 << (QVariantList() << QVariant(5));
3686 QTest::newRow("parent scope shouldn't be inherited by import with imports")
3687 << testFileUrl("jsimportfail/failOne.qml")
3689 << (QStringList() << QString(testFileUrl("jsimportfail/failOne.qml").toString() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
3690 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3691 << (QVariantList() << QVariant(QString()));
3693 QTest::newRow("javascript imports in an import should be private to the import scope")
3694 << testFileUrl("jsimportfail/failTwo.qml")
3696 << (QStringList() << QString(testFileUrl("jsimportfail/failTwo.qml").toString() + QLatin1String(":6: ReferenceError: ImportOneJs is not defined")))
3697 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3698 << (QVariantList() << QVariant(QString()));
3700 QTest::newRow("module imports in an import should be private to the import scope")
3701 << testFileUrl("jsimportfail/failThree.qml")
3703 << (QStringList() << QString(testFileUrl("jsimportfail/failThree.qml").toString() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
3704 << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
3705 << (QVariantList() << QVariant(false));
3707 QTest::newRow("typenames in an import should be private to the import scope")
3708 << testFileUrl("jsimportfail/failFour.qml")
3710 << (QStringList() << QString(testFileUrl("jsimportfail/failFour.qml").toString() + QLatin1String(":6: ReferenceError: JsQtTest is not defined")))
3711 << (QStringList() << QLatin1String("importedModuleEnumValue"))
3712 << (QVariantList() << QVariant(0));
3714 QTest::newRow("import with imports has it's own activation scope")
3715 << testFileUrl("jsimportfail/failFive.qml")
3717 << (QStringList() << QString(testFileUrl("jsimportfail/importWithImports.js").toString() + QLatin1String(":8: ReferenceError: Component is not defined")))
3718 << (QStringList() << QLatin1String("componentError"))
3719 << (QVariantList() << QVariant(0));
3721 QTest::newRow("import pragma library script")
3722 << testFileUrl("jsimport/testImportPragmaLibrary.qml")
3725 << (QStringList() << QLatin1String("testValue"))
3726 << (QVariantList() << QVariant(31));
3728 QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3729 << testFileUrl("jsimportfail/testImportPragmaLibrary.qml")
3731 << (QStringList() << QString(testFileUrl("jsimportfail/importPragmaLibrary.js").toString() + QLatin1String(":6: ReferenceError: Component is not defined")))
3732 << (QStringList() << QLatin1String("testValue"))
3733 << (QVariantList() << QVariant(0));
3735 QTest::newRow("import pragma library script which has an import")
3736 << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
3739 << (QStringList() << QLatin1String("testValue"))
3740 << (QVariantList() << QVariant(55));
3742 QTest::newRow("import pragma library script which has a pragma library import")
3743 << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3746 << (QStringList() << QLatin1String("testValue"))
3747 << (QVariantList() << QVariant(18));
3749 QTest::newRow("import module api into js import")
3750 << testFileUrl("jsimport/testImportModuleApi.qml")
3753 << (QStringList() << QLatin1String("testValue"))
3754 << (QVariantList() << QVariant(20));
3756 QTest::newRow("import module which exports a script")
3757 << testFileUrl("jsimport/testJsImport.qml")
3760 << (QStringList() << QLatin1String("importedScriptStringValue")
3761 << QLatin1String("renamedScriptStringValue")
3762 << QLatin1String("reimportedScriptStringValue"))
3763 << (QVariantList() << QVariant(QString("Hello"))
3764 << QVariant(QString("Hello"))
3765 << QVariant(QString("Hello")));
3768 void tst_qqmlecmascript::importScripts()
3770 QFETCH(QUrl, testfile);
3771 QFETCH(QString, errorMessage);
3772 QFETCH(QStringList, warningMessages);
3773 QFETCH(QStringList, propertyNames);
3774 QFETCH(QVariantList, propertyValues);
3776 QQmlComponent component(&engine, testfile);
3778 if (!errorMessage.isEmpty())
3779 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3781 if (warningMessages.size())
3782 foreach (const QString &warning, warningMessages)
3783 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3785 QObject *object = component.create();
3786 if (!errorMessage.isEmpty()) {
3787 QVERIFY(object == 0);
3789 QVERIFY(object != 0);
3790 for (int i = 0; i < propertyNames.size(); ++i)
3791 QCOMPARE(object->property(propertyNames.at(i).toLatin1().constData()), propertyValues.at(i));
3796 void tst_qqmlecmascript::scarceResources_other()
3798 /* These tests require knowledge of state, since we test values after
3799 performing signal or function invocation. */
3801 QPixmap origPixmap(100, 100);
3802 origPixmap.fill(Qt::blue);
3803 QString srp_name, expectedWarning;
3804 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
3805 ScarceResourceObject *eo = 0;
3807 QObject *object = 0;
3809 /* property var semantics */
3811 // test that scarce resources are handled properly in signal invocation
3812 QQmlComponent varComponentTen(&engine, testFileUrl("scarceResourceSignal.var.qml"));
3813 object = varComponentTen.create();
3814 srsc = object->findChild<QObject*>("srsc");
3816 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3817 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3818 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3819 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3820 QMetaObject::invokeMethod(srsc, "testSignal");
3821 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3822 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3823 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3824 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3825 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3826 QVERIFY(srsc->property("scarceResourceCopy").isValid());
3827 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3828 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3829 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3830 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3833 // test that scarce resources are handled properly from js functions in qml files
3834 QQmlComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml"));
3835 object = varComponentEleven.create();
3836 QVERIFY(object != 0);
3837 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3838 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3839 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3840 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3841 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3842 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3843 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3844 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3845 QMetaObject::invokeMethod(object, "releaseScarceResource");
3846 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3847 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3848 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3849 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3852 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3853 QQmlComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
3854 object = varComponentTwelve.create();
3855 QVERIFY(object != 0);
3856 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3857 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3858 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3859 srp_name = object->property("srp_name").toString();
3860 expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3861 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3862 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3863 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3864 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3865 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3866 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3869 // test that if an Item which has JS ownership but has a scarce resource property is garbage collected,
3870 // that the scarce resource is removed from the engine's list of scarce resources to clean up.
3871 QQmlComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml"));
3872 object = varComponentThirteen.create();
3873 QVERIFY(object != 0);
3874 QVERIFY(!object->property("varProperty").isValid()); // not assigned yet
3875 QMetaObject::invokeMethod(object, "assignVarProperty");
3876 QVERIFY(ep->scarceResources.isEmpty()); // the scarce resource is a VME property.
3877 QMetaObject::invokeMethod(object, "deassignVarProperty");
3878 QVERIFY(ep->scarceResources.isEmpty()); // should still be empty; the resource should have been released on gc.
3881 /* property variant semantics */
3883 // test that scarce resources are handled properly in signal invocation
3884 QQmlComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml"));
3885 object = variantComponentTen.create();
3886 QVERIFY(object != 0);
3887 srsc = object->findChild<QObject*>("srsc");
3889 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3890 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3891 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3892 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3893 QMetaObject::invokeMethod(srsc, "testSignal");
3894 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3895 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3896 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3897 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3898 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3899 QVERIFY(srsc->property("scarceResourceCopy").isValid());
3900 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3901 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3902 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3903 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3906 // test that scarce resources are handled properly from js functions in qml files
3907 QQmlComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml"));
3908 object = variantComponentEleven.create();
3909 QVERIFY(object != 0);
3910 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3911 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3912 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3913 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3914 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3915 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3916 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3917 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3918 QMetaObject::invokeMethod(object, "releaseScarceResource");
3919 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3920 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3921 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3922 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3925 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3926 QQmlComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml"));
3927 object = variantComponentTwelve.create();
3928 QVERIFY(object != 0);
3929 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3930 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3931 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3932 srp_name = object->property("srp_name").toString();
3933 expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3934 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3935 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3936 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3937 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3938 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3939 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3943 void tst_qqmlecmascript::scarceResources_data()
3945 QTest::addColumn<QUrl>("qmlFile");
3946 QTest::addColumn<bool>("readDetachStatus");
3947 QTest::addColumn<bool>("expectedDetachStatus");
3948 QTest::addColumn<QStringList>("propertyNames");
3949 QTest::addColumn<QVariantList>("expectedValidity");
3950 QTest::addColumn<QVariantList>("expectedValues");
3951 QTest::addColumn<QStringList>("expectedErrors");
3953 QPixmap origPixmap(100, 100);
3954 origPixmap.fill(Qt::blue);
3956 /* property var semantics */
3958 // in the following three cases, the instance created from the component
3959 // has a property which is a copy of the scarce resource; hence, the
3960 // resource should NOT be detached prior to deletion of the object instance,
3961 // unless the resource is destroyed explicitly.
3962 QTest::newRow("var: import scarce resource copy directly")
3963 << testFileUrl("scarceResourceCopy.var.qml")
3965 << false // won't be detached, because assigned to property and not explicitly released
3966 << (QStringList() << QLatin1String("scarceResourceCopy"))
3967 << (QList<QVariant>() << true)
3968 << (QList<QVariant>() << origPixmap)
3971 QTest::newRow("var: import scarce resource copy from JS")
3972 << testFileUrl("scarceResourceCopyFromJs.var.qml")
3974 << false // won't be detached, because assigned to property and not explicitly released
3975 << (QStringList() << QLatin1String("scarceResourceCopy"))
3976 << (QList<QVariant>() << true)
3977 << (QList<QVariant>() << origPixmap)
3980 QTest::newRow("var: import released scarce resource copy from JS")
3981 << testFileUrl("scarceResourceDestroyedCopy.var.qml")
3983 << true // explicitly released, so it will be detached
3984 << (QStringList() << QLatin1String("scarceResourceCopy"))
3985 << (QList<QVariant>() << false)
3986 << (QList<QVariant>() << QVariant())
3989 // in the following three cases, no other copy should exist in memory,
3990 // and so it should be detached (unless explicitly preserved).
3991 QTest::newRow("var: import auto-release SR from JS in binding side-effect")
3992 << testFileUrl("scarceResourceTest.var.qml")
3994 << true // auto released, so it will be detached
3995 << (QStringList() << QLatin1String("scarceResourceTest"))
3996 << (QList<QVariant>() << true)
3997 << (QList<QVariant>() << QVariant(100))
3999 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4000 << testFileUrl("scarceResourceTestPreserve.var.qml")
4002 << false // won't be detached because we explicitly preserve it
4003 << (QStringList() << QLatin1String("scarceResourceTest"))
4004 << (QList<QVariant>() << true)
4005 << (QList<QVariant>() << QVariant(100))
4007 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4008 << testFileUrl("scarceResourceTestMultiple.var.qml")
4010 << true // will be detached because all resources were released manually or automatically.
4011 << (QStringList() << QLatin1String("scarceResourceTest"))
4012 << (QList<QVariant>() << true)
4013 << (QList<QVariant>() << QVariant(100))
4016 // In the following three cases, test that scarce resources are handled
4017 // correctly for imports.
4018 QTest::newRow("var: import with no binding")
4019 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4020 << false // cannot check detach status.
4023 << QList<QVariant>()
4024 << QList<QVariant>()
4026 QTest::newRow("var: import with binding without explicit preserve")
4027 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4030 << (QStringList() << QLatin1String("scarceResourceCopy"))
4031 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4032 << (QList<QVariant>() << QVariant())
4034 QTest::newRow("var: import with explicit release after binding evaluation")
4035 << testFileUrl("scarceResourceCopyImport.var.qml")
4038 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4039 << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated.
4040 << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true))
4042 QTest::newRow("var: import with different js objects")
4043 << testFileUrl("scarceResourceCopyImportDifferent.var.qml")
4046 << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4047 << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4048 << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false))
4050 QTest::newRow("var: import with different js objects and explicit release")
4051 << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml")
4054 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4055 << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4056 << (QList<QVariant>() << QVariant(origPixmap) << QVariant())
4058 QTest::newRow("var: import with same js objects and explicit release")
4059 << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml")
4062 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4063 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4064 << (QList<QVariant>() << QVariant() << QVariant())
4066 QTest::newRow("var: binding with same js objects and explicit release")
4067 << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml")
4070 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4071 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4072 << (QList<QVariant>() << QVariant() << QVariant())
4076 /* property variant semantics */
4078 // in the following three cases, the instance created from the component
4079 // has a property which is a copy of the scarce resource; hence, the
4080 // resource should NOT be detached prior to deletion of the object instance,
4081 // unless the resource is destroyed explicitly.
4082 QTest::newRow("variant: import scarce resource copy directly")
4083 << testFileUrl("scarceResourceCopy.variant.qml")
4085 << false // won't be detached, because assigned to property and not explicitly released
4086 << (QStringList() << QLatin1String("scarceResourceCopy"))
4087 << (QList<QVariant>() << true)
4088 << (QList<QVariant>() << origPixmap)
4091 QTest::newRow("variant: import scarce resource copy from JS")
4092 << testFileUrl("scarceResourceCopyFromJs.variant.qml")
4094 << false // won't be detached, because assigned to property and not explicitly released
4095 << (QStringList() << QLatin1String("scarceResourceCopy"))
4096 << (QList<QVariant>() << true)
4097 << (QList<QVariant>() << origPixmap)
4100 QTest::newRow("variant: import released scarce resource copy from JS")
4101 << testFileUrl("scarceResourceDestroyedCopy.variant.qml")
4103 << true // explicitly released, so it will be detached
4104 << (QStringList() << QLatin1String("scarceResourceCopy"))
4105 << (QList<QVariant>() << false)
4106 << (QList<QVariant>() << QVariant())
4109 // in the following three cases, no other copy should exist in memory,
4110 // and so it should be detached (unless explicitly preserved).
4111 QTest::newRow("variant: import auto-release SR from JS in binding side-effect")
4112 << testFileUrl("scarceResourceTest.variant.qml")
4114 << true // auto released, so it will be detached
4115 << (QStringList() << QLatin1String("scarceResourceTest"))
4116 << (QList<QVariant>() << true)
4117 << (QList<QVariant>() << QVariant(100))
4119 QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect")
4120 << testFileUrl("scarceResourceTestPreserve.variant.qml")
4122 << false // won't be detached because we explicitly preserve it
4123 << (QStringList() << QLatin1String("scarceResourceTest"))
4124 << (QList<QVariant>() << true)
4125 << (QList<QVariant>() << QVariant(100))
4127 QTest::newRow("variant: import multiple scarce resources")
4128 << testFileUrl("scarceResourceTestMultiple.variant.qml")
4130 << true // will be detached because all resources were released manually or automatically.
4131 << (QStringList() << QLatin1String("scarceResourceTest"))
4132 << (QList<QVariant>() << true)
4133 << (QList<QVariant>() << QVariant(100))
4136 // In the following three cases, test that scarce resources are handled
4137 // correctly for imports.
4138 QTest::newRow("variant: import with no binding")
4139 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4140 << false // cannot check detach status.
4143 << QList<QVariant>()
4144 << QList<QVariant>()
4146 QTest::newRow("variant: import with binding without explicit preserve")
4147 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4150 << (QStringList() << QLatin1String("scarceResourceCopy"))
4151 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4152 << (QList<QVariant>() << QVariant())
4154 QTest::newRow("variant: import with explicit release after binding evaluation")
4155 << testFileUrl("scarceResourceCopyImport.variant.qml")
4158 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo"))
4159 << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies.
4160 << (QList<QVariant>() << origPixmap << origPixmap << QVariant())
4164 void tst_qqmlecmascript::scarceResources()
4166 QFETCH(QUrl, qmlFile);
4167 QFETCH(bool, readDetachStatus);
4168 QFETCH(bool, expectedDetachStatus);
4169 QFETCH(QStringList, propertyNames);
4170 QFETCH(QVariantList, expectedValidity);
4171 QFETCH(QVariantList, expectedValues);
4172 QFETCH(QStringList, expectedErrors);
4174 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4175 ScarceResourceObject *eo = 0;
4176 QObject *object = 0;
4178 QQmlComponent c(&engine, qmlFile);
4179 object = c.create();
4180 QVERIFY(object != 0);
4181 for (int i = 0; i < propertyNames.size(); ++i) {
4182 QString prop = propertyNames.at(i);
4183 bool validity = expectedValidity.at(i).toBool();
4184 QVariant value = expectedValues.at(i);
4186 QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity);
4187 if (value.type() == QVariant::Int) {
4188 QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt());
4189 } else if (value.type() == QVariant::Pixmap) {
4190 QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>());
4194 if (readDetachStatus) {
4195 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4196 QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
4199 QVERIFY(ep->scarceResources.isEmpty());
4203 void tst_qqmlecmascript::propertyChangeSlots()
4205 // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
4206 QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml"));
4207 QObject *object = component.create();
4208 QVERIFY(object != 0);
4211 // ensure that invalid property names fail properly.
4212 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4213 QQmlComponent e1(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.1.qml"));
4214 QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
4215 QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
4216 object = e1.create();
4217 QVERIFY(object == 0);
4220 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4221 QQmlComponent e2(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.2.qml"));
4222 expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
4223 QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
4224 object = e2.create();
4225 QVERIFY(object == 0);
4228 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4229 QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml"));
4230 expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
4231 QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
4232 object = e3.create();
4233 QVERIFY(object == 0);
4236 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4237 QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml"));
4238 expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
4239 QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
4240 object = e4.create();
4241 QVERIFY(object == 0);
4245 void tst_qqmlecmascript::propertyVar_data()
4247 QTest::addColumn<QUrl>("qmlFile");
4250 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyVar.1.qml");
4251 QTest::newRow("non-bindable object changed") << testFileUrl("propertyVar.2.qml");
4252 QTest::newRow("primitive changed") << testFileUrl("propertyVar.3.qml");
4253 QTest::newRow("javascript array modification") << testFileUrl("propertyVar.4.qml");
4254 QTest::newRow("javascript map modification") << testFileUrl("propertyVar.5.qml");
4255 QTest::newRow("javascript array assignment") << testFileUrl("propertyVar.6.qml");
4256 QTest::newRow("javascript map assignment") << testFileUrl("propertyVar.7.qml");
4257 QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml");
4258 QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml");
4259 QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml");
4260 QTest::newRow("javascript function assignment") << testFileUrl("propertyVar.11.qml");
4261 QTest::newRow("javascript special assignment") << testFileUrl("propertyVar.12.qml");
4262 QTest::newRow("declarative binding assignment") << testFileUrl("propertyVar.13.qml");
4263 QTest::newRow("imperative binding assignment") << testFileUrl("propertyVar.14.qml");
4264 QTest::newRow("stored binding assignment") << testFileUrl("propertyVar.15.qml");
4265 QTest::newRow("function expression binding assignment") << testFileUrl("propertyVar.16.qml");
4268 void tst_qqmlecmascript::propertyVar()
4270 QFETCH(QUrl, qmlFile);
4272 QQmlComponent component(&engine, qmlFile);
4273 QObject *object = component.create();
4274 QVERIFY(object != 0);
4276 QCOMPARE(object->property("test").toBool(), true);
4281 void tst_qqmlecmascript::propertyQJSValue_data()
4283 QTest::addColumn<QUrl>("qmlFile");
4286 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyQJSValue.1.qml");
4287 QTest::newRow("non-bindable object changed") << testFileUrl("propertyQJSValue.2.qml");
4288 QTest::newRow("primitive changed") << testFileUrl("propertyQJSValue.3.qml");
4289 QTest::newRow("javascript array modification") << testFileUrl("propertyQJSValue.4.qml");
4290 QTest::newRow("javascript map modification") << testFileUrl("propertyQJSValue.5.qml");
4291 QTest::newRow("javascript array assignment") << testFileUrl("propertyQJSValue.6.qml");
4292 QTest::newRow("javascript map assignment") << testFileUrl("propertyQJSValue.7.qml");
4293 QTest::newRow("literal property assignment") << testFileUrl("propertyQJSValue.8.qml");
4294 QTest::newRow("qobject property assignment") << testFileUrl("propertyQJSValue.9.qml");
4295 QTest::newRow("base class var property assignment") << testFileUrl("propertyQJSValue.10.qml");
4296 QTest::newRow("javascript function assignment") << testFileUrl("propertyQJSValue.11.qml");
4297 QTest::newRow("javascript special assignment") << testFileUrl("propertyQJSValue.12.qml");
4298 QTest::newRow("declarative binding assignment") << testFileUrl("propertyQJSValue.13.qml");
4299 QTest::newRow("imperative binding assignment") << testFileUrl("propertyQJSValue.14.qml");
4300 QTest::newRow("stored binding assignment") << testFileUrl("propertyQJSValue.15.qml");
4301 QTest::newRow("javascript function binding") << testFileUrl("propertyQJSValue.16.qml");
4303 QTest::newRow("reset property") << testFileUrl("propertyQJSValue.reset.qml");
4304 QTest::newRow("reset property in binding") << testFileUrl("propertyQJSValue.bindingreset.qml");
4307 void tst_qqmlecmascript::propertyQJSValue()
4309 QFETCH(QUrl, qmlFile);
4311 QQmlComponent component(&engine, qmlFile);
4312 QObject *object = component.create();
4313 QVERIFY(object != 0);
4315 QCOMPARE(object->property("test").toBool(), true);
4320 // Tests that we can write QVariant values to var properties from C++
4321 void tst_qqmlecmascript::propertyVarCpp()
4323 QObject *object = 0;
4325 // ensure that writing to and reading from a var property from cpp works as required.
4326 // Literal values stored in var properties can be read and written as QVariants
4327 // of a specific type, whereas object values are read as QVariantMaps.
4328 QQmlComponent component(&engine, testFileUrl("propertyVarCpp.qml"));
4329 object = component.create();
4330 QVERIFY(object != 0);
4331 // assign int to property var that currently has int assigned
4332 QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
4333 QCOMPARE(object->property("varBound"), QVariant(15));
4334 QCOMPARE(object->property("intBound"), QVariant(15));
4335 QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
4336 QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
4337 // assign string to property var that current has bool assigned
4338 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
4339 QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
4340 QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
4341 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
4342 // now enforce behaviour when accessing JavaScript objects from cpp.
4343 QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
4347 static void gc(QQmlEngine &engine)
4349 engine.collectGarbage();
4350 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4351 QCoreApplication::processEvents();
4354 void tst_qqmlecmascript::propertyVarOwnership()
4356 // Referenced JS objects are not collected
4358 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.qml"));
4359 QObject *object = component.create();
4360 QVERIFY(object != 0);
4361 QCOMPARE(object->property("test").toBool(), false);
4362 QMetaObject::invokeMethod(object, "runTest");
4363 QCOMPARE(object->property("test").toBool(), true);
4366 // Referenced JS objects are not collected
4368 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml"));
4369 QObject *object = component.create();
4370 QVERIFY(object != 0);
4371 QCOMPARE(object->property("test").toBool(), false);
4372 QMetaObject::invokeMethod(object, "runTest");
4373 QCOMPARE(object->property("test").toBool(), true);
4376 // Qt objects are not collected until they've been dereferenced
4378 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml"));
4379 QObject *object = component.create();
4380 QVERIFY(object != 0);
4382 QCOMPARE(object->property("test2").toBool(), false);
4383 QCOMPARE(object->property("test2").toBool(), false);
4385 QMetaObject::invokeMethod(object, "runTest");
4386 QCOMPARE(object->property("test1").toBool(), true);
4388 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4389 QVERIFY(!referencedObject.isNull());
4391 QVERIFY(!referencedObject.isNull());
4393 QMetaObject::invokeMethod(object, "runTest2");
4394 QCOMPARE(object->property("test2").toBool(), true);
4396 QVERIFY(referencedObject.isNull());
4400 // Self reference does not prevent Qt object collection
4402 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml"));
4403 QObject *object = component.create();
4404 QVERIFY(object != 0);
4406 QCOMPARE(object->property("test").toBool(), true);
4408 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4409 QVERIFY(!referencedObject.isNull());
4411 QVERIFY(!referencedObject.isNull());
4413 QMetaObject::invokeMethod(object, "runTest");
4415 QVERIFY(referencedObject.isNull());
4419 // Garbage collection cannot result in attempted dereference of empty handle
4421 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.5.qml"));
4422 QObject *object = component.create();
4423 QVERIFY(object != 0);
4424 QMetaObject::invokeMethod(object, "runTest");
4425 QCOMPARE(object->property("test").toBool(), true);
4430 void tst_qqmlecmascript::propertyVarImplicitOwnership()
4432 // The childObject has a reference to a different QObject. We want to ensure
4433 // that the different item will not be cleaned up until required. IE, the childObject
4434 // has implicit ownership of the constructed QObject.
4435 QQmlComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml"));
4436 QObject *object = component.create();
4437 QVERIFY(object != 0);
4438 QMetaObject::invokeMethod(object, "assignCircular");
4439 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4440 QCoreApplication::processEvents();
4441 QObject *rootObject = object->property("vp").value<QObject*>();
4442 QVERIFY(rootObject != 0);
4443 QObject *childObject = rootObject->findChild<QObject*>("text");
4444 QVERIFY(childObject != 0);
4445 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4446 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4447 QMetaObject::invokeMethod(childObject, "constructQObject"); // creates a reference to a constructed QObject.
4448 QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
4449 QVERIFY(!qobjectGuard.isNull());
4450 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4451 QCoreApplication::processEvents();
4452 QVERIFY(!qobjectGuard.isNull());
4453 QMetaObject::invokeMethod(object, "deassignCircular");
4454 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4455 QCoreApplication::processEvents();
4456 QVERIFY(qobjectGuard.isNull()); // should have been collected now.
4460 void tst_qqmlecmascript::propertyVarReparent()
4462 // ensure that nothing breaks if we re-parent objects
4463 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4464 QObject *object = component.create();
4465 QVERIFY(object != 0);
4466 QMetaObject::invokeMethod(object, "assignVarProp");
4467 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4468 QCoreApplication::processEvents();
4469 QObject *rect = object->property("vp").value<QObject*>();
4470 QObject *text = rect->findChild<QObject*>("textOne");
4471 QObject *text2 = rect->findChild<QObject*>("textTwo");
4472 QWeakPointer<QObject> rectGuard(rect);
4473 QWeakPointer<QObject> textGuard(text);
4474 QWeakPointer<QObject> text2Guard(text2);
4475 QVERIFY(!rectGuard.isNull());
4476 QVERIFY(!textGuard.isNull());
4477 QVERIFY(!text2Guard.isNull());
4478 QCOMPARE(text->property("textCanary").toInt(), 11);
4479 QCOMPARE(text2->property("textCanary").toInt(), 12);
4480 // now construct an image which we will reparent.
4481 QMetaObject::invokeMethod(text2, "constructQObject");
4482 QObject *image = text2->property("vp").value<QObject*>();
4483 QWeakPointer<QObject> imageGuard(image);
4484 QVERIFY(!imageGuard.isNull());
4485 QCOMPARE(image->property("imageCanary").toInt(), 13);
4486 // now reparent the "Image" object (currently, it has JS ownership)
4487 image->setParent(text); // shouldn't be collected after deassignVp now, since has a parent.
4488 QMetaObject::invokeMethod(text2, "deassignVp");
4489 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4490 QCoreApplication::processEvents();
4491 QCOMPARE(text->property("textCanary").toInt(), 11);
4492 QCOMPARE(text2->property("textCanary").toInt(), 22);
4493 QVERIFY(!imageGuard.isNull()); // should still be alive.
4494 QCOMPARE(image->property("imageCanary").toInt(), 13); // still able to access var properties
4495 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4496 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4497 QCoreApplication::processEvents();
4498 QVERIFY(imageGuard.isNull()); // should now have been deleted, due to parent being deleted.
4502 void tst_qqmlecmascript::propertyVarReparentNullContext()
4504 // sometimes reparenting can cause problems
4505 // (eg, if the ctxt is collected, varproperties are no longer available)
4506 // this test ensures that no crash occurs in that situation.
4507 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4508 QObject *object = component.create();
4509 QVERIFY(object != 0);
4510 QMetaObject::invokeMethod(object, "assignVarProp");
4511 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4512 QCoreApplication::processEvents();
4513 QObject *rect = object->property("vp").value<QObject*>();
4514 QObject *text = rect->findChild<QObject*>("textOne");
4515 QObject *text2 = rect->findChild<QObject*>("textTwo");
4516 QWeakPointer<QObject> rectGuard(rect);
4517 QWeakPointer<QObject> textGuard(text);
4518 QWeakPointer<QObject> text2Guard(text2);
4519 QVERIFY(!rectGuard.isNull());
4520 QVERIFY(!textGuard.isNull());
4521 QVERIFY(!text2Guard.isNull());
4522 QCOMPARE(text->property("textCanary").toInt(), 11);
4523 QCOMPARE(text2->property("textCanary").toInt(), 12);
4524 // now construct an image which we will reparent.
4525 QMetaObject::invokeMethod(text2, "constructQObject");
4526 QObject *image = text2->property("vp").value<QObject*>();
4527 QWeakPointer<QObject> imageGuard(image);
4528 QVERIFY(!imageGuard.isNull());
4529 QCOMPARE(image->property("imageCanary").toInt(), 13);
4530 // now reparent the "Image" object (currently, it has JS ownership)
4531 image->setParent(object); // reparented to base object. after deassignVarProp, the ctxt will be invalid.
4532 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4533 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4534 QCoreApplication::processEvents();
4535 QVERIFY(!imageGuard.isNull()); // should still be alive.
4536 QVERIFY(!image->property("imageCanary").isValid()); // but varProperties won't be available (null context).
4538 QVERIFY(imageGuard.isNull()); // should now be dead.
4541 void tst_qqmlecmascript::propertyVarCircular()
4543 // enforce behaviour regarding circular references - ensure qdvmemo deletion.
4544 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.qml"));
4545 QObject *object = component.create();
4546 QVERIFY(object != 0);
4547 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4548 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4549 QCoreApplication::processEvents();
4550 QCOMPARE(object->property("canaryInt"), QVariant(5));
4551 QVariant canaryResourceVariant = object->property("canaryResource");
4552 QVERIFY(canaryResourceVariant.isValid());
4553 QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
4554 canaryResourceVariant = QVariant(); // invalidate it to remove one copy of the pixmap from memory.
4555 QMetaObject::invokeMethod(object, "deassignCanaryResource"); // remove one copy of the pixmap from memory
4556 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4557 QCoreApplication::processEvents();
4558 QVERIFY(!canaryResourcePixmap.isDetached()); // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
4559 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4560 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4561 QCoreApplication::processEvents();
4562 QCOMPARE(object->property("canaryInt"), QVariant(2));
4563 QCOMPARE(object->property("canaryResource"), QVariant(1));
4564 QVERIFY(canaryResourcePixmap.isDetached()); // now detached, since orig copy was member of qdvmemo which was deleted.
4568 void tst_qqmlecmascript::propertyVarCircular2()
4570 // track deletion of JS-owned parent item with Cpp-owned child
4571 // where the child has a var property referencing its parent.
4572 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4573 QObject *object = component.create();
4574 QVERIFY(object != 0);
4575 QMetaObject::invokeMethod(object, "assignCircular");
4576 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4577 QCoreApplication::processEvents();
4578 QObject *rootObject = object->property("vp").value<QObject*>();
4579 QVERIFY(rootObject != 0);
4580 QObject *childObject = rootObject->findChild<QObject*>("text");
4581 QVERIFY(childObject != 0);
4582 QWeakPointer<QObject> rootObjectTracker(rootObject);
4583 QVERIFY(!rootObjectTracker.isNull());
4584 QWeakPointer<QObject> childObjectTracker(childObject);
4585 QVERIFY(!childObjectTracker.isNull());
4587 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4588 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4589 QMetaObject::invokeMethod(object, "deassignCircular");
4590 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4591 QCoreApplication::processEvents();
4592 QVERIFY(rootObjectTracker.isNull()); // should have been collected
4593 QVERIFY(childObjectTracker.isNull()); // should have been collected
4597 void tst_qqmlecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
4599 *(int*)(parameter) += 1;
4600 qPersistentDispose(object);
4603 void tst_qqmlecmascript::propertyVarInheritance()
4605 int propertyVarWeakRefCallbackCount = 0;
4607 // enforce behaviour regarding element inheritance - ensure handle disposal.
4608 // The particular component under test here has a chain of references.
4609 QQmlComponent component(&engine, testFileUrl("propertyVar.inherit.qml"));
4610 QObject *object = component.create();
4611 QVERIFY(object != 0);
4612 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4613 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4614 QCoreApplication::processEvents();
4615 // we want to be able to track when the varProperties array of the last metaobject is disposed
4616 QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
4617 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*>();
4618 QQmlVMEMetaObject *icovmemo = QQmlVMEMetaObject::get(ico5);
4619 QQmlVMEMetaObject *ccovmemo = QQmlVMEMetaObject::get(cco5);
4620 v8::Persistent<v8::Value> icoCanaryHandle;
4621 v8::Persistent<v8::Value> ccoCanaryHandle;
4624 // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only
4625 // public function which can return us a handle to something in the varProperties array.
4626 icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ")));
4627 ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")));
4628 // we make them weak and invoke the gc, but we should not hit the weak-callback yet
4629 // as the varproperties array of each vmemo still references the resource.
4630 icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4631 ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4633 QVERIFY(propertyVarWeakRefCallbackCount == 0);
4635 // now we deassign the var prop, which should trigger collection of item subtrees.
4636 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4637 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4638 QCoreApplication::processEvents();
4639 // ensure that there are only weak handles to the underlying varProperties array remaining.
4641 QCOMPARE(propertyVarWeakRefCallbackCount, 2); // should have been called for both, since all refs should be weak.
4643 // since there are no parent vmemo's to keep implicit references alive, and the only handles
4644 // to what remains are weak, all varProperties arrays must have been collected.
4647 void tst_qqmlecmascript::propertyVarInheritance2()
4649 int propertyVarWeakRefCallbackCount = 0;
4651 // The particular component under test here does NOT have a chain of references; the
4652 // only link between rootObject and childObject is that rootObject is the parent of childObject.
4653 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4654 QObject *object = component.create();
4655 QVERIFY(object != 0);
4656 QMetaObject::invokeMethod(object, "assignCircular");
4657 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4658 QCoreApplication::processEvents();
4659 QObject *rootObject = object->property("vp").value<QObject*>();
4660 QVERIFY(rootObject != 0);
4661 QObject *childObject = rootObject->findChild<QObject*>("text");
4662 QVERIFY(childObject != 0);
4663 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4664 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4665 v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
4668 propertyVarWeakRefCallbackCount = 0; // reset callback count.
4669 childObjectVarArrayValueHandle = qPersistentNew(QQmlVMEMetaObject::get(childObject)->vmeProperty(childObject->metaObject()->indexOfProperty("vp")));
4670 childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4672 QVERIFY(propertyVarWeakRefCallbackCount == 0); // should not have been collected yet.
4673 QCOMPARE(childObject->property("vp").value<QObject*>(), rootObject);
4674 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4676 QMetaObject::invokeMethod(object, "deassignCircular");
4677 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4678 QCoreApplication::processEvents();
4679 QVERIFY(propertyVarWeakRefCallbackCount == 1); // should have been collected now.
4683 // Ensure that QObject type conversion works on binding assignment
4684 void tst_qqmlecmascript::elementAssign()
4686 QQmlComponent component(&engine, testFileUrl("elementAssign.qml"));
4688 QObject *object = component.create();
4689 QVERIFY(object != 0);
4691 QCOMPARE(object->property("test").toBool(), true);
4697 void tst_qqmlecmascript::objectPassThroughSignals()
4699 QQmlComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml"));
4701 QObject *object = component.create();
4702 QVERIFY(object != 0);
4704 QCOMPARE(object->property("test").toBool(), true);
4710 void tst_qqmlecmascript::objectConversion()
4712 QQmlComponent component(&engine, testFileUrl("objectConversion.qml"));
4714 QObject *object = component.create();
4715 QVERIFY(object != 0);
4717 QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
4718 QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
4725 void tst_qqmlecmascript::booleanConversion()
4727 QQmlComponent component(&engine, testFileUrl("booleanConversion.qml"));
4729 QObject *object = component.create();
4730 QVERIFY(object != 0);
4732 QCOMPARE(object->property("test_true1").toBool(), true);
4733 QCOMPARE(object->property("test_true2").toBool(), true);
4734 QCOMPARE(object->property("test_true3").toBool(), true);
4735 QCOMPARE(object->property("test_true4").toBool(), true);
4736 QCOMPARE(object->property("test_true5").toBool(), true);
4738 QCOMPARE(object->property("test_false1").toBool(), false);
4739 QCOMPARE(object->property("test_false2").toBool(), false);
4740 QCOMPARE(object->property("test_false3").toBool(), false);
4745 void tst_qqmlecmascript::handleReferenceManagement()
4750 // Linear QObject reference
4751 QQmlEngine hrmEngine;
4752 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml"));
4753 QObject *object = component.create();
4754 QVERIFY(object != 0);
4755 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4756 cro->setEngine(&hrmEngine);
4757 cro->setDtorCount(&dtorCount);
4758 QMetaObject::invokeMethod(object, "createReference");
4760 QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
4762 hrmEngine.collectGarbage();
4763 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4764 QCoreApplication::processEvents();
4765 QCOMPARE(dtorCount, 3);
4770 // Circular QObject reference
4771 QQmlEngine hrmEngine;
4772 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml"));
4773 QObject *object = component.create();
4774 QVERIFY(object != 0);
4775 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4776 cro->setEngine(&hrmEngine);
4777 cro->setDtorCount(&dtorCount);
4778 QMetaObject::invokeMethod(object, "circularReference");
4780 QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
4782 hrmEngine.collectGarbage();
4783 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4784 QCoreApplication::processEvents();
4785 QCOMPARE(dtorCount, 3);
4790 // Linear handle reference
4791 QQmlEngine hrmEngine;
4792 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.1.qml"));
4793 QObject *object = component.create();
4794 QVERIFY(object != 0);
4795 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4797 crh->setEngine(&hrmEngine);
4798 crh->setDtorCount(&dtorCount);
4799 QMetaObject::invokeMethod(object, "createReference");
4800 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4801 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4802 QVERIFY(first != 0);
4803 QVERIFY(second != 0);
4804 first->addReference(QQmlData::get(second)->v8object); // create reference
4805 // now we have to reparent second and make second owned by JS.
4806 second->setParent(0);
4807 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
4809 QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
4811 hrmEngine.collectGarbage();
4812 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4813 QCoreApplication::processEvents();
4814 QCOMPARE(dtorCount, 3);
4819 // Circular handle reference
4820 QQmlEngine hrmEngine;
4821 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.2.qml"));
4822 QObject *object = component.create();
4823 QVERIFY(object != 0);
4824 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4826 crh->setEngine(&hrmEngine);
4827 crh->setDtorCount(&dtorCount);
4828 QMetaObject::invokeMethod(object, "circularReference");
4829 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4830 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4831 QVERIFY(first != 0);
4832 QVERIFY(second != 0);
4833 first->addReference(QQmlData::get(second)->v8object); // create circular reference
4834 second->addReference(QQmlData::get(first)->v8object); // note: must be weak.
4835 // now we have to reparent and change ownership, and unset the property references.
4836 first->setParent(0);
4837 second->setParent(0);
4838 QQmlEngine::setObjectOwnership(first, QQmlEngine::JavaScriptOwnership);
4839 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
4840 object->setProperty("first", QVariant::fromValue<QObject*>(0));
4841 object->setProperty("second", QVariant::fromValue<QObject*>(0));
4843 QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
4845 hrmEngine.collectGarbage();
4846 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4847 QCoreApplication::processEvents();
4848 QCOMPARE(dtorCount, 3);
4853 // multiple engine interaction - linear reference
4854 QQmlEngine hrmEngine1;
4855 QQmlEngine hrmEngine2;
4856 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4857 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4858 QObject *object1 = component1.create();
4859 QObject *object2 = component2.create();
4860 QVERIFY(object1 != 0);
4861 QVERIFY(object2 != 0);
4862 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4863 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4866 crh1->setEngine(&hrmEngine1);
4867 crh2->setEngine(&hrmEngine2);
4868 crh1->setDtorCount(&dtorCount);
4869 crh2->setDtorCount(&dtorCount);
4870 QMetaObject::invokeMethod(object1, "createReference");
4871 QMetaObject::invokeMethod(object2, "createReference");
4872 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4873 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4874 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4875 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4876 QVERIFY(first1 != 0);
4877 QVERIFY(second1 != 0);
4878 QVERIFY(first2 != 0);
4879 QVERIFY(second2 != 0);
4880 first1->addReference(QQmlData::get(second2)->v8object); // create reference across engines
4881 // now we have to reparent second2 and make second2 owned by JS.
4882 second2->setParent(0);
4883 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4885 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4886 QCoreApplication::processEvents();
4887 QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
4890 hrmEngine1.collectGarbage();
4891 hrmEngine2.collectGarbage();
4892 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4893 QCoreApplication::processEvents();
4894 QCOMPARE(dtorCount, 6);
4899 // multiple engine interaction - circular reference
4900 QQmlEngine hrmEngine1;
4901 QQmlEngine hrmEngine2;
4902 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4903 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4904 QObject *object1 = component1.create();
4905 QObject *object2 = component2.create();
4906 QVERIFY(object1 != 0);
4907 QVERIFY(object2 != 0);
4908 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4909 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4912 crh1->setEngine(&hrmEngine1);
4913 crh2->setEngine(&hrmEngine2);
4914 crh1->setDtorCount(&dtorCount);
4915 crh2->setDtorCount(&dtorCount);
4916 QMetaObject::invokeMethod(object1, "createReference");
4917 QMetaObject::invokeMethod(object2, "createReference");
4918 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4919 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4920 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4921 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4922 QVERIFY(first1 != 0);
4923 QVERIFY(second1 != 0);
4924 QVERIFY(first2 != 0);
4925 QVERIFY(second2 != 0);
4926 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
4927 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
4928 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
4929 first2->addReference(QQmlData::get(first1)->v8object); // close the loop - circular ref across engines
4930 // now we have to reparent and change ownership to JS, and remove property references.
4931 first1->setParent(0);
4932 second1->setParent(0);
4933 first2->setParent(0);
4934 second2->setParent(0);
4935 QQmlEngine::setObjectOwnership(first1, QQmlEngine::JavaScriptOwnership);
4936 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
4937 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
4938 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4939 object1->setProperty("first", QVariant::fromValue<QObject*>(0));
4940 object1->setProperty("second", QVariant::fromValue<QObject*>(0));
4941 object2->setProperty("first", QVariant::fromValue<QObject*>(0));
4942 object2->setProperty("second", QVariant::fromValue<QObject*>(0));
4944 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4945 QCoreApplication::processEvents();
4946 QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
4949 hrmEngine1.collectGarbage();
4950 hrmEngine2.collectGarbage();
4951 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4952 QCoreApplication::processEvents();
4953 QCOMPARE(dtorCount, 6);
4958 // multiple engine interaction - linear reference with engine deletion
4959 QQmlEngine *hrmEngine1 = new QQmlEngine;
4960 QQmlEngine *hrmEngine2 = new QQmlEngine;
4961 QQmlComponent component1(hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4962 QQmlComponent component2(hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4963 QObject *object1 = component1.create();
4964 QObject *object2 = component2.create();
4965 QVERIFY(object1 != 0);
4966 QVERIFY(object2 != 0);
4967 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4968 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4971 crh1->setEngine(hrmEngine1);
4972 crh2->setEngine(hrmEngine2);
4973 crh1->setDtorCount(&dtorCount);
4974 crh2->setDtorCount(&dtorCount);
4975 QMetaObject::invokeMethod(object1, "createReference");
4976 QMetaObject::invokeMethod(object2, "createReference");
4977 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4978 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4979 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4980 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4981 QVERIFY(first1 != 0);
4982 QVERIFY(second1 != 0);
4983 QVERIFY(first2 != 0);
4984 QVERIFY(second2 != 0);
4985 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
4986 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
4987 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
4988 // now we have to reparent and change ownership to JS.
4989 first1->setParent(crh1);
4990 second1->setParent(0);
4991 first2->setParent(0);
4992 second2->setParent(0);
4993 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
4994 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
4995 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4998 QCOMPARE(dtorCount, 0);
4999 delete hrmEngine2; // should trigger deletion of objects with JS ownership tracked by this engine
5001 QCOMPARE(dtorCount, 2); // first2 and second2 should have been deleted.
5005 QCOMPARE(dtorCount, 6); // deleting object1 and object2 should trigger deletion of first1 and first2.
5007 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5008 QCoreApplication::processEvents();
5009 QCOMPARE(dtorCount, 6); // all objects should have been cleaned up prior to deleting hrmEngine1.
5013 // Dynamic variant property reference keeps target alive
5014 QQmlEngine hrmEngine;
5015 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.qml"));
5016 QObject *object = component.create();
5017 QVERIFY(object != 0);
5018 QMetaObject::invokeMethod(object, "createReference");
5020 QMetaObject::invokeMethod(object, "ensureReference");
5022 QMetaObject::invokeMethod(object, "removeReference");
5024 QMetaObject::invokeMethod(object, "ensureDeletion");
5025 QCOMPARE(object->property("success").toBool(), true);
5030 // Dynamic Item property reference keeps target alive
5031 QQmlEngine hrmEngine;
5032 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.2.qml"));
5033 QObject *object = component.create();
5034 QVERIFY(object != 0);
5035 QMetaObject::invokeMethod(object, "createReference");
5037 QMetaObject::invokeMethod(object, "ensureReference");
5039 QMetaObject::invokeMethod(object, "removeReference");
5041 QMetaObject::invokeMethod(object, "ensureDeletion");
5042 QCOMPARE(object->property("success").toBool(), true);
5047 // Item property reference to deleted item doesn't crash
5048 QQmlEngine hrmEngine;
5049 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.3.qml"));
5050 QObject *object = component.create();
5051 QVERIFY(object != 0);
5052 QMetaObject::invokeMethod(object, "createReference");
5054 QMetaObject::invokeMethod(object, "ensureReference");
5056 QMetaObject::invokeMethod(object, "manuallyDelete");
5058 QMetaObject::invokeMethod(object, "ensureDeleted");
5059 QCOMPARE(object->property("success").toBool(), true);
5064 void tst_qqmlecmascript::stringArg()
5066 QQmlComponent component(&engine, testFileUrl("stringArg.qml"));
5067 QObject *object = component.create();
5068 QVERIFY(object != 0);
5069 QMetaObject::invokeMethod(object, "success");
5070 QVERIFY(object->property("returnValue").toBool());
5072 QString w1 = testFileUrl("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
5073 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5074 QMetaObject::invokeMethod(object, "failure");
5075 QVERIFY(object->property("returnValue").toBool());
5080 void tst_qqmlecmascript::readonlyDeclaration()
5082 QQmlComponent component(&engine, testFileUrl("readonlyDeclaration.qml"));
5084 QObject *object = component.create();
5085 QVERIFY(object != 0);
5087 QCOMPARE(object->property("test").toBool(), true);
5092 Q_DECLARE_METATYPE(QList<int>)
5093 Q_DECLARE_METATYPE(QList<qreal>)
5094 Q_DECLARE_METATYPE(QList<bool>)
5095 Q_DECLARE_METATYPE(QList<QString>)
5096 Q_DECLARE_METATYPE(QList<QUrl>)
5097 void tst_qqmlecmascript::sequenceConversionRead()
5100 QUrl qmlFile = testFileUrl("sequenceConversion.read.qml");
5101 QQmlComponent component(&engine, qmlFile);
5102 QObject *object = component.create();
5103 QVERIFY(object != 0);
5104 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5107 QMetaObject::invokeMethod(object, "readSequences");
5108 QList<int> intList; intList << 1 << 2 << 3 << 4;
5109 QCOMPARE(object->property("intListLength").toInt(), intList.length());
5110 QCOMPARE(object->property("intList").value<QList<int> >(), intList);
5111 QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
5112 QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
5113 QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
5114 QList<bool> boolList; boolList << true << false << true << false;
5115 QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
5116 QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
5117 QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5118 QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
5119 QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
5120 QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
5121 QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
5122 QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
5123 QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5124 QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
5125 QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
5127 QMetaObject::invokeMethod(object, "readSequenceElements");
5128 QCOMPARE(object->property("intVal").toInt(), 2);
5129 QCOMPARE(object->property("qrealVal").toReal(), 2.2);
5130 QCOMPARE(object->property("boolVal").toBool(), false);
5131 QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
5132 QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
5133 QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
5135 QMetaObject::invokeMethod(object, "enumerateSequenceElements");
5136 QCOMPARE(object->property("enumerationMatches").toBool(), true);
5138 intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
5139 QQmlProperty seqProp(seq, "intListProperty");
5140 QCOMPARE(seqProp.read().value<QList<int> >(), intList);
5141 QQmlProperty seqProp2(seq, "intListProperty", &engine);
5142 QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
5144 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5145 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5151 QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml");
5152 QQmlComponent component(&engine, qmlFile);
5153 QObject *object = component.create();
5154 QVERIFY(object != 0);
5155 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5158 // we haven't registered QList<QPoint> as a sequence type.
5159 QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5160 QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
5161 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5162 QTest::ignoreMessage(QtWarningMsg, warningTwo.toLatin1().constData());
5164 QMetaObject::invokeMethod(object, "performTest");
5166 // QList<QPoint> has not been registered as a sequence type.
5167 QCOMPARE(object->property("pointListLength").toInt(), 0);
5168 QVERIFY(!object->property("pointList").isValid());
5169 QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5170 QQmlProperty seqProp(seq, "pointListProperty", &engine);
5171 QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
5177 void tst_qqmlecmascript::sequenceConversionWrite()
5180 QUrl qmlFile = testFileUrl("sequenceConversion.write.qml");
5181 QQmlComponent component(&engine, qmlFile);
5182 QObject *object = component.create();
5183 QVERIFY(object != 0);
5184 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5187 QMetaObject::invokeMethod(object, "writeSequences");
5188 QCOMPARE(object->property("success").toBool(), true);
5190 QMetaObject::invokeMethod(object, "writeSequenceElements");
5191 QCOMPARE(object->property("success").toBool(), true);
5193 QMetaObject::invokeMethod(object, "writeOtherElements");
5194 QCOMPARE(object->property("success").toBool(), true);
5196 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5197 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5203 QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml");
5204 QQmlComponent component(&engine, qmlFile);
5205 QObject *object = component.create();
5206 QVERIFY(object != 0);
5207 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5210 // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
5211 QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to an unregistered type");
5212 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5214 QMetaObject::invokeMethod(object, "performTest");
5216 QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
5217 QCOMPARE(seq->pointListProperty(), pointList);
5223 void tst_qqmlecmascript::sequenceConversionArray()
5225 // ensure that in JS the returned sequences act just like normal JS Arrays.
5226 QUrl qmlFile = testFileUrl("sequenceConversion.array.qml");
5227 QQmlComponent component(&engine, qmlFile);
5228 QObject *object = component.create();
5229 QVERIFY(object != 0);
5230 QMetaObject::invokeMethod(object, "indexedAccess");
5231 QVERIFY(object->property("success").toBool());
5232 QMetaObject::invokeMethod(object, "arrayOperations");
5233 QVERIFY(object->property("success").toBool());
5234 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5235 QVERIFY(object->property("success").toBool());
5236 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5237 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5242 void tst_qqmlecmascript::sequenceConversionIndexes()
5244 // ensure that we gracefully fail if unsupported index values are specified.
5245 // Qt container classes only support non-negative, signed integer index values.
5246 QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
5247 QQmlComponent component(&engine, qmlFile);
5248 QObject *object = component.create();
5249 QVERIFY(object != 0);
5250 QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
5251 QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
5252 QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
5253 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
5254 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
5255 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
5256 QMetaObject::invokeMethod(object, "indexedAccess");
5257 QVERIFY(object->property("success").toBool());
5261 void tst_qqmlecmascript::sequenceConversionThreads()
5263 // ensure that sequence conversion operations work correctly in a worker thread
5264 // and that serialisation between the main and worker thread succeeds.
5265 QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml");
5266 QQmlComponent component(&engine, qmlFile);
5267 QObject *object = component.create();
5268 QVERIFY(object != 0);
5270 QMetaObject::invokeMethod(object, "testIntSequence");
5271 QTRY_VERIFY(object->property("finished").toBool());
5272 QVERIFY(object->property("success").toBool());
5274 QMetaObject::invokeMethod(object, "testQrealSequence");
5275 QTRY_VERIFY(object->property("finished").toBool());
5276 QVERIFY(object->property("success").toBool());
5278 QMetaObject::invokeMethod(object, "testBoolSequence");
5279 QTRY_VERIFY(object->property("finished").toBool());
5280 QVERIFY(object->property("success").toBool());
5282 QMetaObject::invokeMethod(object, "testStringSequence");
5283 QTRY_VERIFY(object->property("finished").toBool());
5284 QVERIFY(object->property("success").toBool());
5286 QMetaObject::invokeMethod(object, "testQStringSequence");
5287 QTRY_VERIFY(object->property("finished").toBool());
5288 QVERIFY(object->property("success").toBool());
5290 QMetaObject::invokeMethod(object, "testUrlSequence");
5291 QTRY_VERIFY(object->property("finished").toBool());
5292 QVERIFY(object->property("success").toBool());
5294 QMetaObject::invokeMethod(object, "testVariantSequence");
5295 QTRY_VERIFY(object->property("finished").toBool());
5296 QVERIFY(object->property("success").toBool());
5301 void tst_qqmlecmascript::sequenceConversionBindings()
5304 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml");
5305 QQmlComponent component(&engine, qmlFile);
5306 QObject *object = component.create();
5307 QVERIFY(object != 0);
5308 QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
5309 QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
5310 QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
5311 QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
5312 QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
5317 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml");
5318 QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
5319 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5320 QQmlComponent component(&engine, qmlFile);
5321 QObject *object = component.create();
5322 QVERIFY(object != 0);
5327 void tst_qqmlecmascript::sequenceConversionCopy()
5329 QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml");
5330 QQmlComponent component(&engine, qmlFile);
5331 QObject *object = component.create();
5332 QVERIFY(object != 0);
5333 QMetaObject::invokeMethod(object, "testCopySequences");
5334 QCOMPARE(object->property("success").toBool(), true);
5335 QMetaObject::invokeMethod(object, "readSequenceCopyElements");
5336 QCOMPARE(object->property("success").toBool(), true);
5337 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5338 QCOMPARE(object->property("success").toBool(), true);
5342 void tst_qqmlecmascript::assignSequenceTypes()
5344 // test binding array to sequence type property
5346 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml"));
5347 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5348 QVERIFY(object != 0);
5349 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5350 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5351 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5352 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5353 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5354 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5358 // test binding literal to sequence type property
5360 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml"));
5361 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5362 QVERIFY(object != 0);
5363 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5364 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5365 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5366 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5367 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5368 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5372 // test binding single value to sequence type property
5374 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml"));
5375 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5376 QVERIFY(object != 0);
5377 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5378 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5379 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5380 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5384 // test assigning array to sequence type property in js function
5386 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml"));
5387 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5388 QVERIFY(object != 0);
5389 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5390 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5391 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5392 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5393 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5394 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5398 // test assigning literal to sequence type property in js function
5400 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml"));
5401 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5402 QVERIFY(object != 0);
5403 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5404 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5405 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5406 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5407 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5408 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5412 // test assigning single value to sequence type property in js function
5414 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml"));
5415 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5416 QVERIFY(object != 0);
5417 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5418 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5419 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5420 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5424 // test QList<QUrl> literal assignment and binding assignment causes url resolution when required
5426 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml"));
5427 QObject *object = component.create();
5428 QVERIFY(object != 0);
5429 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
5430 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
5431 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
5432 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
5433 MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco5"));
5434 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0);
5435 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5436 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5437 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5438 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5439 QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5444 // Test that assigning a null object works
5445 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
5446 void tst_qqmlecmascript::nullObjectBinding()
5448 QQmlComponent component(&engine, testFileUrl("nullObjectBinding.qml"));
5450 QObject *object = component.create();
5451 QVERIFY(object != 0);
5453 QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
5458 // Test that bindings don't evaluate once the engine has been destroyed
5459 void tst_qqmlecmascript::deletedEngine()
5461 QQmlEngine *engine = new QQmlEngine;
5462 QQmlComponent component(engine, testFileUrl("deletedEngine.qml"));
5464 QObject *object = component.create();
5465 QVERIFY(object != 0);
5467 QCOMPARE(object->property("a").toInt(), 39);
5468 object->setProperty("b", QVariant(9));
5469 QCOMPARE(object->property("a").toInt(), 117);
5473 QCOMPARE(object->property("a").toInt(), 117);
5474 object->setProperty("b", QVariant(10));
5475 QCOMPARE(object->property("a").toInt(), 117);
5480 // Test the crashing part of QTBUG-9705
5481 void tst_qqmlecmascript::libraryScriptAssert()
5483 QQmlComponent component(&engine, testFileUrl("libraryScriptAssert.qml"));
5485 QObject *object = component.create();
5486 QVERIFY(object != 0);
5491 void tst_qqmlecmascript::variantsAssignedUndefined()
5493 QQmlComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml"));
5495 QObject *object = component.create();
5496 QVERIFY(object != 0);
5498 QCOMPARE(object->property("test1").toInt(), 10);
5499 QCOMPARE(object->property("test2").toInt(), 11);
5501 object->setProperty("runTest", true);
5503 QCOMPARE(object->property("test1"), QVariant());
5504 QCOMPARE(object->property("test2"), QVariant());
5510 void tst_qqmlecmascript::qtbug_9792()
5512 QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml"));
5514 QQmlContext *context = new QQmlContext(engine.rootContext());
5516 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
5517 QVERIFY(object != 0);
5519 QTest::ignoreMessage(QtDebugMsg, "Hello world!");
5520 object->basicSignal();
5524 transientErrorsMsgCount = 0;
5525 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5527 object->basicSignal();
5529 qInstallMsgHandler(old);
5531 QCOMPARE(transientErrorsMsgCount, 0);
5536 // Verifies that QQmlGuard<>s used in the vmemetaobject are cleaned correctly
5537 void tst_qqmlecmascript::qtcreatorbug_1289()
5539 QQmlComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml"));
5541 QObject *o = component.create();
5544 QObject *nested = qvariant_cast<QObject *>(o->property("object"));
5545 QVERIFY(nested != 0);
5547 QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
5550 nested = qvariant_cast<QObject *>(o->property("object"));
5551 QVERIFY(nested == 0);
5553 // If the bug is present, the next line will crash
5557 // Test that we shut down without stupid warnings
5558 void tst_qqmlecmascript::noSpuriousWarningsAtShutdown()
5561 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml"));
5563 QObject *o = component.create();
5565 transientErrorsMsgCount = 0;
5566 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5570 qInstallMsgHandler(old);
5572 QCOMPARE(transientErrorsMsgCount, 0);
5577 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml"));
5579 QObject *o = component.create();
5581 transientErrorsMsgCount = 0;
5582 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5586 qInstallMsgHandler(old);
5588 QCOMPARE(transientErrorsMsgCount, 0);
5592 void tst_qqmlecmascript::canAssignNullToQObject()
5595 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml"));
5597 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5600 QVERIFY(o->objectProperty() != 0);
5602 o->setProperty("runTest", true);
5604 QVERIFY(o->objectProperty() == 0);
5610 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml"));
5612 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5615 QVERIFY(o->objectProperty() == 0);
5621 void tst_qqmlecmascript::functionAssignment_fromBinding()
5623 QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml"));
5625 QString url = component.url().toString();
5626 QString w1 = url + ":4:25: Unable to assign a function to a property of any type other than var.";
5627 QString w2 = url + ":5:25: Invalid use of Qt.binding() in a binding declaration.";
5628 QString w3 = url + ":6:21: Invalid use of Qt.binding() in a binding declaration.";
5629 QString w4 = url + ":7:15: Invalid use of Qt.binding() in a binding declaration.";
5630 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5631 QTest::ignoreMessage(QtWarningMsg, w2.toLatin1().constData());
5632 QTest::ignoreMessage(QtWarningMsg, w3.toLatin1().constData());
5633 QTest::ignoreMessage(QtWarningMsg, w4.toLatin1().constData());
5635 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5638 QVERIFY(!o->property("a").isValid());
5643 void tst_qqmlecmascript::functionAssignment_fromJS()
5645 QFETCH(QString, triggerProperty);
5647 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5648 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5650 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5652 QVERIFY(!o->property("a").isValid());
5654 o->setProperty("aNumber", QVariant(5));
5655 o->setProperty(triggerProperty.toUtf8().constData(), true);
5656 QCOMPARE(o->property("a"), QVariant(50));
5658 o->setProperty("aNumber", QVariant(10));
5659 QCOMPARE(o->property("a"), QVariant(100));
5664 void tst_qqmlecmascript::functionAssignment_fromJS_data()
5666 QTest::addColumn<QString>("triggerProperty");
5668 QTest::newRow("assign to property") << "assignToProperty";
5669 QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
5671 QTest::newRow("assign to value type") << "assignToValueType";
5673 QTest::newRow("use 'this'") << "assignWithThis";
5674 QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
5677 void tst_qqmlecmascript::functionAssignmentfromJS_invalid()
5679 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5680 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5682 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5684 QVERIFY(!o->property("a").isValid());
5686 o->setProperty("assignFuncWithoutReturn", true);
5687 QVERIFY(!o->property("a").isValid());
5689 QString url = component.url().toString();
5690 QString warning = url + ":67:17: Unable to assign QString to int";
5691 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5692 o->setProperty("assignWrongType", true);
5694 warning = url + ":71:29: Unable to assign QString to int";
5695 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5696 o->setProperty("assignWrongTypeToValueType", true);
5701 void tst_qqmlecmascript::functionAssignment_afterBinding()
5703 QQmlComponent component(&engine, testFileUrl("functionAssignment.3.qml"));
5705 QString url = component.url().toString();
5706 QString w1 = url + ":16: Error: Cannot assign JavaScript function to int";
5707 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5709 QObject *o = component.create();
5711 QCOMPARE(o->property("t1"), QVariant::fromValue<int>(4)); // should have bound
5712 QCOMPARE(o->property("t2"), QVariant::fromValue<int>(2)); // should not have changed
5717 void tst_qqmlecmascript::eval()
5719 QQmlComponent component(&engine, testFileUrl("eval.qml"));
5721 QObject *o = component.create();
5724 QCOMPARE(o->property("test1").toBool(), true);
5725 QCOMPARE(o->property("test2").toBool(), true);
5726 QCOMPARE(o->property("test3").toBool(), true);
5727 QCOMPARE(o->property("test4").toBool(), true);
5728 QCOMPARE(o->property("test5").toBool(), true);
5733 void tst_qqmlecmascript::function()
5735 QQmlComponent component(&engine, testFileUrl("function.qml"));
5737 QObject *o = component.create();
5740 QCOMPARE(o->property("test1").toBool(), true);
5741 QCOMPARE(o->property("test2").toBool(), true);
5742 QCOMPARE(o->property("test3").toBool(), true);
5747 void tst_qqmlecmascript::functionException()
5749 // QTBUG-24037 - shouldn't crash.
5750 QString errstr = testFileUrl("v8functionException.qml").toString() + QLatin1String(":13: SyntaxError: Unexpected token ILLEGAL");
5751 QTest::ignoreMessage(QtWarningMsg, qPrintable(errstr));
5752 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Exception occurred during compilation of function: dynamicSlot()");
5753 QQmlComponent component(&engine, testFileUrl("v8functionException.qml"));
5754 QObject *o = component.create();
5756 QMetaObject::invokeMethod(o, "dynamicSlot");
5760 // Test the "Qt.include" method
5761 void tst_qqmlecmascript::include()
5763 // Non-library relative include
5765 QQmlComponent component(&engine, testFileUrl("include.qml"));
5766 QObject *o = component.create();
5769 QCOMPARE(o->property("test0").toInt(), 99);
5770 QCOMPARE(o->property("test1").toBool(), true);
5771 QCOMPARE(o->property("test2").toBool(), true);
5772 QCOMPARE(o->property("test2_1").toBool(), true);
5773 QCOMPARE(o->property("test3").toBool(), true);
5774 QCOMPARE(o->property("test3_1").toBool(), true);
5779 // Library relative include
5781 QQmlComponent component(&engine, testFileUrl("include_shared.qml"));
5782 QObject *o = component.create();
5785 QCOMPARE(o->property("test0").toInt(), 99);
5786 QCOMPARE(o->property("test1").toBool(), true);
5787 QCOMPARE(o->property("test2").toBool(), true);
5788 QCOMPARE(o->property("test2_1").toBool(), true);
5789 QCOMPARE(o->property("test3").toBool(), true);
5790 QCOMPARE(o->property("test3_1").toBool(), true);
5797 QQmlComponent component(&engine, testFileUrl("include_callback.qml"));
5798 QObject *o = component.create();
5801 QCOMPARE(o->property("test1").toBool(), true);
5802 QCOMPARE(o->property("test2").toBool(), true);
5803 QCOMPARE(o->property("test3").toBool(), true);
5804 QCOMPARE(o->property("test4").toBool(), true);
5805 QCOMPARE(o->property("test5").toBool(), true);
5806 QCOMPARE(o->property("test6").toBool(), true);
5811 // Including file with ".pragma library"
5813 QQmlComponent component(&engine, testFileUrl("include_pragma.qml"));
5814 QObject *o = component.create();
5816 QCOMPARE(o->property("test1").toInt(), 100);
5823 TestHTTPServer server(8111);
5824 QVERIFY(server.isValid());
5825 server.serveDirectory(dataDirectory());
5827 QQmlComponent component(&engine, testFileUrl("include_remote.qml"));
5828 QObject *o = component.create();
5831 QTRY_VERIFY(o->property("done").toBool() == true);
5832 QTRY_VERIFY(o->property("done2").toBool() == true);
5834 QCOMPARE(o->property("test1").toBool(), true);
5835 QCOMPARE(o->property("test2").toBool(), true);
5836 QCOMPARE(o->property("test3").toBool(), true);
5837 QCOMPARE(o->property("test4").toBool(), true);
5838 QCOMPARE(o->property("test5").toBool(), true);
5840 QCOMPARE(o->property("test6").toBool(), true);
5841 QCOMPARE(o->property("test7").toBool(), true);
5842 QCOMPARE(o->property("test8").toBool(), true);
5843 QCOMPARE(o->property("test9").toBool(), true);
5844 QCOMPARE(o->property("test10").toBool(), true);
5851 TestHTTPServer server(8111);
5852 QVERIFY(server.isValid());
5853 server.serveDirectory(dataDirectory());
5855 QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml"));
5856 QObject *o = component.create();
5859 QTRY_VERIFY(o->property("done").toBool() == true);
5861 QCOMPARE(o->property("test1").toBool(), true);
5862 QCOMPARE(o->property("test2").toBool(), true);
5863 QCOMPARE(o->property("test3").toBool(), true);
5869 void tst_qqmlecmascript::signalHandlers()
5871 QQmlComponent component(&engine, testFileUrl("signalHandlers.qml"));
5872 QObject *o = component.create();
5875 QVERIFY(o->property("count").toInt() == 0);
5876 QMetaObject::invokeMethod(o, "testSignalCall");
5877 QCOMPARE(o->property("count").toInt(), 1);
5879 QMetaObject::invokeMethod(o, "testSignalHandlerCall");
5880 QCOMPARE(o->property("count").toInt(), 1);
5881 QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
5883 QVERIFY(o->property("funcCount").toInt() == 0);
5884 QMetaObject::invokeMethod(o, "testSignalConnection");
5885 QCOMPARE(o->property("funcCount").toInt(), 1);
5887 QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
5888 QCOMPARE(o->property("funcCount").toInt(), 2);
5890 QMetaObject::invokeMethod(o, "testSignalDefined");
5891 QCOMPARE(o->property("definedResult").toBool(), true);
5893 QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
5894 QCOMPARE(o->property("definedHandlerResult").toBool(), true);
5899 void tst_qqmlecmascript::qtbug_10696()
5901 QQmlComponent component(&engine, testFileUrl("qtbug_10696.qml"));
5902 QObject *o = component.create();
5907 void tst_qqmlecmascript::qtbug_11606()
5909 QQmlComponent component(&engine, testFileUrl("qtbug_11606.qml"));
5910 QObject *o = component.create();
5912 QCOMPARE(o->property("test").toBool(), true);
5916 void tst_qqmlecmascript::qtbug_11600()
5918 QQmlComponent component(&engine, testFileUrl("qtbug_11600.qml"));
5919 QObject *o = component.create();
5921 QCOMPARE(o->property("test").toBool(), true);
5925 void tst_qqmlecmascript::qtbug_21864()
5927 QQmlComponent component(&engine, testFileUrl("qtbug_21864.qml"));
5928 QObject *o = component.create();
5930 QCOMPARE(o->property("test").toBool(), true);
5934 void tst_qqmlecmascript::rewriteMultiLineStrings()
5938 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml"));
5939 QObject *o = component.create();
5941 QTRY_COMPARE(o->property("test").toBool(), true);
5946 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings_crlf.1.qml"));
5947 QObject *o = component.create();
5953 void tst_qqmlecmascript::qobjectConnectionListExceptionHandling()
5956 QQmlComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml"));
5957 QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined");
5958 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5959 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5960 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5961 QObject *o = component.create();
5963 QCOMPARE(o->property("test").toBool(), true);
5967 // Reading and writing non-scriptable properties should fail
5968 void tst_qqmlecmascript::nonscriptable()
5970 QQmlComponent component(&engine, testFileUrl("nonscriptable.qml"));
5971 QObject *o = component.create();
5973 QCOMPARE(o->property("readOk").toBool(), true);
5974 QCOMPARE(o->property("writeOk").toBool(), true);
5978 // deleteLater() should not be callable from QML
5979 void tst_qqmlecmascript::deleteLater()
5981 QQmlComponent component(&engine, testFileUrl("deleteLater.qml"));
5982 QObject *o = component.create();
5984 QCOMPARE(o->property("test").toBool(), true);
5988 // objectNameChanged() should be usable from QML
5989 void tst_qqmlecmascript::objectNameChangedSignal()
5991 QQmlComponent component(&engine, testFileUrl("objectNameChangedSignal.qml"));
5992 QObject *o = component.create();
5994 QCOMPARE(o->property("test").toBool(), false);
5995 o->setObjectName("obj");
5996 QCOMPARE(o->property("test").toBool(), true);
6000 // destroyed() should not be usable from QML
6001 void tst_qqmlecmascript::destroyedSignal()
6003 QQmlComponent component(&engine, testFileUrl("destroyedSignal.qml"));
6004 QVERIFY(component.isError());
6006 QString expectedErrorString = component.url().toString() + QLatin1String(":5:5: Cannot assign to non-existent property \"onDestroyed\"");
6007 QCOMPARE(component.errors().at(0).toString(), expectedErrorString);
6010 void tst_qqmlecmascript::in()
6012 QQmlComponent component(&engine, testFileUrl("in.qml"));
6013 QObject *o = component.create();
6015 QCOMPARE(o->property("test1").toBool(), true);
6016 QCOMPARE(o->property("test2").toBool(), true);
6020 void tst_qqmlecmascript::typeOf()
6022 QQmlComponent component(&engine, testFileUrl("typeOf.qml"));
6024 QObject *o = component.create();
6027 QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
6028 QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
6029 QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
6030 QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
6031 QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
6032 QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
6033 QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
6034 QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
6035 QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
6040 void tst_qqmlecmascript::qtbug_24448()
6042 QQmlComponent component(&engine, testFileUrl("qtbug_24448.qml"));
6043 QScopedPointer<QObject> o(component.create());
6045 QVERIFY(o->property("test").toBool());
6048 void tst_qqmlecmascript::sharedAttachedObject()
6050 QQmlComponent component(&engine, testFileUrl("sharedAttachedObject.qml"));
6051 QObject *o = component.create();
6053 QCOMPARE(o->property("test1").toBool(), true);
6054 QCOMPARE(o->property("test2").toBool(), true);
6059 void tst_qqmlecmascript::objectName()
6061 QQmlComponent component(&engine, testFileUrl("objectName.qml"));
6062 QObject *o = component.create();
6065 QCOMPARE(o->property("test1").toString(), QString("hello"));
6066 QCOMPARE(o->property("test2").toString(), QString("ell"));
6068 o->setObjectName("world");
6070 QCOMPARE(o->property("test1").toString(), QString("world"));
6071 QCOMPARE(o->property("test2").toString(), QString("orl"));
6076 void tst_qqmlecmascript::writeRemovesBinding()
6078 QQmlComponent component(&engine, testFileUrl("writeRemovesBinding.qml"));
6079 QObject *o = component.create();
6082 QCOMPARE(o->property("test").toBool(), true);
6087 // Test bindings assigned to alias properties actually assign to the alias' target
6088 void tst_qqmlecmascript::aliasBindingsAssignCorrectly()
6090 QQmlComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml"));
6091 QObject *o = component.create();
6094 QCOMPARE(o->property("test").toBool(), true);
6099 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
6100 void tst_qqmlecmascript::aliasBindingsOverrideTarget()
6103 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml"));
6104 QObject *o = component.create();
6107 QCOMPARE(o->property("test").toBool(), true);
6113 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml"));
6114 QObject *o = component.create();
6117 QCOMPARE(o->property("test").toBool(), true);
6123 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml"));
6124 QObject *o = component.create();
6127 QCOMPARE(o->property("test").toBool(), true);
6133 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
6134 void tst_qqmlecmascript::aliasWritesOverrideBindings()
6137 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml"));
6138 QObject *o = component.create();
6141 QCOMPARE(o->property("test").toBool(), true);
6147 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml"));
6148 QObject *o = component.create();
6151 QCOMPARE(o->property("test").toBool(), true);
6157 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml"));
6158 QObject *o = component.create();
6161 QCOMPARE(o->property("test").toBool(), true);
6167 // Allow an alais to a composite element
6169 void tst_qqmlecmascript::aliasToCompositeElement()
6171 QQmlComponent component(&engine, testFileUrl("aliasToCompositeElement.qml"));
6173 QObject *object = component.create();
6174 QVERIFY(object != 0);
6179 void tst_qqmlecmascript::qtbug_20344()
6181 QQmlComponent component(&engine, testFileUrl("qtbug_20344.qml"));
6183 QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
6184 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6186 QObject *object = component.create();
6187 QVERIFY(object != 0);
6192 void tst_qqmlecmascript::revisionErrors()
6195 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml"));
6196 QString url = component.url().toString();
6198 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6199 QString warning2 = url + ":11: ReferenceError: prop2 is not defined";
6200 QString warning3 = url + ":13: ReferenceError: method2 is not defined";
6202 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6203 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6204 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6205 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6206 QVERIFY(object != 0);
6210 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml"));
6211 QString url = component.url().toString();
6213 // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
6214 // method2, prop2 from MyRevisionedClass not available
6215 // method4, prop4 from MyRevisionedSubclass not available
6216 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6217 QString warning2 = url + ":14: ReferenceError: prop2 is not defined";
6218 QString warning3 = url + ":10: ReferenceError: prop4 is not defined";
6219 QString warning4 = url + ":16: ReferenceError: prop4 is not defined";
6220 QString warning5 = url + ":20: ReferenceError: method2 is not defined";
6222 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6223 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6224 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6225 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
6226 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
6227 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6228 QVERIFY(object != 0);
6232 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml"));
6233 QString url = component.url().toString();
6235 // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
6236 // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
6237 QString warning1 = url + ":30: ReferenceError: methodD is not defined";
6238 QString warning2 = url + ":10: ReferenceError: propD is not defined";
6239 QString warning3 = url + ":20: ReferenceError: propD is not defined";
6240 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6241 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6242 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6243 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6244 QVERIFY(object != 0);
6249 void tst_qqmlecmascript::revision()
6252 QQmlComponent component(&engine, testFileUrl("metaobjectRevision.qml"));
6253 QString url = component.url().toString();
6255 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6256 QVERIFY(object != 0);
6260 QQmlComponent component(&engine, testFileUrl("metaobjectRevision2.qml"));
6261 QString url = component.url().toString();
6263 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6264 QVERIFY(object != 0);
6268 QQmlComponent component(&engine, testFileUrl("metaobjectRevision3.qml"));
6269 QString url = component.url().toString();
6271 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6272 QVERIFY(object != 0);
6275 // Test that non-root classes can resolve revisioned methods
6277 QQmlComponent component(&engine, testFileUrl("metaobjectRevision4.qml"));
6279 QObject *object = component.create();
6280 QVERIFY(object != 0);
6281 QCOMPARE(object->property("test").toReal(), 11.);
6286 void tst_qqmlecmascript::realToInt()
6288 QQmlComponent component(&engine, testFileUrl("realToInt.qml"));
6289 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6290 QVERIFY(object != 0);
6292 QMetaObject::invokeMethod(object, "test1");
6293 QCOMPARE(object->value(), int(4));
6294 QMetaObject::invokeMethod(object, "test2");
6295 QCOMPARE(object->value(), int(8));
6298 void tst_qqmlecmascript::urlProperty()
6301 QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml"));
6302 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6303 QVERIFY(object != 0);
6304 object->setStringProperty("http://qt-project.org");
6305 QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
6306 QCOMPARE(object->intProperty(), 123);
6307 QCOMPARE(object->value(), 1);
6308 QCOMPARE(object->property("result").toBool(), true);
6312 void tst_qqmlecmascript::urlPropertyWithEncoding()
6315 QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml"));
6316 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6317 QVERIFY(object != 0);
6318 object->setStringProperty("http://qt-project.org");
6320 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6321 QCOMPARE(object->urlProperty(), encoded);
6322 QCOMPARE(object->value(), 0); // Interpreting URL as string yields canonicalised version
6323 QCOMPARE(object->property("result").toBool(), true);
6327 void tst_qqmlecmascript::urlListPropertyWithEncoding()
6330 QQmlComponent component(&engine, testFileUrl("urlListProperty.qml"));
6331 QObject *object = component.create();
6332 QVERIFY(object != 0);
6333 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
6334 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
6335 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
6336 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
6337 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0);
6339 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6340 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded));
6341 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded));
6342 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6343 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6348 void tst_qqmlecmascript::dynamicString()
6350 QQmlComponent component(&engine, testFileUrl("dynamicString.qml"));
6351 QObject *object = component.create();
6352 QVERIFY(object != 0);
6353 QCOMPARE(object->property("stringProperty").toString(),
6354 QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
6357 void tst_qqmlecmascript::deleteLaterObjectMethodCall()
6359 QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml"));
6360 QObject *object = component.create();
6361 QVERIFY(object != 0);
6364 void tst_qqmlecmascript::automaticSemicolon()
6366 QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml"));
6367 QObject *object = component.create();
6368 QVERIFY(object != 0);
6371 void tst_qqmlecmascript::unaryExpression()
6373 QQmlComponent component(&engine, testFileUrl("unaryExpression.qml"));
6374 QObject *object = component.create();
6375 QVERIFY(object != 0);
6378 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
6379 void tst_qqmlecmascript::doubleEvaluate()
6381 QQmlComponent component(&engine, testFileUrl("doubleEvaluate.qml"));
6382 QObject *object = component.create();
6383 QVERIFY(object != 0);
6384 WriteCounter *wc = qobject_cast<WriteCounter *>(object);
6386 QCOMPARE(wc->count(), 1);
6388 wc->setProperty("x", 9);
6390 QCOMPARE(wc->count(), 2);
6395 static QStringList messages;
6396 static void captureMsgHandler(QtMsgType, const char *msg)
6398 messages.append(QLatin1String(msg));
6401 void tst_qqmlecmascript::nonNotifyable()
6403 QV4Compiler::enableV4(false);
6404 QQmlComponent component(&engine, testFileUrl("nonNotifyable.qml"));
6405 QV4Compiler::enableV4(true);
6407 QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
6409 QObject *object = component.create();
6410 qInstallMsgHandler(old);
6412 QVERIFY(object != 0);
6414 QString expected1 = QLatin1String("QQmlExpression: Expression ") +
6415 component.url().toString() +
6416 QLatin1String(":5 depends on non-NOTIFYable properties:");
6417 QString expected2 = QLatin1String(" ") +
6418 QLatin1String(object->metaObject()->className()) +
6419 QLatin1String("::value");
6421 QCOMPARE(messages.length(), 2);
6422 QCOMPARE(messages.at(0), expected1);
6423 QCOMPARE(messages.at(1), expected2);
6428 void tst_qqmlecmascript::forInLoop()
6430 QQmlComponent component(&engine, testFileUrl("forInLoop.qml"));
6431 QObject *object = component.create();
6432 QVERIFY(object != 0);
6434 QMetaObject::invokeMethod(object, "listProperty");
6436 QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
6437 QCOMPARE(r.size(), 3);
6438 QCOMPARE(r[0],QLatin1String("0=obj1"));
6439 QCOMPARE(r[1],QLatin1String("1=obj2"));
6440 QCOMPARE(r[2],QLatin1String("2=obj3"));
6442 //TODO: should test for in loop for other objects (such as QObjects) as well.
6447 // An object the binding depends on is deleted while the binding is still running
6448 void tst_qqmlecmascript::deleteWhileBindingRunning()
6450 QQmlComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml"));
6451 QObject *object = component.create();
6452 QVERIFY(object != 0);
6456 void tst_qqmlecmascript::qtbug_22679()
6459 object.setStringProperty(QLatin1String("Please work correctly"));
6460 engine.rootContext()->setContextProperty("contextProp", &object);
6462 QQmlComponent component(&engine, testFileUrl("qtbug_22679.qml"));
6463 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6464 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6466 QObject *o = component.create();
6468 QCOMPARE(warningsSpy.count(), 0);
6472 void tst_qqmlecmascript::qtbug_22843_data()
6474 QTest::addColumn<bool>("library");
6476 QTest::newRow("without .pragma library") << false;
6477 QTest::newRow("with .pragma library") << true;
6480 void tst_qqmlecmascript::qtbug_22843()
6482 QFETCH(bool, library);
6484 QString fileName("qtbug_22843");
6486 fileName += QLatin1String(".library");
6487 fileName += QLatin1String(".qml");
6489 QQmlComponent component(&engine, testFileUrl(fileName));
6490 QString url = component.url().toString();
6491 QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )");
6492 QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'");
6494 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6495 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6496 for (int x = 0; x < 3; ++x) {
6497 warningsSpy.clear();
6498 // For libraries, only the first import attempt should produce a
6499 // SyntaxError warning; subsequent component creation should not
6500 // attempt to reload the script.
6501 bool expectSyntaxError = !library || (x == 0);
6502 if (expectSyntaxError)
6503 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
6504 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
6505 QObject *object = component.create();
6506 QVERIFY(object != 0);
6507 QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
6513 void tst_qqmlecmascript::switchStatement()
6516 QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml"));
6517 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6518 QVERIFY(object != 0);
6520 // `object->value()' is the number of executed statements
6522 object->setStringProperty("A");
6523 QCOMPARE(object->value(), 5);
6525 object->setStringProperty("S");
6526 QCOMPARE(object->value(), 3);
6528 object->setStringProperty("D");
6529 QCOMPARE(object->value(), 3);
6531 object->setStringProperty("F");
6532 QCOMPARE(object->value(), 4);
6534 object->setStringProperty("something else");
6535 QCOMPARE(object->value(), 1);
6539 QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml"));
6540 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6541 QVERIFY(object != 0);
6543 // `object->value()' is the number of executed statements
6545 object->setStringProperty("A");
6546 QCOMPARE(object->value(), 5);
6548 object->setStringProperty("S");
6549 QCOMPARE(object->value(), 3);
6551 object->setStringProperty("D");
6552 QCOMPARE(object->value(), 3);
6554 object->setStringProperty("F");
6555 QCOMPARE(object->value(), 3);
6557 object->setStringProperty("something else");
6558 QCOMPARE(object->value(), 4);
6562 QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml"));
6563 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6564 QVERIFY(object != 0);
6566 // `object->value()' is the number of executed statements
6568 object->setStringProperty("A");
6569 QCOMPARE(object->value(), 5);
6571 object->setStringProperty("S");
6572 QCOMPARE(object->value(), 3);
6574 object->setStringProperty("D");
6575 QCOMPARE(object->value(), 3);
6577 object->setStringProperty("F");
6578 QCOMPARE(object->value(), 3);
6580 object->setStringProperty("something else");
6581 QCOMPARE(object->value(), 6);
6585 QQmlComponent component(&engine, testFileUrl("switchStatement.4.qml"));
6587 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
6588 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6590 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6591 QVERIFY(object != 0);
6593 // `object->value()' is the number of executed statements
6595 object->setStringProperty("A");
6596 QCOMPARE(object->value(), 5);
6598 object->setStringProperty("S");
6599 QCOMPARE(object->value(), 3);
6601 object->setStringProperty("D");
6602 QCOMPARE(object->value(), 3);
6604 object->setStringProperty("F");
6605 QCOMPARE(object->value(), 3);
6607 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6609 object->setStringProperty("something else");
6613 QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml"));
6614 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6615 QVERIFY(object != 0);
6617 // `object->value()' is the number of executed statements
6619 object->setStringProperty("A");
6620 QCOMPARE(object->value(), 1);
6622 object->setStringProperty("S");
6623 QCOMPARE(object->value(), 1);
6625 object->setStringProperty("D");
6626 QCOMPARE(object->value(), 1);
6628 object->setStringProperty("F");
6629 QCOMPARE(object->value(), 1);
6631 object->setStringProperty("something else");
6632 QCOMPARE(object->value(), 1);
6636 QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml"));
6637 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6638 QVERIFY(object != 0);
6640 // `object->value()' is the number of executed statements
6642 object->setStringProperty("A");
6643 QCOMPARE(object->value(), 123);
6645 object->setStringProperty("S");
6646 QCOMPARE(object->value(), 123);
6648 object->setStringProperty("D");
6649 QCOMPARE(object->value(), 321);
6651 object->setStringProperty("F");
6652 QCOMPARE(object->value(), 321);
6654 object->setStringProperty("something else");
6655 QCOMPARE(object->value(), 0);
6659 void tst_qqmlecmascript::withStatement()
6662 QQmlComponent component(&engine, testFileUrl("withStatement.1.qml"));
6663 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6664 QVERIFY(object != 0);
6666 QCOMPARE(object->value(), 123);
6670 void tst_qqmlecmascript::tryStatement()
6673 QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml"));
6674 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6675 QVERIFY(object != 0);
6677 QCOMPARE(object->value(), 123);
6681 QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml"));
6682 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6683 QVERIFY(object != 0);
6685 QCOMPARE(object->value(), 321);
6689 QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml"));
6690 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6691 QVERIFY(object != 0);
6693 QCOMPARE(object->value(), 1);
6697 QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml"));
6698 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6699 QVERIFY(object != 0);
6701 QCOMPARE(object->value(), 1);
6705 class CppInvokableWithQObjectDerived : public QObject
6709 CppInvokableWithQObjectDerived() {}
6710 ~CppInvokableWithQObjectDerived() {}
6712 Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data)
6714 MyQmlObject *obj = new MyQmlObject();
6715 obj->setStringProperty(data);
6719 Q_INVOKABLE QString getStringProperty(MyQmlObject *obj)
6721 return obj->stringProperty();
6725 void tst_qqmlecmascript::invokableWithQObjectDerived()
6727 CppInvokableWithQObjectDerived invokable;
6731 engine.rootContext()->setContextProperty("invokable", &invokable);
6733 QQmlComponent component(&engine, testFileUrl("qobjectDerivedArgument.qml"));
6735 QObject *object = component.create();
6737 QVERIFY(object != 0);
6738 QVERIFY(object->property("result").value<bool>() == true);
6744 void tst_qqmlecmascript::realTypePrecision()
6746 // Properties and signal parameters of type real should have double precision.
6747 QQmlComponent component(&engine, testFileUrl("realTypePrecision.qml"));
6748 QScopedPointer<QObject> object(component.create());
6749 QVERIFY(object != 0);
6750 QCOMPARE(object->property("test").toDouble(), 1234567890.);
6751 QCOMPARE(object->property("test2").toDouble(), 1234567890.);
6752 QCOMPARE(object->property("test3").toDouble(), 1234567890.);
6753 QCOMPARE(object->property("test4").toDouble(), 1234567890.);
6754 QCOMPARE(object->property("test5").toDouble(), 1234567890.);
6755 QCOMPARE(object->property("test6").toDouble(), 1234567890.*2);
6758 void tst_qqmlecmascript::registeredFlagMethod()
6761 QQmlComponent component(&engine, testFileUrl("registeredFlagMethod.qml"));
6762 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6763 QVERIFY(object != 0);
6765 QCOMPARE(object->buttons(), 0);
6766 emit object->basicSignal();
6767 QCOMPARE(object->buttons(), Qt::RightButton);
6773 void tst_qqmlecmascript::replaceBinding()
6776 QQmlComponent c(&engine, testFileUrl("replaceBinding.qml"));
6777 QObject *obj = c.create();
6780 QVERIFY(obj->property("success").toBool());
6784 void tst_qqmlecmascript::deleteRootObjectInCreation()
6788 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.qml"));
6789 QObject *obj = c.create();
6791 QVERIFY(obj->property("rootIndestructible").toBool());
6792 QVERIFY(!obj->property("childDestructible").toBool());
6794 QVERIFY(obj->property("childDestructible").toBool());
6799 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.2.qml"));
6800 QObject *object = c.create();
6801 QVERIFY(object != 0);
6802 QVERIFY(object->property("testConditionsMet").toBool());
6807 void tst_qqmlecmascript::onDestruction()
6810 // Delete object manually to invoke the associated handlers,
6811 // prior to engine destruction.
6813 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
6814 QObject *obj = c.create();
6820 // In this case, the teardown of the engine causes deletion
6821 // of contexts and child items. This triggers the
6822 // onDestruction handler of a (previously .destroy()ed)
6823 // component instance. This shouldn't crash.
6825 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
6826 QObject *obj = c.create();
6831 struct EventProcessor : public QObject
6835 Q_INVOKABLE void process()
6837 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
6838 QCoreApplication::processEvents();
6842 void tst_qqmlecmascript::bindingSuppression()
6845 EventProcessor processor;
6846 engine.rootContext()->setContextProperty("pendingEvents", &processor);
6848 transientErrorsMsgCount = 0;
6849 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
6851 QQmlComponent c(&engine, testFileUrl("bindingSuppression.qml"));
6852 QObject *obj = c.create();
6856 qInstallMsgHandler(old);
6857 QCOMPARE(transientErrorsMsgCount, 0);
6860 void tst_qqmlecmascript::signalEmitted()
6863 // calling destroy on the sibling.
6865 QQmlComponent c(&engine, testFileUrl("signalEmitted.2.qml"));
6866 QObject *obj = c.create();
6868 QTRY_VERIFY(obj->property("success").toBool());
6873 // allowing gc to clean up the sibling.
6875 QQmlComponent c(&engine, testFileUrl("signalEmitted.3.qml"));
6876 QObject *obj = c.create();
6878 gc(engine); // should collect c1.
6879 QTRY_VERIFY(obj->property("success").toBool());
6884 // allowing gc to clean up the sibling after manually destroying target.
6886 QQmlComponent c(&engine, testFileUrl("signalEmitted.4.qml"));
6887 QObject *obj = c.create();
6889 gc(engine); // should collect c1.
6890 QMetaObject::invokeMethod(obj, "destroyC2");
6891 QTRY_VERIFY(obj->property("success").toBool()); // handles events (incl. delete later).
6897 void tst_qqmlecmascript::threadSignal()
6900 QQmlComponent c(&engine, testFileUrl("threadSignal.qml"));
6901 QObject *object = c.create();
6902 QVERIFY(object != 0);
6903 QTRY_VERIFY(object->property("passed").toBool());
6907 QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml"));
6908 QObject *object = c.create();
6909 QVERIFY(object != 0);
6910 QSignalSpy doneSpy(object, SIGNAL(done(const QString &)));
6911 QMetaObject::invokeMethod(object, "doIt");
6912 QTRY_VERIFY(object->property("passed").toBool());
6913 QCOMPARE(doneSpy.count(), 1);
6918 // ensure that the qqmldata::destroyed() handler doesn't cause problems
6919 void tst_qqmlecmascript::qqmldataDestroyed()
6921 // gc cleans up a qobject, later the qqmldata destroyed handler will run.
6923 QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.qml"));
6924 QObject *object = c.create();
6925 QVERIFY(object != 0);
6926 // now gc causing the collection of the dynamically constructed object.
6927 engine.collectGarbage();
6928 engine.collectGarbage();
6929 // now process events to allow deletion (calling qqmldata::destroyed())
6930 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
6931 QCoreApplication::processEvents();
6936 // in this case, the object has CPP ownership, and the gc will
6937 // be triggered during its beginCreate stage.
6939 QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.2.qml"));
6940 QObject *object = c.create();
6941 QVERIFY(object != 0);
6942 QVERIFY(object->property("testConditionsMet").toBool());
6943 // the gc() within the handler will have triggered the weak
6944 // qobject reference callback. If that incorrectly disposes
6945 // the handle, when the qqmldata::destroyed() handler is
6946 // called due to object deletion we will see a crash.
6948 // shouldn't have crashed.
6952 void tst_qqmlecmascript::secondAlias()
6954 QQmlComponent c(&engine, testFileUrl("secondAlias.qml"));
6955 QObject *object = c.create();
6956 QVERIFY(object != 0);
6957 QCOMPARE(object->property("test").toInt(), 200);
6961 // An alias to a var property works
6962 void tst_qqmlecmascript::varAlias()
6964 QQmlComponent c(&engine, testFileUrl("varAlias.qml"));
6965 QObject *object = c.create();
6966 QVERIFY(object != 0);
6967 QCOMPARE(object->property("test").toInt(), 192);
6971 // Used to trigger an assert in the lazy meta object creation stage
6972 void tst_qqmlecmascript::overrideDataAssert()
6974 QQmlComponent c(&engine, testFileUrl("overrideDataAssert.qml"));
6975 QObject *object = c.create();
6976 QVERIFY(object != 0);
6977 object->metaObject();
6981 QTEST_MAIN(tst_qqmlecmascript)
6983 #include "tst_qqmlecmascript.moc"