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")
3611 void tst_qqmlecmascript::moduleApi()
3613 QFETCH(QUrl, testfile);
3614 QFETCH(QString, errorMessage);
3615 QFETCH(QStringList, warningMessages);
3616 QFETCH(QStringList, readProperties);
3617 QFETCH(QVariantList, readExpectedValues);
3618 QFETCH(QStringList, writeProperties);
3619 QFETCH(QVariantList, writeValues);
3620 QFETCH(QStringList, readBackProperties);
3621 QFETCH(QVariantList, readBackExpectedValues);
3623 QQmlComponent component(&engine, testfile);
3625 if (!errorMessage.isEmpty())
3626 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3628 if (warningMessages.size())
3629 foreach (const QString &warning, warningMessages)
3630 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3632 QObject *object = component.create();
3633 if (!errorMessage.isEmpty()) {
3634 QVERIFY(object == 0);
3636 QVERIFY(object != 0);
3637 for (int i = 0; i < readProperties.size(); ++i)
3638 QCOMPARE(object->property(readProperties.at(i).toLatin1().constData()), readExpectedValues.at(i));
3639 for (int i = 0; i < writeProperties.size(); ++i)
3640 QVERIFY(object->setProperty(writeProperties.at(i).toLatin1().constData(), writeValues.at(i)));
3641 for (int i = 0; i < readBackProperties.size(); ++i)
3642 QCOMPARE(object->property(readBackProperties.at(i).toLatin1().constData()), readBackExpectedValues.at(i));
3647 void tst_qqmlecmascript::importScripts_data()
3649 QTest::addColumn<QUrl>("testfile");
3650 QTest::addColumn<QString>("errorMessage");
3651 QTest::addColumn<QStringList>("warningMessages");
3652 QTest::addColumn<QStringList>("propertyNames");
3653 QTest::addColumn<QVariantList>("propertyValues");
3655 QTest::newRow("basic functionality")
3656 << testFileUrl("jsimport/testImport.qml")
3659 << (QStringList() << QLatin1String("importedScriptStringValue")
3660 << QLatin1String("importedScriptFunctionValue")
3661 << QLatin1String("importedModuleAttachedPropertyValue")
3662 << QLatin1String("importedModuleEnumValue"))
3663 << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3668 QTest::newRow("import scoping")
3669 << testFileUrl("jsimport/testImportScoping.qml")
3672 << (QStringList() << QLatin1String("componentError"))
3673 << (QVariantList() << QVariant(5));
3675 QTest::newRow("parent scope shouldn't be inherited by import with imports")
3676 << testFileUrl("jsimportfail/failOne.qml")
3678 << (QStringList() << QString(testFileUrl("jsimportfail/failOne.qml").toString() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
3679 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3680 << (QVariantList() << QVariant(QString()));
3682 QTest::newRow("javascript imports in an import should be private to the import scope")
3683 << testFileUrl("jsimportfail/failTwo.qml")
3685 << (QStringList() << QString(testFileUrl("jsimportfail/failTwo.qml").toString() + QLatin1String(":6: ReferenceError: ImportOneJs is not defined")))
3686 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3687 << (QVariantList() << QVariant(QString()));
3689 QTest::newRow("module imports in an import should be private to the import scope")
3690 << testFileUrl("jsimportfail/failThree.qml")
3692 << (QStringList() << QString(testFileUrl("jsimportfail/failThree.qml").toString() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
3693 << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
3694 << (QVariantList() << QVariant(false));
3696 QTest::newRow("typenames in an import should be private to the import scope")
3697 << testFileUrl("jsimportfail/failFour.qml")
3699 << (QStringList() << QString(testFileUrl("jsimportfail/failFour.qml").toString() + QLatin1String(":6: ReferenceError: JsQtTest is not defined")))
3700 << (QStringList() << QLatin1String("importedModuleEnumValue"))
3701 << (QVariantList() << QVariant(0));
3703 QTest::newRow("import with imports has it's own activation scope")
3704 << testFileUrl("jsimportfail/failFive.qml")
3706 << (QStringList() << QString(testFileUrl("jsimportfail/importWithImports.js").toString() + QLatin1String(":8: ReferenceError: Component is not defined")))
3707 << (QStringList() << QLatin1String("componentError"))
3708 << (QVariantList() << QVariant(0));
3710 QTest::newRow("import pragma library script")
3711 << testFileUrl("jsimport/testImportPragmaLibrary.qml")
3714 << (QStringList() << QLatin1String("testValue"))
3715 << (QVariantList() << QVariant(31));
3717 QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3718 << testFileUrl("jsimportfail/testImportPragmaLibrary.qml")
3720 << (QStringList() << QString(testFileUrl("jsimportfail/importPragmaLibrary.js").toString() + QLatin1String(":6: ReferenceError: Component is not defined")))
3721 << (QStringList() << QLatin1String("testValue"))
3722 << (QVariantList() << QVariant(0));
3724 QTest::newRow("import pragma library script which has an import")
3725 << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
3728 << (QStringList() << QLatin1String("testValue"))
3729 << (QVariantList() << QVariant(55));
3731 QTest::newRow("import pragma library script which has a pragma library import")
3732 << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3735 << (QStringList() << QLatin1String("testValue"))
3736 << (QVariantList() << QVariant(18));
3738 QTest::newRow("import module api into js import")
3739 << testFileUrl("jsimport/testImportModuleApi.qml")
3742 << (QStringList() << QLatin1String("testValue"))
3743 << (QVariantList() << QVariant(20));
3745 QTest::newRow("import module which exports a script")
3746 << testFileUrl("jsimport/testJsImport.qml")
3749 << (QStringList() << QLatin1String("importedScriptStringValue")
3750 << QLatin1String("renamedScriptStringValue")
3751 << QLatin1String("reimportedScriptStringValue"))
3752 << (QVariantList() << QVariant(QString("Hello"))
3753 << QVariant(QString("Hello"))
3754 << QVariant(QString("Hello")));
3757 void tst_qqmlecmascript::importScripts()
3759 QFETCH(QUrl, testfile);
3760 QFETCH(QString, errorMessage);
3761 QFETCH(QStringList, warningMessages);
3762 QFETCH(QStringList, propertyNames);
3763 QFETCH(QVariantList, propertyValues);
3765 QQmlComponent component(&engine, testfile);
3767 if (!errorMessage.isEmpty())
3768 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3770 if (warningMessages.size())
3771 foreach (const QString &warning, warningMessages)
3772 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3774 QObject *object = component.create();
3775 if (!errorMessage.isEmpty()) {
3776 QVERIFY(object == 0);
3778 QVERIFY(object != 0);
3779 for (int i = 0; i < propertyNames.size(); ++i)
3780 QCOMPARE(object->property(propertyNames.at(i).toLatin1().constData()), propertyValues.at(i));
3785 void tst_qqmlecmascript::scarceResources_other()
3787 /* These tests require knowledge of state, since we test values after
3788 performing signal or function invocation. */
3790 QPixmap origPixmap(100, 100);
3791 origPixmap.fill(Qt::blue);
3792 QString srp_name, expectedWarning;
3793 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
3794 ScarceResourceObject *eo = 0;
3796 QObject *object = 0;
3798 /* property var semantics */
3800 // test that scarce resources are handled properly in signal invocation
3801 QQmlComponent varComponentTen(&engine, testFileUrl("scarceResourceSignal.var.qml"));
3802 object = varComponentTen.create();
3803 srsc = object->findChild<QObject*>("srsc");
3805 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3806 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3807 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3808 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3809 QMetaObject::invokeMethod(srsc, "testSignal");
3810 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3811 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3812 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3813 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3814 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3815 QVERIFY(srsc->property("scarceResourceCopy").isValid());
3816 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3817 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3818 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3819 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3822 // test that scarce resources are handled properly from js functions in qml files
3823 QQmlComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml"));
3824 object = varComponentEleven.create();
3825 QVERIFY(object != 0);
3826 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3827 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3828 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3829 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3830 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3831 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3832 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3833 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3834 QMetaObject::invokeMethod(object, "releaseScarceResource");
3835 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3836 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3837 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3838 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3841 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3842 QQmlComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
3843 object = varComponentTwelve.create();
3844 QVERIFY(object != 0);
3845 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3846 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3847 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3848 srp_name = object->property("srp_name").toString();
3849 expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3850 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3851 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3852 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3853 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3854 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3855 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3858 // test that if an Item which has JS ownership but has a scarce resource property is garbage collected,
3859 // that the scarce resource is removed from the engine's list of scarce resources to clean up.
3860 QQmlComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml"));
3861 object = varComponentThirteen.create();
3862 QVERIFY(object != 0);
3863 QVERIFY(!object->property("varProperty").isValid()); // not assigned yet
3864 QMetaObject::invokeMethod(object, "assignVarProperty");
3865 QVERIFY(ep->scarceResources.isEmpty()); // the scarce resource is a VME property.
3866 QMetaObject::invokeMethod(object, "deassignVarProperty");
3867 QVERIFY(ep->scarceResources.isEmpty()); // should still be empty; the resource should have been released on gc.
3870 /* property variant semantics */
3872 // test that scarce resources are handled properly in signal invocation
3873 QQmlComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml"));
3874 object = variantComponentTen.create();
3875 QVERIFY(object != 0);
3876 srsc = object->findChild<QObject*>("srsc");
3878 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3879 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3880 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3881 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3882 QMetaObject::invokeMethod(srsc, "testSignal");
3883 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3884 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3885 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3886 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3887 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3888 QVERIFY(srsc->property("scarceResourceCopy").isValid());
3889 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3890 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3891 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3892 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3895 // test that scarce resources are handled properly from js functions in qml files
3896 QQmlComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml"));
3897 object = variantComponentEleven.create();
3898 QVERIFY(object != 0);
3899 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3900 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3901 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3902 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3903 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3904 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3905 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3906 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3907 QMetaObject::invokeMethod(object, "releaseScarceResource");
3908 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3909 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3910 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3911 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3914 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3915 QQmlComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml"));
3916 object = variantComponentTwelve.create();
3917 QVERIFY(object != 0);
3918 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3919 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3920 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3921 srp_name = object->property("srp_name").toString();
3922 expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3923 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3924 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3925 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3926 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3927 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3928 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3932 void tst_qqmlecmascript::scarceResources_data()
3934 QTest::addColumn<QUrl>("qmlFile");
3935 QTest::addColumn<bool>("readDetachStatus");
3936 QTest::addColumn<bool>("expectedDetachStatus");
3937 QTest::addColumn<QStringList>("propertyNames");
3938 QTest::addColumn<QVariantList>("expectedValidity");
3939 QTest::addColumn<QVariantList>("expectedValues");
3940 QTest::addColumn<QStringList>("expectedErrors");
3942 QPixmap origPixmap(100, 100);
3943 origPixmap.fill(Qt::blue);
3945 /* property var semantics */
3947 // in the following three cases, the instance created from the component
3948 // has a property which is a copy of the scarce resource; hence, the
3949 // resource should NOT be detached prior to deletion of the object instance,
3950 // unless the resource is destroyed explicitly.
3951 QTest::newRow("var: import scarce resource copy directly")
3952 << testFileUrl("scarceResourceCopy.var.qml")
3954 << false // won't be detached, because assigned to property and not explicitly released
3955 << (QStringList() << QLatin1String("scarceResourceCopy"))
3956 << (QList<QVariant>() << true)
3957 << (QList<QVariant>() << origPixmap)
3960 QTest::newRow("var: import scarce resource copy from JS")
3961 << testFileUrl("scarceResourceCopyFromJs.var.qml")
3963 << false // won't be detached, because assigned to property and not explicitly released
3964 << (QStringList() << QLatin1String("scarceResourceCopy"))
3965 << (QList<QVariant>() << true)
3966 << (QList<QVariant>() << origPixmap)
3969 QTest::newRow("var: import released scarce resource copy from JS")
3970 << testFileUrl("scarceResourceDestroyedCopy.var.qml")
3972 << true // explicitly released, so it will be detached
3973 << (QStringList() << QLatin1String("scarceResourceCopy"))
3974 << (QList<QVariant>() << false)
3975 << (QList<QVariant>() << QVariant())
3978 // in the following three cases, no other copy should exist in memory,
3979 // and so it should be detached (unless explicitly preserved).
3980 QTest::newRow("var: import auto-release SR from JS in binding side-effect")
3981 << testFileUrl("scarceResourceTest.var.qml")
3983 << true // auto released, so it will be detached
3984 << (QStringList() << QLatin1String("scarceResourceTest"))
3985 << (QList<QVariant>() << true)
3986 << (QList<QVariant>() << QVariant(100))
3988 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
3989 << testFileUrl("scarceResourceTestPreserve.var.qml")
3991 << false // won't be detached because we explicitly preserve it
3992 << (QStringList() << QLatin1String("scarceResourceTest"))
3993 << (QList<QVariant>() << true)
3994 << (QList<QVariant>() << QVariant(100))
3996 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
3997 << testFileUrl("scarceResourceTestMultiple.var.qml")
3999 << true // will be detached because all resources were released manually or automatically.
4000 << (QStringList() << QLatin1String("scarceResourceTest"))
4001 << (QList<QVariant>() << true)
4002 << (QList<QVariant>() << QVariant(100))
4005 // In the following three cases, test that scarce resources are handled
4006 // correctly for imports.
4007 QTest::newRow("var: import with no binding")
4008 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4009 << false // cannot check detach status.
4012 << QList<QVariant>()
4013 << QList<QVariant>()
4015 QTest::newRow("var: import with binding without explicit preserve")
4016 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4019 << (QStringList() << QLatin1String("scarceResourceCopy"))
4020 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4021 << (QList<QVariant>() << QVariant())
4023 QTest::newRow("var: import with explicit release after binding evaluation")
4024 << testFileUrl("scarceResourceCopyImport.var.qml")
4027 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4028 << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated.
4029 << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true))
4031 QTest::newRow("var: import with different js objects")
4032 << testFileUrl("scarceResourceCopyImportDifferent.var.qml")
4035 << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4036 << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4037 << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false))
4039 QTest::newRow("var: import with different js objects and explicit release")
4040 << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml")
4043 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4044 << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4045 << (QList<QVariant>() << QVariant(origPixmap) << QVariant())
4047 QTest::newRow("var: import with same js objects and explicit release")
4048 << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml")
4051 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4052 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4053 << (QList<QVariant>() << QVariant() << QVariant())
4055 QTest::newRow("var: binding with same js objects and explicit release")
4056 << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml")
4059 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4060 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4061 << (QList<QVariant>() << QVariant() << QVariant())
4065 /* property variant semantics */
4067 // in the following three cases, the instance created from the component
4068 // has a property which is a copy of the scarce resource; hence, the
4069 // resource should NOT be detached prior to deletion of the object instance,
4070 // unless the resource is destroyed explicitly.
4071 QTest::newRow("variant: import scarce resource copy directly")
4072 << testFileUrl("scarceResourceCopy.variant.qml")
4074 << false // won't be detached, because assigned to property and not explicitly released
4075 << (QStringList() << QLatin1String("scarceResourceCopy"))
4076 << (QList<QVariant>() << true)
4077 << (QList<QVariant>() << origPixmap)
4080 QTest::newRow("variant: import scarce resource copy from JS")
4081 << testFileUrl("scarceResourceCopyFromJs.variant.qml")
4083 << false // won't be detached, because assigned to property and not explicitly released
4084 << (QStringList() << QLatin1String("scarceResourceCopy"))
4085 << (QList<QVariant>() << true)
4086 << (QList<QVariant>() << origPixmap)
4089 QTest::newRow("variant: import released scarce resource copy from JS")
4090 << testFileUrl("scarceResourceDestroyedCopy.variant.qml")
4092 << true // explicitly released, so it will be detached
4093 << (QStringList() << QLatin1String("scarceResourceCopy"))
4094 << (QList<QVariant>() << false)
4095 << (QList<QVariant>() << QVariant())
4098 // in the following three cases, no other copy should exist in memory,
4099 // and so it should be detached (unless explicitly preserved).
4100 QTest::newRow("variant: import auto-release SR from JS in binding side-effect")
4101 << testFileUrl("scarceResourceTest.variant.qml")
4103 << true // auto released, so it will be detached
4104 << (QStringList() << QLatin1String("scarceResourceTest"))
4105 << (QList<QVariant>() << true)
4106 << (QList<QVariant>() << QVariant(100))
4108 QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect")
4109 << testFileUrl("scarceResourceTestPreserve.variant.qml")
4111 << false // won't be detached because we explicitly preserve it
4112 << (QStringList() << QLatin1String("scarceResourceTest"))
4113 << (QList<QVariant>() << true)
4114 << (QList<QVariant>() << QVariant(100))
4116 QTest::newRow("variant: import multiple scarce resources")
4117 << testFileUrl("scarceResourceTestMultiple.variant.qml")
4119 << true // will be detached because all resources were released manually or automatically.
4120 << (QStringList() << QLatin1String("scarceResourceTest"))
4121 << (QList<QVariant>() << true)
4122 << (QList<QVariant>() << QVariant(100))
4125 // In the following three cases, test that scarce resources are handled
4126 // correctly for imports.
4127 QTest::newRow("variant: import with no binding")
4128 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4129 << false // cannot check detach status.
4132 << QList<QVariant>()
4133 << QList<QVariant>()
4135 QTest::newRow("variant: import with binding without explicit preserve")
4136 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4139 << (QStringList() << QLatin1String("scarceResourceCopy"))
4140 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4141 << (QList<QVariant>() << QVariant())
4143 QTest::newRow("variant: import with explicit release after binding evaluation")
4144 << testFileUrl("scarceResourceCopyImport.variant.qml")
4147 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo"))
4148 << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies.
4149 << (QList<QVariant>() << origPixmap << origPixmap << QVariant())
4153 void tst_qqmlecmascript::scarceResources()
4155 QFETCH(QUrl, qmlFile);
4156 QFETCH(bool, readDetachStatus);
4157 QFETCH(bool, expectedDetachStatus);
4158 QFETCH(QStringList, propertyNames);
4159 QFETCH(QVariantList, expectedValidity);
4160 QFETCH(QVariantList, expectedValues);
4161 QFETCH(QStringList, expectedErrors);
4163 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4164 ScarceResourceObject *eo = 0;
4165 QObject *object = 0;
4167 QQmlComponent c(&engine, qmlFile);
4168 object = c.create();
4169 QVERIFY(object != 0);
4170 for (int i = 0; i < propertyNames.size(); ++i) {
4171 QString prop = propertyNames.at(i);
4172 bool validity = expectedValidity.at(i).toBool();
4173 QVariant value = expectedValues.at(i);
4175 QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity);
4176 if (value.type() == QVariant::Int) {
4177 QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt());
4178 } else if (value.type() == QVariant::Pixmap) {
4179 QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>());
4183 if (readDetachStatus) {
4184 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4185 QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
4188 QVERIFY(ep->scarceResources.isEmpty());
4192 void tst_qqmlecmascript::propertyChangeSlots()
4194 // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
4195 QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml"));
4196 QObject *object = component.create();
4197 QVERIFY(object != 0);
4200 // ensure that invalid property names fail properly.
4201 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4202 QQmlComponent e1(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.1.qml"));
4203 QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
4204 QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
4205 object = e1.create();
4206 QVERIFY(object == 0);
4209 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4210 QQmlComponent e2(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.2.qml"));
4211 expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
4212 QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
4213 object = e2.create();
4214 QVERIFY(object == 0);
4217 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4218 QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml"));
4219 expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
4220 QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
4221 object = e3.create();
4222 QVERIFY(object == 0);
4225 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4226 QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml"));
4227 expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
4228 QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
4229 object = e4.create();
4230 QVERIFY(object == 0);
4234 void tst_qqmlecmascript::propertyVar_data()
4236 QTest::addColumn<QUrl>("qmlFile");
4239 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyVar.1.qml");
4240 QTest::newRow("non-bindable object changed") << testFileUrl("propertyVar.2.qml");
4241 QTest::newRow("primitive changed") << testFileUrl("propertyVar.3.qml");
4242 QTest::newRow("javascript array modification") << testFileUrl("propertyVar.4.qml");
4243 QTest::newRow("javascript map modification") << testFileUrl("propertyVar.5.qml");
4244 QTest::newRow("javascript array assignment") << testFileUrl("propertyVar.6.qml");
4245 QTest::newRow("javascript map assignment") << testFileUrl("propertyVar.7.qml");
4246 QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml");
4247 QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml");
4248 QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml");
4249 QTest::newRow("javascript function assignment") << testFileUrl("propertyVar.11.qml");
4250 QTest::newRow("javascript special assignment") << testFileUrl("propertyVar.12.qml");
4251 QTest::newRow("declarative binding assignment") << testFileUrl("propertyVar.13.qml");
4252 QTest::newRow("imperative binding assignment") << testFileUrl("propertyVar.14.qml");
4253 QTest::newRow("stored binding assignment") << testFileUrl("propertyVar.15.qml");
4254 QTest::newRow("function expression binding assignment") << testFileUrl("propertyVar.16.qml");
4257 void tst_qqmlecmascript::propertyVar()
4259 QFETCH(QUrl, qmlFile);
4261 QQmlComponent component(&engine, qmlFile);
4262 QObject *object = component.create();
4263 QVERIFY(object != 0);
4265 QCOMPARE(object->property("test").toBool(), true);
4270 void tst_qqmlecmascript::propertyQJSValue_data()
4272 QTest::addColumn<QUrl>("qmlFile");
4275 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyQJSValue.1.qml");
4276 QTest::newRow("non-bindable object changed") << testFileUrl("propertyQJSValue.2.qml");
4277 QTest::newRow("primitive changed") << testFileUrl("propertyQJSValue.3.qml");
4278 QTest::newRow("javascript array modification") << testFileUrl("propertyQJSValue.4.qml");
4279 QTest::newRow("javascript map modification") << testFileUrl("propertyQJSValue.5.qml");
4280 QTest::newRow("javascript array assignment") << testFileUrl("propertyQJSValue.6.qml");
4281 QTest::newRow("javascript map assignment") << testFileUrl("propertyQJSValue.7.qml");
4282 QTest::newRow("literal property assignment") << testFileUrl("propertyQJSValue.8.qml");
4283 QTest::newRow("qobject property assignment") << testFileUrl("propertyQJSValue.9.qml");
4284 QTest::newRow("base class var property assignment") << testFileUrl("propertyQJSValue.10.qml");
4285 QTest::newRow("javascript function assignment") << testFileUrl("propertyQJSValue.11.qml");
4286 QTest::newRow("javascript special assignment") << testFileUrl("propertyQJSValue.12.qml");
4287 QTest::newRow("declarative binding assignment") << testFileUrl("propertyQJSValue.13.qml");
4288 QTest::newRow("imperative binding assignment") << testFileUrl("propertyQJSValue.14.qml");
4289 QTest::newRow("stored binding assignment") << testFileUrl("propertyQJSValue.15.qml");
4290 QTest::newRow("javascript function binding") << testFileUrl("propertyQJSValue.16.qml");
4292 QTest::newRow("reset property") << testFileUrl("propertyQJSValue.reset.qml");
4293 QTest::newRow("reset property in binding") << testFileUrl("propertyQJSValue.bindingreset.qml");
4296 void tst_qqmlecmascript::propertyQJSValue()
4298 QFETCH(QUrl, qmlFile);
4300 QQmlComponent component(&engine, qmlFile);
4301 QObject *object = component.create();
4302 QVERIFY(object != 0);
4304 QCOMPARE(object->property("test").toBool(), true);
4309 // Tests that we can write QVariant values to var properties from C++
4310 void tst_qqmlecmascript::propertyVarCpp()
4312 QObject *object = 0;
4314 // ensure that writing to and reading from a var property from cpp works as required.
4315 // Literal values stored in var properties can be read and written as QVariants
4316 // of a specific type, whereas object values are read as QVariantMaps.
4317 QQmlComponent component(&engine, testFileUrl("propertyVarCpp.qml"));
4318 object = component.create();
4319 QVERIFY(object != 0);
4320 // assign int to property var that currently has int assigned
4321 QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
4322 QCOMPARE(object->property("varBound"), QVariant(15));
4323 QCOMPARE(object->property("intBound"), QVariant(15));
4324 QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
4325 QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
4326 // assign string to property var that current has bool assigned
4327 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
4328 QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
4329 QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
4330 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
4331 // now enforce behaviour when accessing JavaScript objects from cpp.
4332 QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
4336 static void gc(QQmlEngine &engine)
4338 engine.collectGarbage();
4339 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4340 QCoreApplication::processEvents();
4343 void tst_qqmlecmascript::propertyVarOwnership()
4345 // Referenced JS objects are not collected
4347 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.qml"));
4348 QObject *object = component.create();
4349 QVERIFY(object != 0);
4350 QCOMPARE(object->property("test").toBool(), false);
4351 QMetaObject::invokeMethod(object, "runTest");
4352 QCOMPARE(object->property("test").toBool(), true);
4355 // Referenced JS objects are not collected
4357 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml"));
4358 QObject *object = component.create();
4359 QVERIFY(object != 0);
4360 QCOMPARE(object->property("test").toBool(), false);
4361 QMetaObject::invokeMethod(object, "runTest");
4362 QCOMPARE(object->property("test").toBool(), true);
4365 // Qt objects are not collected until they've been dereferenced
4367 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml"));
4368 QObject *object = component.create();
4369 QVERIFY(object != 0);
4371 QCOMPARE(object->property("test2").toBool(), false);
4372 QCOMPARE(object->property("test2").toBool(), false);
4374 QMetaObject::invokeMethod(object, "runTest");
4375 QCOMPARE(object->property("test1").toBool(), true);
4377 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4378 QVERIFY(!referencedObject.isNull());
4380 QVERIFY(!referencedObject.isNull());
4382 QMetaObject::invokeMethod(object, "runTest2");
4383 QCOMPARE(object->property("test2").toBool(), true);
4385 QVERIFY(referencedObject.isNull());
4389 // Self reference does not prevent Qt object collection
4391 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml"));
4392 QObject *object = component.create();
4393 QVERIFY(object != 0);
4395 QCOMPARE(object->property("test").toBool(), true);
4397 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4398 QVERIFY(!referencedObject.isNull());
4400 QVERIFY(!referencedObject.isNull());
4402 QMetaObject::invokeMethod(object, "runTest");
4404 QVERIFY(referencedObject.isNull());
4408 // Garbage collection cannot result in attempted dereference of empty handle
4410 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.5.qml"));
4411 QObject *object = component.create();
4412 QVERIFY(object != 0);
4413 QMetaObject::invokeMethod(object, "runTest");
4414 QCOMPARE(object->property("test").toBool(), true);
4419 void tst_qqmlecmascript::propertyVarImplicitOwnership()
4421 // The childObject has a reference to a different QObject. We want to ensure
4422 // that the different item will not be cleaned up until required. IE, the childObject
4423 // has implicit ownership of the constructed QObject.
4424 QQmlComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml"));
4425 QObject *object = component.create();
4426 QVERIFY(object != 0);
4427 QMetaObject::invokeMethod(object, "assignCircular");
4428 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4429 QCoreApplication::processEvents();
4430 QObject *rootObject = object->property("vp").value<QObject*>();
4431 QVERIFY(rootObject != 0);
4432 QObject *childObject = rootObject->findChild<QObject*>("text");
4433 QVERIFY(childObject != 0);
4434 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4435 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4436 QMetaObject::invokeMethod(childObject, "constructQObject"); // creates a reference to a constructed QObject.
4437 QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
4438 QVERIFY(!qobjectGuard.isNull());
4439 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4440 QCoreApplication::processEvents();
4441 QVERIFY(!qobjectGuard.isNull());
4442 QMetaObject::invokeMethod(object, "deassignCircular");
4443 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4444 QCoreApplication::processEvents();
4445 QVERIFY(qobjectGuard.isNull()); // should have been collected now.
4449 void tst_qqmlecmascript::propertyVarReparent()
4451 // ensure that nothing breaks if we re-parent objects
4452 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4453 QObject *object = component.create();
4454 QVERIFY(object != 0);
4455 QMetaObject::invokeMethod(object, "assignVarProp");
4456 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4457 QCoreApplication::processEvents();
4458 QObject *rect = object->property("vp").value<QObject*>();
4459 QObject *text = rect->findChild<QObject*>("textOne");
4460 QObject *text2 = rect->findChild<QObject*>("textTwo");
4461 QWeakPointer<QObject> rectGuard(rect);
4462 QWeakPointer<QObject> textGuard(text);
4463 QWeakPointer<QObject> text2Guard(text2);
4464 QVERIFY(!rectGuard.isNull());
4465 QVERIFY(!textGuard.isNull());
4466 QVERIFY(!text2Guard.isNull());
4467 QCOMPARE(text->property("textCanary").toInt(), 11);
4468 QCOMPARE(text2->property("textCanary").toInt(), 12);
4469 // now construct an image which we will reparent.
4470 QMetaObject::invokeMethod(text2, "constructQObject");
4471 QObject *image = text2->property("vp").value<QObject*>();
4472 QWeakPointer<QObject> imageGuard(image);
4473 QVERIFY(!imageGuard.isNull());
4474 QCOMPARE(image->property("imageCanary").toInt(), 13);
4475 // now reparent the "Image" object (currently, it has JS ownership)
4476 image->setParent(text); // shouldn't be collected after deassignVp now, since has a parent.
4477 QMetaObject::invokeMethod(text2, "deassignVp");
4478 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4479 QCoreApplication::processEvents();
4480 QCOMPARE(text->property("textCanary").toInt(), 11);
4481 QCOMPARE(text2->property("textCanary").toInt(), 22);
4482 QVERIFY(!imageGuard.isNull()); // should still be alive.
4483 QCOMPARE(image->property("imageCanary").toInt(), 13); // still able to access var properties
4484 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4485 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4486 QCoreApplication::processEvents();
4487 QVERIFY(imageGuard.isNull()); // should now have been deleted, due to parent being deleted.
4491 void tst_qqmlecmascript::propertyVarReparentNullContext()
4493 // sometimes reparenting can cause problems
4494 // (eg, if the ctxt is collected, varproperties are no longer available)
4495 // this test ensures that no crash occurs in that situation.
4496 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4497 QObject *object = component.create();
4498 QVERIFY(object != 0);
4499 QMetaObject::invokeMethod(object, "assignVarProp");
4500 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4501 QCoreApplication::processEvents();
4502 QObject *rect = object->property("vp").value<QObject*>();
4503 QObject *text = rect->findChild<QObject*>("textOne");
4504 QObject *text2 = rect->findChild<QObject*>("textTwo");
4505 QWeakPointer<QObject> rectGuard(rect);
4506 QWeakPointer<QObject> textGuard(text);
4507 QWeakPointer<QObject> text2Guard(text2);
4508 QVERIFY(!rectGuard.isNull());
4509 QVERIFY(!textGuard.isNull());
4510 QVERIFY(!text2Guard.isNull());
4511 QCOMPARE(text->property("textCanary").toInt(), 11);
4512 QCOMPARE(text2->property("textCanary").toInt(), 12);
4513 // now construct an image which we will reparent.
4514 QMetaObject::invokeMethod(text2, "constructQObject");
4515 QObject *image = text2->property("vp").value<QObject*>();
4516 QWeakPointer<QObject> imageGuard(image);
4517 QVERIFY(!imageGuard.isNull());
4518 QCOMPARE(image->property("imageCanary").toInt(), 13);
4519 // now reparent the "Image" object (currently, it has JS ownership)
4520 image->setParent(object); // reparented to base object. after deassignVarProp, the ctxt will be invalid.
4521 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4522 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4523 QCoreApplication::processEvents();
4524 QVERIFY(!imageGuard.isNull()); // should still be alive.
4525 QVERIFY(!image->property("imageCanary").isValid()); // but varProperties won't be available (null context).
4527 QVERIFY(imageGuard.isNull()); // should now be dead.
4530 void tst_qqmlecmascript::propertyVarCircular()
4532 // enforce behaviour regarding circular references - ensure qdvmemo deletion.
4533 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.qml"));
4534 QObject *object = component.create();
4535 QVERIFY(object != 0);
4536 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4537 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4538 QCoreApplication::processEvents();
4539 QCOMPARE(object->property("canaryInt"), QVariant(5));
4540 QVariant canaryResourceVariant = object->property("canaryResource");
4541 QVERIFY(canaryResourceVariant.isValid());
4542 QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
4543 canaryResourceVariant = QVariant(); // invalidate it to remove one copy of the pixmap from memory.
4544 QMetaObject::invokeMethod(object, "deassignCanaryResource"); // remove one copy of the pixmap from memory
4545 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4546 QCoreApplication::processEvents();
4547 QVERIFY(!canaryResourcePixmap.isDetached()); // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
4548 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4549 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4550 QCoreApplication::processEvents();
4551 QCOMPARE(object->property("canaryInt"), QVariant(2));
4552 QCOMPARE(object->property("canaryResource"), QVariant(1));
4553 QVERIFY(canaryResourcePixmap.isDetached()); // now detached, since orig copy was member of qdvmemo which was deleted.
4557 void tst_qqmlecmascript::propertyVarCircular2()
4559 // track deletion of JS-owned parent item with Cpp-owned child
4560 // where the child has a var property referencing its parent.
4561 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4562 QObject *object = component.create();
4563 QVERIFY(object != 0);
4564 QMetaObject::invokeMethod(object, "assignCircular");
4565 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4566 QCoreApplication::processEvents();
4567 QObject *rootObject = object->property("vp").value<QObject*>();
4568 QVERIFY(rootObject != 0);
4569 QObject *childObject = rootObject->findChild<QObject*>("text");
4570 QVERIFY(childObject != 0);
4571 QWeakPointer<QObject> rootObjectTracker(rootObject);
4572 QVERIFY(!rootObjectTracker.isNull());
4573 QWeakPointer<QObject> childObjectTracker(childObject);
4574 QVERIFY(!childObjectTracker.isNull());
4576 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4577 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4578 QMetaObject::invokeMethod(object, "deassignCircular");
4579 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4580 QCoreApplication::processEvents();
4581 QVERIFY(rootObjectTracker.isNull()); // should have been collected
4582 QVERIFY(childObjectTracker.isNull()); // should have been collected
4586 void tst_qqmlecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
4588 *(int*)(parameter) += 1;
4589 qPersistentDispose(object);
4592 void tst_qqmlecmascript::propertyVarInheritance()
4594 int propertyVarWeakRefCallbackCount = 0;
4596 // enforce behaviour regarding element inheritance - ensure handle disposal.
4597 // The particular component under test here has a chain of references.
4598 QQmlComponent component(&engine, testFileUrl("propertyVar.inherit.qml"));
4599 QObject *object = component.create();
4600 QVERIFY(object != 0);
4601 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4602 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4603 QCoreApplication::processEvents();
4604 // we want to be able to track when the varProperties array of the last metaobject is disposed
4605 QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
4606 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*>();
4607 QQmlVMEMetaObject *icovmemo = QQmlVMEMetaObject::get(ico5);
4608 QQmlVMEMetaObject *ccovmemo = QQmlVMEMetaObject::get(cco5);
4609 v8::Persistent<v8::Value> icoCanaryHandle;
4610 v8::Persistent<v8::Value> ccoCanaryHandle;
4613 // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only
4614 // public function which can return us a handle to something in the varProperties array.
4615 icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ")));
4616 ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")));
4617 // we make them weak and invoke the gc, but we should not hit the weak-callback yet
4618 // as the varproperties array of each vmemo still references the resource.
4619 icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4620 ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4622 QVERIFY(propertyVarWeakRefCallbackCount == 0);
4624 // now we deassign the var prop, which should trigger collection of item subtrees.
4625 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4626 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4627 QCoreApplication::processEvents();
4628 // ensure that there are only weak handles to the underlying varProperties array remaining.
4630 QCOMPARE(propertyVarWeakRefCallbackCount, 2); // should have been called for both, since all refs should be weak.
4632 // since there are no parent vmemo's to keep implicit references alive, and the only handles
4633 // to what remains are weak, all varProperties arrays must have been collected.
4636 void tst_qqmlecmascript::propertyVarInheritance2()
4638 int propertyVarWeakRefCallbackCount = 0;
4640 // The particular component under test here does NOT have a chain of references; the
4641 // only link between rootObject and childObject is that rootObject is the parent of childObject.
4642 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4643 QObject *object = component.create();
4644 QVERIFY(object != 0);
4645 QMetaObject::invokeMethod(object, "assignCircular");
4646 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4647 QCoreApplication::processEvents();
4648 QObject *rootObject = object->property("vp").value<QObject*>();
4649 QVERIFY(rootObject != 0);
4650 QObject *childObject = rootObject->findChild<QObject*>("text");
4651 QVERIFY(childObject != 0);
4652 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4653 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4654 v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
4657 propertyVarWeakRefCallbackCount = 0; // reset callback count.
4658 childObjectVarArrayValueHandle = qPersistentNew(QQmlVMEMetaObject::get(childObject)->vmeProperty(childObject->metaObject()->indexOfProperty("vp")));
4659 childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4661 QVERIFY(propertyVarWeakRefCallbackCount == 0); // should not have been collected yet.
4662 QCOMPARE(childObject->property("vp").value<QObject*>(), rootObject);
4663 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4665 QMetaObject::invokeMethod(object, "deassignCircular");
4666 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4667 QCoreApplication::processEvents();
4668 QVERIFY(propertyVarWeakRefCallbackCount == 1); // should have been collected now.
4672 // Ensure that QObject type conversion works on binding assignment
4673 void tst_qqmlecmascript::elementAssign()
4675 QQmlComponent component(&engine, testFileUrl("elementAssign.qml"));
4677 QObject *object = component.create();
4678 QVERIFY(object != 0);
4680 QCOMPARE(object->property("test").toBool(), true);
4686 void tst_qqmlecmascript::objectPassThroughSignals()
4688 QQmlComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml"));
4690 QObject *object = component.create();
4691 QVERIFY(object != 0);
4693 QCOMPARE(object->property("test").toBool(), true);
4699 void tst_qqmlecmascript::objectConversion()
4701 QQmlComponent component(&engine, testFileUrl("objectConversion.qml"));
4703 QObject *object = component.create();
4704 QVERIFY(object != 0);
4706 QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
4707 QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
4714 void tst_qqmlecmascript::booleanConversion()
4716 QQmlComponent component(&engine, testFileUrl("booleanConversion.qml"));
4718 QObject *object = component.create();
4719 QVERIFY(object != 0);
4721 QCOMPARE(object->property("test_true1").toBool(), true);
4722 QCOMPARE(object->property("test_true2").toBool(), true);
4723 QCOMPARE(object->property("test_true3").toBool(), true);
4724 QCOMPARE(object->property("test_true4").toBool(), true);
4725 QCOMPARE(object->property("test_true5").toBool(), true);
4727 QCOMPARE(object->property("test_false1").toBool(), false);
4728 QCOMPARE(object->property("test_false2").toBool(), false);
4729 QCOMPARE(object->property("test_false3").toBool(), false);
4734 void tst_qqmlecmascript::handleReferenceManagement()
4739 // Linear QObject reference
4740 QQmlEngine hrmEngine;
4741 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml"));
4742 QObject *object = component.create();
4743 QVERIFY(object != 0);
4744 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4745 cro->setEngine(&hrmEngine);
4746 cro->setDtorCount(&dtorCount);
4747 QMetaObject::invokeMethod(object, "createReference");
4749 QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
4751 hrmEngine.collectGarbage();
4752 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4753 QCoreApplication::processEvents();
4754 QCOMPARE(dtorCount, 3);
4759 // Circular QObject reference
4760 QQmlEngine hrmEngine;
4761 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml"));
4762 QObject *object = component.create();
4763 QVERIFY(object != 0);
4764 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4765 cro->setEngine(&hrmEngine);
4766 cro->setDtorCount(&dtorCount);
4767 QMetaObject::invokeMethod(object, "circularReference");
4769 QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
4771 hrmEngine.collectGarbage();
4772 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4773 QCoreApplication::processEvents();
4774 QCOMPARE(dtorCount, 3);
4779 // Linear handle reference
4780 QQmlEngine hrmEngine;
4781 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.1.qml"));
4782 QObject *object = component.create();
4783 QVERIFY(object != 0);
4784 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4786 crh->setEngine(&hrmEngine);
4787 crh->setDtorCount(&dtorCount);
4788 QMetaObject::invokeMethod(object, "createReference");
4789 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4790 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4791 QVERIFY(first != 0);
4792 QVERIFY(second != 0);
4793 first->addReference(QQmlData::get(second)->v8object); // create reference
4794 // now we have to reparent second and make second owned by JS.
4795 second->setParent(0);
4796 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
4798 QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
4800 hrmEngine.collectGarbage();
4801 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4802 QCoreApplication::processEvents();
4803 QCOMPARE(dtorCount, 3);
4808 // Circular handle reference
4809 QQmlEngine hrmEngine;
4810 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.2.qml"));
4811 QObject *object = component.create();
4812 QVERIFY(object != 0);
4813 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4815 crh->setEngine(&hrmEngine);
4816 crh->setDtorCount(&dtorCount);
4817 QMetaObject::invokeMethod(object, "circularReference");
4818 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4819 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4820 QVERIFY(first != 0);
4821 QVERIFY(second != 0);
4822 first->addReference(QQmlData::get(second)->v8object); // create circular reference
4823 second->addReference(QQmlData::get(first)->v8object); // note: must be weak.
4824 // now we have to reparent and change ownership, and unset the property references.
4825 first->setParent(0);
4826 second->setParent(0);
4827 QQmlEngine::setObjectOwnership(first, QQmlEngine::JavaScriptOwnership);
4828 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
4829 object->setProperty("first", QVariant::fromValue<QObject*>(0));
4830 object->setProperty("second", QVariant::fromValue<QObject*>(0));
4832 QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
4834 hrmEngine.collectGarbage();
4835 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4836 QCoreApplication::processEvents();
4837 QCOMPARE(dtorCount, 3);
4842 // multiple engine interaction - linear reference
4843 QQmlEngine hrmEngine1;
4844 QQmlEngine hrmEngine2;
4845 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4846 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4847 QObject *object1 = component1.create();
4848 QObject *object2 = component2.create();
4849 QVERIFY(object1 != 0);
4850 QVERIFY(object2 != 0);
4851 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4852 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4855 crh1->setEngine(&hrmEngine1);
4856 crh2->setEngine(&hrmEngine2);
4857 crh1->setDtorCount(&dtorCount);
4858 crh2->setDtorCount(&dtorCount);
4859 QMetaObject::invokeMethod(object1, "createReference");
4860 QMetaObject::invokeMethod(object2, "createReference");
4861 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4862 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4863 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4864 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4865 QVERIFY(first1 != 0);
4866 QVERIFY(second1 != 0);
4867 QVERIFY(first2 != 0);
4868 QVERIFY(second2 != 0);
4869 first1->addReference(QQmlData::get(second2)->v8object); // create reference across engines
4870 // now we have to reparent second2 and make second2 owned by JS.
4871 second2->setParent(0);
4872 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4874 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4875 QCoreApplication::processEvents();
4876 QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
4879 hrmEngine1.collectGarbage();
4880 hrmEngine2.collectGarbage();
4881 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4882 QCoreApplication::processEvents();
4883 QCOMPARE(dtorCount, 6);
4888 // multiple engine interaction - circular reference
4889 QQmlEngine hrmEngine1;
4890 QQmlEngine hrmEngine2;
4891 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4892 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4893 QObject *object1 = component1.create();
4894 QObject *object2 = component2.create();
4895 QVERIFY(object1 != 0);
4896 QVERIFY(object2 != 0);
4897 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4898 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4901 crh1->setEngine(&hrmEngine1);
4902 crh2->setEngine(&hrmEngine2);
4903 crh1->setDtorCount(&dtorCount);
4904 crh2->setDtorCount(&dtorCount);
4905 QMetaObject::invokeMethod(object1, "createReference");
4906 QMetaObject::invokeMethod(object2, "createReference");
4907 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4908 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4909 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4910 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4911 QVERIFY(first1 != 0);
4912 QVERIFY(second1 != 0);
4913 QVERIFY(first2 != 0);
4914 QVERIFY(second2 != 0);
4915 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
4916 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
4917 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
4918 first2->addReference(QQmlData::get(first1)->v8object); // close the loop - circular ref across engines
4919 // now we have to reparent and change ownership to JS, and remove property references.
4920 first1->setParent(0);
4921 second1->setParent(0);
4922 first2->setParent(0);
4923 second2->setParent(0);
4924 QQmlEngine::setObjectOwnership(first1, QQmlEngine::JavaScriptOwnership);
4925 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
4926 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
4927 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4928 object1->setProperty("first", QVariant::fromValue<QObject*>(0));
4929 object1->setProperty("second", QVariant::fromValue<QObject*>(0));
4930 object2->setProperty("first", QVariant::fromValue<QObject*>(0));
4931 object2->setProperty("second", QVariant::fromValue<QObject*>(0));
4933 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4934 QCoreApplication::processEvents();
4935 QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
4938 hrmEngine1.collectGarbage();
4939 hrmEngine2.collectGarbage();
4940 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4941 QCoreApplication::processEvents();
4942 QCOMPARE(dtorCount, 6);
4947 // multiple engine interaction - linear reference with engine deletion
4948 QQmlEngine *hrmEngine1 = new QQmlEngine;
4949 QQmlEngine *hrmEngine2 = new QQmlEngine;
4950 QQmlComponent component1(hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4951 QQmlComponent component2(hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4952 QObject *object1 = component1.create();
4953 QObject *object2 = component2.create();
4954 QVERIFY(object1 != 0);
4955 QVERIFY(object2 != 0);
4956 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4957 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4960 crh1->setEngine(hrmEngine1);
4961 crh2->setEngine(hrmEngine2);
4962 crh1->setDtorCount(&dtorCount);
4963 crh2->setDtorCount(&dtorCount);
4964 QMetaObject::invokeMethod(object1, "createReference");
4965 QMetaObject::invokeMethod(object2, "createReference");
4966 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4967 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4968 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4969 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4970 QVERIFY(first1 != 0);
4971 QVERIFY(second1 != 0);
4972 QVERIFY(first2 != 0);
4973 QVERIFY(second2 != 0);
4974 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
4975 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
4976 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
4977 // now we have to reparent and change ownership to JS.
4978 first1->setParent(crh1);
4979 second1->setParent(0);
4980 first2->setParent(0);
4981 second2->setParent(0);
4982 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
4983 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
4984 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4987 QCOMPARE(dtorCount, 0);
4988 delete hrmEngine2; // should trigger deletion of objects with JS ownership tracked by this engine
4990 QCOMPARE(dtorCount, 2); // first2 and second2 should have been deleted.
4994 QCOMPARE(dtorCount, 6); // deleting object1 and object2 should trigger deletion of first1 and first2.
4996 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4997 QCoreApplication::processEvents();
4998 QCOMPARE(dtorCount, 6); // all objects should have been cleaned up prior to deleting hrmEngine1.
5002 // Dynamic variant property reference keeps target alive
5003 QQmlEngine hrmEngine;
5004 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.qml"));
5005 QObject *object = component.create();
5006 QVERIFY(object != 0);
5007 QMetaObject::invokeMethod(object, "createReference");
5009 QMetaObject::invokeMethod(object, "ensureReference");
5011 QMetaObject::invokeMethod(object, "removeReference");
5013 QMetaObject::invokeMethod(object, "ensureDeletion");
5014 QCOMPARE(object->property("success").toBool(), true);
5019 // Dynamic Item property reference keeps target alive
5020 QQmlEngine hrmEngine;
5021 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.2.qml"));
5022 QObject *object = component.create();
5023 QVERIFY(object != 0);
5024 QMetaObject::invokeMethod(object, "createReference");
5026 QMetaObject::invokeMethod(object, "ensureReference");
5028 QMetaObject::invokeMethod(object, "removeReference");
5030 QMetaObject::invokeMethod(object, "ensureDeletion");
5031 QCOMPARE(object->property("success").toBool(), true);
5036 // Item property reference to deleted item doesn't crash
5037 QQmlEngine hrmEngine;
5038 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.3.qml"));
5039 QObject *object = component.create();
5040 QVERIFY(object != 0);
5041 QMetaObject::invokeMethod(object, "createReference");
5043 QMetaObject::invokeMethod(object, "ensureReference");
5045 QMetaObject::invokeMethod(object, "manuallyDelete");
5047 QMetaObject::invokeMethod(object, "ensureDeleted");
5048 QCOMPARE(object->property("success").toBool(), true);
5053 void tst_qqmlecmascript::stringArg()
5055 QQmlComponent component(&engine, testFileUrl("stringArg.qml"));
5056 QObject *object = component.create();
5057 QVERIFY(object != 0);
5058 QMetaObject::invokeMethod(object, "success");
5059 QVERIFY(object->property("returnValue").toBool());
5061 QString w1 = testFileUrl("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
5062 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5063 QMetaObject::invokeMethod(object, "failure");
5064 QVERIFY(object->property("returnValue").toBool());
5069 void tst_qqmlecmascript::readonlyDeclaration()
5071 QQmlComponent component(&engine, testFileUrl("readonlyDeclaration.qml"));
5073 QObject *object = component.create();
5074 QVERIFY(object != 0);
5076 QCOMPARE(object->property("test").toBool(), true);
5081 Q_DECLARE_METATYPE(QList<int>)
5082 Q_DECLARE_METATYPE(QList<qreal>)
5083 Q_DECLARE_METATYPE(QList<bool>)
5084 Q_DECLARE_METATYPE(QList<QString>)
5085 Q_DECLARE_METATYPE(QList<QUrl>)
5086 void tst_qqmlecmascript::sequenceConversionRead()
5089 QUrl qmlFile = testFileUrl("sequenceConversion.read.qml");
5090 QQmlComponent component(&engine, qmlFile);
5091 QObject *object = component.create();
5092 QVERIFY(object != 0);
5093 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5096 QMetaObject::invokeMethod(object, "readSequences");
5097 QList<int> intList; intList << 1 << 2 << 3 << 4;
5098 QCOMPARE(object->property("intListLength").toInt(), intList.length());
5099 QCOMPARE(object->property("intList").value<QList<int> >(), intList);
5100 QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
5101 QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
5102 QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
5103 QList<bool> boolList; boolList << true << false << true << false;
5104 QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
5105 QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
5106 QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5107 QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
5108 QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
5109 QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
5110 QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
5111 QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
5112 QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5113 QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
5114 QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
5116 QMetaObject::invokeMethod(object, "readSequenceElements");
5117 QCOMPARE(object->property("intVal").toInt(), 2);
5118 QCOMPARE(object->property("qrealVal").toReal(), 2.2);
5119 QCOMPARE(object->property("boolVal").toBool(), false);
5120 QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
5121 QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
5122 QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
5124 QMetaObject::invokeMethod(object, "enumerateSequenceElements");
5125 QCOMPARE(object->property("enumerationMatches").toBool(), true);
5127 intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
5128 QQmlProperty seqProp(seq, "intListProperty");
5129 QCOMPARE(seqProp.read().value<QList<int> >(), intList);
5130 QQmlProperty seqProp2(seq, "intListProperty", &engine);
5131 QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
5133 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5134 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5140 QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml");
5141 QQmlComponent component(&engine, qmlFile);
5142 QObject *object = component.create();
5143 QVERIFY(object != 0);
5144 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5147 // we haven't registered QList<QPoint> as a sequence type.
5148 QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5149 QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
5150 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5151 QTest::ignoreMessage(QtWarningMsg, warningTwo.toLatin1().constData());
5153 QMetaObject::invokeMethod(object, "performTest");
5155 // QList<QPoint> has not been registered as a sequence type.
5156 QCOMPARE(object->property("pointListLength").toInt(), 0);
5157 QVERIFY(!object->property("pointList").isValid());
5158 QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5159 QQmlProperty seqProp(seq, "pointListProperty", &engine);
5160 QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
5166 void tst_qqmlecmascript::sequenceConversionWrite()
5169 QUrl qmlFile = testFileUrl("sequenceConversion.write.qml");
5170 QQmlComponent component(&engine, qmlFile);
5171 QObject *object = component.create();
5172 QVERIFY(object != 0);
5173 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5176 QMetaObject::invokeMethod(object, "writeSequences");
5177 QCOMPARE(object->property("success").toBool(), true);
5179 QMetaObject::invokeMethod(object, "writeSequenceElements");
5180 QCOMPARE(object->property("success").toBool(), true);
5182 QMetaObject::invokeMethod(object, "writeOtherElements");
5183 QCOMPARE(object->property("success").toBool(), true);
5185 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5186 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5192 QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml");
5193 QQmlComponent component(&engine, qmlFile);
5194 QObject *object = component.create();
5195 QVERIFY(object != 0);
5196 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5199 // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
5200 QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to an unregistered type");
5201 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5203 QMetaObject::invokeMethod(object, "performTest");
5205 QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
5206 QCOMPARE(seq->pointListProperty(), pointList);
5212 void tst_qqmlecmascript::sequenceConversionArray()
5214 // ensure that in JS the returned sequences act just like normal JS Arrays.
5215 QUrl qmlFile = testFileUrl("sequenceConversion.array.qml");
5216 QQmlComponent component(&engine, qmlFile);
5217 QObject *object = component.create();
5218 QVERIFY(object != 0);
5219 QMetaObject::invokeMethod(object, "indexedAccess");
5220 QVERIFY(object->property("success").toBool());
5221 QMetaObject::invokeMethod(object, "arrayOperations");
5222 QVERIFY(object->property("success").toBool());
5223 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5224 QVERIFY(object->property("success").toBool());
5225 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5226 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5231 void tst_qqmlecmascript::sequenceConversionIndexes()
5233 // ensure that we gracefully fail if unsupported index values are specified.
5234 // Qt container classes only support non-negative, signed integer index values.
5235 QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
5236 QQmlComponent component(&engine, qmlFile);
5237 QObject *object = component.create();
5238 QVERIFY(object != 0);
5239 QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
5240 QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
5241 QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
5242 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
5243 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
5244 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
5245 QMetaObject::invokeMethod(object, "indexedAccess");
5246 QVERIFY(object->property("success").toBool());
5250 void tst_qqmlecmascript::sequenceConversionThreads()
5252 // ensure that sequence conversion operations work correctly in a worker thread
5253 // and that serialisation between the main and worker thread succeeds.
5254 QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml");
5255 QQmlComponent component(&engine, qmlFile);
5256 QObject *object = component.create();
5257 QVERIFY(object != 0);
5259 QMetaObject::invokeMethod(object, "testIntSequence");
5260 QTRY_VERIFY(object->property("finished").toBool());
5261 QVERIFY(object->property("success").toBool());
5263 QMetaObject::invokeMethod(object, "testQrealSequence");
5264 QTRY_VERIFY(object->property("finished").toBool());
5265 QVERIFY(object->property("success").toBool());
5267 QMetaObject::invokeMethod(object, "testBoolSequence");
5268 QTRY_VERIFY(object->property("finished").toBool());
5269 QVERIFY(object->property("success").toBool());
5271 QMetaObject::invokeMethod(object, "testStringSequence");
5272 QTRY_VERIFY(object->property("finished").toBool());
5273 QVERIFY(object->property("success").toBool());
5275 QMetaObject::invokeMethod(object, "testQStringSequence");
5276 QTRY_VERIFY(object->property("finished").toBool());
5277 QVERIFY(object->property("success").toBool());
5279 QMetaObject::invokeMethod(object, "testUrlSequence");
5280 QTRY_VERIFY(object->property("finished").toBool());
5281 QVERIFY(object->property("success").toBool());
5283 QMetaObject::invokeMethod(object, "testVariantSequence");
5284 QTRY_VERIFY(object->property("finished").toBool());
5285 QVERIFY(object->property("success").toBool());
5290 void tst_qqmlecmascript::sequenceConversionBindings()
5293 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml");
5294 QQmlComponent component(&engine, qmlFile);
5295 QObject *object = component.create();
5296 QVERIFY(object != 0);
5297 QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
5298 QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
5299 QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
5300 QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
5301 QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
5306 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml");
5307 QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
5308 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5309 QQmlComponent component(&engine, qmlFile);
5310 QObject *object = component.create();
5311 QVERIFY(object != 0);
5316 void tst_qqmlecmascript::sequenceConversionCopy()
5318 QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml");
5319 QQmlComponent component(&engine, qmlFile);
5320 QObject *object = component.create();
5321 QVERIFY(object != 0);
5322 QMetaObject::invokeMethod(object, "testCopySequences");
5323 QCOMPARE(object->property("success").toBool(), true);
5324 QMetaObject::invokeMethod(object, "readSequenceCopyElements");
5325 QCOMPARE(object->property("success").toBool(), true);
5326 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5327 QCOMPARE(object->property("success").toBool(), true);
5331 void tst_qqmlecmascript::assignSequenceTypes()
5333 // test binding array to sequence type property
5335 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml"));
5336 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5337 QVERIFY(object != 0);
5338 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5339 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5340 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5341 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5342 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5343 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5347 // test binding literal to sequence type property
5349 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml"));
5350 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5351 QVERIFY(object != 0);
5352 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5353 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5354 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5355 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5356 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5357 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5361 // test binding single value to sequence type property
5363 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml"));
5364 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5365 QVERIFY(object != 0);
5366 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5367 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5368 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5369 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5373 // test assigning array to sequence type property in js function
5375 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml"));
5376 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5377 QVERIFY(object != 0);
5378 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5379 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5380 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5381 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5382 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5383 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5387 // test assigning literal to sequence type property in js function
5389 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml"));
5390 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5391 QVERIFY(object != 0);
5392 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5393 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5394 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5395 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5396 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5397 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5401 // test assigning single value to sequence type property in js function
5403 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml"));
5404 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5405 QVERIFY(object != 0);
5406 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5407 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5408 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5409 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5413 // test QList<QUrl> literal assignment and binding assignment causes url resolution when required
5415 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml"));
5416 QObject *object = component.create();
5417 QVERIFY(object != 0);
5418 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
5419 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
5420 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
5421 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
5422 MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco5"));
5423 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0);
5424 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5425 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5426 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5427 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5428 QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5433 // Test that assigning a null object works
5434 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
5435 void tst_qqmlecmascript::nullObjectBinding()
5437 QQmlComponent component(&engine, testFileUrl("nullObjectBinding.qml"));
5439 QObject *object = component.create();
5440 QVERIFY(object != 0);
5442 QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
5447 // Test that bindings don't evaluate once the engine has been destroyed
5448 void tst_qqmlecmascript::deletedEngine()
5450 QQmlEngine *engine = new QQmlEngine;
5451 QQmlComponent component(engine, testFileUrl("deletedEngine.qml"));
5453 QObject *object = component.create();
5454 QVERIFY(object != 0);
5456 QCOMPARE(object->property("a").toInt(), 39);
5457 object->setProperty("b", QVariant(9));
5458 QCOMPARE(object->property("a").toInt(), 117);
5462 QCOMPARE(object->property("a").toInt(), 117);
5463 object->setProperty("b", QVariant(10));
5464 QCOMPARE(object->property("a").toInt(), 117);
5469 // Test the crashing part of QTBUG-9705
5470 void tst_qqmlecmascript::libraryScriptAssert()
5472 QQmlComponent component(&engine, testFileUrl("libraryScriptAssert.qml"));
5474 QObject *object = component.create();
5475 QVERIFY(object != 0);
5480 void tst_qqmlecmascript::variantsAssignedUndefined()
5482 QQmlComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml"));
5484 QObject *object = component.create();
5485 QVERIFY(object != 0);
5487 QCOMPARE(object->property("test1").toInt(), 10);
5488 QCOMPARE(object->property("test2").toInt(), 11);
5490 object->setProperty("runTest", true);
5492 QCOMPARE(object->property("test1"), QVariant());
5493 QCOMPARE(object->property("test2"), QVariant());
5499 void tst_qqmlecmascript::qtbug_9792()
5501 QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml"));
5503 QQmlContext *context = new QQmlContext(engine.rootContext());
5505 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
5506 QVERIFY(object != 0);
5508 QTest::ignoreMessage(QtDebugMsg, "Hello world!");
5509 object->basicSignal();
5513 transientErrorsMsgCount = 0;
5514 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5516 object->basicSignal();
5518 qInstallMsgHandler(old);
5520 QCOMPARE(transientErrorsMsgCount, 0);
5525 // Verifies that QQmlGuard<>s used in the vmemetaobject are cleaned correctly
5526 void tst_qqmlecmascript::qtcreatorbug_1289()
5528 QQmlComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml"));
5530 QObject *o = component.create();
5533 QObject *nested = qvariant_cast<QObject *>(o->property("object"));
5534 QVERIFY(nested != 0);
5536 QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
5539 nested = qvariant_cast<QObject *>(o->property("object"));
5540 QVERIFY(nested == 0);
5542 // If the bug is present, the next line will crash
5546 // Test that we shut down without stupid warnings
5547 void tst_qqmlecmascript::noSpuriousWarningsAtShutdown()
5550 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml"));
5552 QObject *o = component.create();
5554 transientErrorsMsgCount = 0;
5555 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5559 qInstallMsgHandler(old);
5561 QCOMPARE(transientErrorsMsgCount, 0);
5566 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml"));
5568 QObject *o = component.create();
5570 transientErrorsMsgCount = 0;
5571 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5575 qInstallMsgHandler(old);
5577 QCOMPARE(transientErrorsMsgCount, 0);
5581 void tst_qqmlecmascript::canAssignNullToQObject()
5584 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml"));
5586 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5589 QVERIFY(o->objectProperty() != 0);
5591 o->setProperty("runTest", true);
5593 QVERIFY(o->objectProperty() == 0);
5599 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml"));
5601 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5604 QVERIFY(o->objectProperty() == 0);
5610 void tst_qqmlecmascript::functionAssignment_fromBinding()
5612 QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml"));
5614 QString url = component.url().toString();
5615 QString w1 = url + ":4:25: Unable to assign a function to a property of any type other than var.";
5616 QString w2 = url + ":5:25: Invalid use of Qt.binding() in a binding declaration.";
5617 QString w3 = url + ":6:21: Invalid use of Qt.binding() in a binding declaration.";
5618 QString w4 = url + ":7:15: Invalid use of Qt.binding() in a binding declaration.";
5619 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5620 QTest::ignoreMessage(QtWarningMsg, w2.toLatin1().constData());
5621 QTest::ignoreMessage(QtWarningMsg, w3.toLatin1().constData());
5622 QTest::ignoreMessage(QtWarningMsg, w4.toLatin1().constData());
5624 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5627 QVERIFY(!o->property("a").isValid());
5632 void tst_qqmlecmascript::functionAssignment_fromJS()
5634 QFETCH(QString, triggerProperty);
5636 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5637 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5639 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5641 QVERIFY(!o->property("a").isValid());
5643 o->setProperty("aNumber", QVariant(5));
5644 o->setProperty(triggerProperty.toUtf8().constData(), true);
5645 QCOMPARE(o->property("a"), QVariant(50));
5647 o->setProperty("aNumber", QVariant(10));
5648 QCOMPARE(o->property("a"), QVariant(100));
5653 void tst_qqmlecmascript::functionAssignment_fromJS_data()
5655 QTest::addColumn<QString>("triggerProperty");
5657 QTest::newRow("assign to property") << "assignToProperty";
5658 QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
5660 QTest::newRow("assign to value type") << "assignToValueType";
5662 QTest::newRow("use 'this'") << "assignWithThis";
5663 QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
5666 void tst_qqmlecmascript::functionAssignmentfromJS_invalid()
5668 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5669 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5671 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5673 QVERIFY(!o->property("a").isValid());
5675 o->setProperty("assignFuncWithoutReturn", true);
5676 QVERIFY(!o->property("a").isValid());
5678 QString url = component.url().toString();
5679 QString warning = url + ":67:17: Unable to assign QString to int";
5680 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5681 o->setProperty("assignWrongType", true);
5683 warning = url + ":71:29: Unable to assign QString to int";
5684 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5685 o->setProperty("assignWrongTypeToValueType", true);
5690 void tst_qqmlecmascript::functionAssignment_afterBinding()
5692 QQmlComponent component(&engine, testFileUrl("functionAssignment.3.qml"));
5694 QString url = component.url().toString();
5695 QString w1 = url + ":16: Error: Cannot assign JavaScript function to int";
5696 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5698 QObject *o = component.create();
5700 QCOMPARE(o->property("t1"), QVariant::fromValue<int>(4)); // should have bound
5701 QCOMPARE(o->property("t2"), QVariant::fromValue<int>(2)); // should not have changed
5706 void tst_qqmlecmascript::eval()
5708 QQmlComponent component(&engine, testFileUrl("eval.qml"));
5710 QObject *o = component.create();
5713 QCOMPARE(o->property("test1").toBool(), true);
5714 QCOMPARE(o->property("test2").toBool(), true);
5715 QCOMPARE(o->property("test3").toBool(), true);
5716 QCOMPARE(o->property("test4").toBool(), true);
5717 QCOMPARE(o->property("test5").toBool(), true);
5722 void tst_qqmlecmascript::function()
5724 QQmlComponent component(&engine, testFileUrl("function.qml"));
5726 QObject *o = component.create();
5729 QCOMPARE(o->property("test1").toBool(), true);
5730 QCOMPARE(o->property("test2").toBool(), true);
5731 QCOMPARE(o->property("test3").toBool(), true);
5736 void tst_qqmlecmascript::functionException()
5738 // QTBUG-24037 - shouldn't crash.
5739 QString errstr = testFileUrl("v8functionException.qml").toString() + QLatin1String(":13: SyntaxError: Unexpected token ILLEGAL");
5740 QTest::ignoreMessage(QtWarningMsg, qPrintable(errstr));
5741 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Exception occurred during compilation of function: dynamicSlot()");
5742 QQmlComponent component(&engine, testFileUrl("v8functionException.qml"));
5743 QObject *o = component.create();
5745 QMetaObject::invokeMethod(o, "dynamicSlot");
5749 // Test the "Qt.include" method
5750 void tst_qqmlecmascript::include()
5752 // Non-library relative include
5754 QQmlComponent component(&engine, testFileUrl("include.qml"));
5755 QObject *o = component.create();
5758 QCOMPARE(o->property("test0").toInt(), 99);
5759 QCOMPARE(o->property("test1").toBool(), true);
5760 QCOMPARE(o->property("test2").toBool(), true);
5761 QCOMPARE(o->property("test2_1").toBool(), true);
5762 QCOMPARE(o->property("test3").toBool(), true);
5763 QCOMPARE(o->property("test3_1").toBool(), true);
5768 // Library relative include
5770 QQmlComponent component(&engine, testFileUrl("include_shared.qml"));
5771 QObject *o = component.create();
5774 QCOMPARE(o->property("test0").toInt(), 99);
5775 QCOMPARE(o->property("test1").toBool(), true);
5776 QCOMPARE(o->property("test2").toBool(), true);
5777 QCOMPARE(o->property("test2_1").toBool(), true);
5778 QCOMPARE(o->property("test3").toBool(), true);
5779 QCOMPARE(o->property("test3_1").toBool(), true);
5786 QQmlComponent component(&engine, testFileUrl("include_callback.qml"));
5787 QObject *o = component.create();
5790 QCOMPARE(o->property("test1").toBool(), true);
5791 QCOMPARE(o->property("test2").toBool(), true);
5792 QCOMPARE(o->property("test3").toBool(), true);
5793 QCOMPARE(o->property("test4").toBool(), true);
5794 QCOMPARE(o->property("test5").toBool(), true);
5795 QCOMPARE(o->property("test6").toBool(), true);
5800 // Including file with ".pragma library"
5802 QQmlComponent component(&engine, testFileUrl("include_pragma.qml"));
5803 QObject *o = component.create();
5805 QCOMPARE(o->property("test1").toInt(), 100);
5812 TestHTTPServer server(8111);
5813 QVERIFY(server.isValid());
5814 server.serveDirectory(dataDirectory());
5816 QQmlComponent component(&engine, testFileUrl("include_remote.qml"));
5817 QObject *o = component.create();
5820 QTRY_VERIFY(o->property("done").toBool() == true);
5821 QTRY_VERIFY(o->property("done2").toBool() == true);
5823 QCOMPARE(o->property("test1").toBool(), true);
5824 QCOMPARE(o->property("test2").toBool(), true);
5825 QCOMPARE(o->property("test3").toBool(), true);
5826 QCOMPARE(o->property("test4").toBool(), true);
5827 QCOMPARE(o->property("test5").toBool(), true);
5829 QCOMPARE(o->property("test6").toBool(), true);
5830 QCOMPARE(o->property("test7").toBool(), true);
5831 QCOMPARE(o->property("test8").toBool(), true);
5832 QCOMPARE(o->property("test9").toBool(), true);
5833 QCOMPARE(o->property("test10").toBool(), true);
5840 TestHTTPServer server(8111);
5841 QVERIFY(server.isValid());
5842 server.serveDirectory(dataDirectory());
5844 QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml"));
5845 QObject *o = component.create();
5848 QTRY_VERIFY(o->property("done").toBool() == true);
5850 QCOMPARE(o->property("test1").toBool(), true);
5851 QCOMPARE(o->property("test2").toBool(), true);
5852 QCOMPARE(o->property("test3").toBool(), true);
5858 void tst_qqmlecmascript::signalHandlers()
5860 QQmlComponent component(&engine, testFileUrl("signalHandlers.qml"));
5861 QObject *o = component.create();
5864 QVERIFY(o->property("count").toInt() == 0);
5865 QMetaObject::invokeMethod(o, "testSignalCall");
5866 QCOMPARE(o->property("count").toInt(), 1);
5868 QMetaObject::invokeMethod(o, "testSignalHandlerCall");
5869 QCOMPARE(o->property("count").toInt(), 1);
5870 QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
5872 QVERIFY(o->property("funcCount").toInt() == 0);
5873 QMetaObject::invokeMethod(o, "testSignalConnection");
5874 QCOMPARE(o->property("funcCount").toInt(), 1);
5876 QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
5877 QCOMPARE(o->property("funcCount").toInt(), 2);
5879 QMetaObject::invokeMethod(o, "testSignalDefined");
5880 QCOMPARE(o->property("definedResult").toBool(), true);
5882 QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
5883 QCOMPARE(o->property("definedHandlerResult").toBool(), true);
5888 void tst_qqmlecmascript::qtbug_10696()
5890 QQmlComponent component(&engine, testFileUrl("qtbug_10696.qml"));
5891 QObject *o = component.create();
5896 void tst_qqmlecmascript::qtbug_11606()
5898 QQmlComponent component(&engine, testFileUrl("qtbug_11606.qml"));
5899 QObject *o = component.create();
5901 QCOMPARE(o->property("test").toBool(), true);
5905 void tst_qqmlecmascript::qtbug_11600()
5907 QQmlComponent component(&engine, testFileUrl("qtbug_11600.qml"));
5908 QObject *o = component.create();
5910 QCOMPARE(o->property("test").toBool(), true);
5914 void tst_qqmlecmascript::qtbug_21864()
5916 QQmlComponent component(&engine, testFileUrl("qtbug_21864.qml"));
5917 QObject *o = component.create();
5919 QCOMPARE(o->property("test").toBool(), true);
5923 void tst_qqmlecmascript::rewriteMultiLineStrings()
5927 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml"));
5928 QObject *o = component.create();
5930 QTRY_COMPARE(o->property("test").toBool(), true);
5935 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings_crlf.1.qml"));
5936 QObject *o = component.create();
5942 void tst_qqmlecmascript::qobjectConnectionListExceptionHandling()
5945 QQmlComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml"));
5946 QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined");
5947 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5948 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5949 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5950 QObject *o = component.create();
5952 QCOMPARE(o->property("test").toBool(), true);
5956 // Reading and writing non-scriptable properties should fail
5957 void tst_qqmlecmascript::nonscriptable()
5959 QQmlComponent component(&engine, testFileUrl("nonscriptable.qml"));
5960 QObject *o = component.create();
5962 QCOMPARE(o->property("readOk").toBool(), true);
5963 QCOMPARE(o->property("writeOk").toBool(), true);
5967 // deleteLater() should not be callable from QML
5968 void tst_qqmlecmascript::deleteLater()
5970 QQmlComponent component(&engine, testFileUrl("deleteLater.qml"));
5971 QObject *o = component.create();
5973 QCOMPARE(o->property("test").toBool(), true);
5977 // objectNameChanged() should be usable from QML
5978 void tst_qqmlecmascript::objectNameChangedSignal()
5980 QQmlComponent component(&engine, testFileUrl("objectNameChangedSignal.qml"));
5981 QObject *o = component.create();
5983 QCOMPARE(o->property("test").toBool(), false);
5984 o->setObjectName("obj");
5985 QCOMPARE(o->property("test").toBool(), true);
5989 // destroyed() should not be usable from QML
5990 void tst_qqmlecmascript::destroyedSignal()
5992 QQmlComponent component(&engine, testFileUrl("destroyedSignal.qml"));
5993 QVERIFY(component.isError());
5995 QString expectedErrorString = component.url().toString() + QLatin1String(":5:5: Cannot assign to non-existent property \"onDestroyed\"");
5996 QCOMPARE(component.errors().at(0).toString(), expectedErrorString);
5999 void tst_qqmlecmascript::in()
6001 QQmlComponent component(&engine, testFileUrl("in.qml"));
6002 QObject *o = component.create();
6004 QCOMPARE(o->property("test1").toBool(), true);
6005 QCOMPARE(o->property("test2").toBool(), true);
6009 void tst_qqmlecmascript::typeOf()
6011 QQmlComponent component(&engine, testFileUrl("typeOf.qml"));
6013 QObject *o = component.create();
6016 QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
6017 QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
6018 QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
6019 QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
6020 QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
6021 QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
6022 QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
6023 QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
6024 QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
6029 void tst_qqmlecmascript::qtbug_24448()
6031 QQmlComponent component(&engine, testFileUrl("qtbug_24448.qml"));
6032 QScopedPointer<QObject> o(component.create());
6034 QVERIFY(o->property("test").toBool());
6037 void tst_qqmlecmascript::sharedAttachedObject()
6039 QQmlComponent component(&engine, testFileUrl("sharedAttachedObject.qml"));
6040 QObject *o = component.create();
6042 QCOMPARE(o->property("test1").toBool(), true);
6043 QCOMPARE(o->property("test2").toBool(), true);
6048 void tst_qqmlecmascript::objectName()
6050 QQmlComponent component(&engine, testFileUrl("objectName.qml"));
6051 QObject *o = component.create();
6054 QCOMPARE(o->property("test1").toString(), QString("hello"));
6055 QCOMPARE(o->property("test2").toString(), QString("ell"));
6057 o->setObjectName("world");
6059 QCOMPARE(o->property("test1").toString(), QString("world"));
6060 QCOMPARE(o->property("test2").toString(), QString("orl"));
6065 void tst_qqmlecmascript::writeRemovesBinding()
6067 QQmlComponent component(&engine, testFileUrl("writeRemovesBinding.qml"));
6068 QObject *o = component.create();
6071 QCOMPARE(o->property("test").toBool(), true);
6076 // Test bindings assigned to alias properties actually assign to the alias' target
6077 void tst_qqmlecmascript::aliasBindingsAssignCorrectly()
6079 QQmlComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml"));
6080 QObject *o = component.create();
6083 QCOMPARE(o->property("test").toBool(), true);
6088 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
6089 void tst_qqmlecmascript::aliasBindingsOverrideTarget()
6092 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml"));
6093 QObject *o = component.create();
6096 QCOMPARE(o->property("test").toBool(), true);
6102 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml"));
6103 QObject *o = component.create();
6106 QCOMPARE(o->property("test").toBool(), true);
6112 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml"));
6113 QObject *o = component.create();
6116 QCOMPARE(o->property("test").toBool(), true);
6122 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
6123 void tst_qqmlecmascript::aliasWritesOverrideBindings()
6126 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml"));
6127 QObject *o = component.create();
6130 QCOMPARE(o->property("test").toBool(), true);
6136 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml"));
6137 QObject *o = component.create();
6140 QCOMPARE(o->property("test").toBool(), true);
6146 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml"));
6147 QObject *o = component.create();
6150 QCOMPARE(o->property("test").toBool(), true);
6156 // Allow an alais to a composite element
6158 void tst_qqmlecmascript::aliasToCompositeElement()
6160 QQmlComponent component(&engine, testFileUrl("aliasToCompositeElement.qml"));
6162 QObject *object = component.create();
6163 QVERIFY(object != 0);
6168 void tst_qqmlecmascript::qtbug_20344()
6170 QQmlComponent component(&engine, testFileUrl("qtbug_20344.qml"));
6172 QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
6173 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6175 QObject *object = component.create();
6176 QVERIFY(object != 0);
6181 void tst_qqmlecmascript::revisionErrors()
6184 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml"));
6185 QString url = component.url().toString();
6187 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6188 QString warning2 = url + ":11: ReferenceError: prop2 is not defined";
6189 QString warning3 = url + ":13: ReferenceError: method2 is not defined";
6191 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6192 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6193 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6194 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6195 QVERIFY(object != 0);
6199 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml"));
6200 QString url = component.url().toString();
6202 // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
6203 // method2, prop2 from MyRevisionedClass not available
6204 // method4, prop4 from MyRevisionedSubclass not available
6205 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6206 QString warning2 = url + ":14: ReferenceError: prop2 is not defined";
6207 QString warning3 = url + ":10: ReferenceError: prop4 is not defined";
6208 QString warning4 = url + ":16: ReferenceError: prop4 is not defined";
6209 QString warning5 = url + ":20: ReferenceError: method2 is not defined";
6211 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6212 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6213 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6214 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
6215 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
6216 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6217 QVERIFY(object != 0);
6221 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml"));
6222 QString url = component.url().toString();
6224 // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
6225 // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
6226 QString warning1 = url + ":30: ReferenceError: methodD is not defined";
6227 QString warning2 = url + ":10: ReferenceError: propD is not defined";
6228 QString warning3 = url + ":20: ReferenceError: propD is not defined";
6229 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6230 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6231 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6232 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6233 QVERIFY(object != 0);
6238 void tst_qqmlecmascript::revision()
6241 QQmlComponent component(&engine, testFileUrl("metaobjectRevision.qml"));
6242 QString url = component.url().toString();
6244 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6245 QVERIFY(object != 0);
6249 QQmlComponent component(&engine, testFileUrl("metaobjectRevision2.qml"));
6250 QString url = component.url().toString();
6252 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6253 QVERIFY(object != 0);
6257 QQmlComponent component(&engine, testFileUrl("metaobjectRevision3.qml"));
6258 QString url = component.url().toString();
6260 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6261 QVERIFY(object != 0);
6264 // Test that non-root classes can resolve revisioned methods
6266 QQmlComponent component(&engine, testFileUrl("metaobjectRevision4.qml"));
6268 QObject *object = component.create();
6269 QVERIFY(object != 0);
6270 QCOMPARE(object->property("test").toReal(), 11.);
6275 void tst_qqmlecmascript::realToInt()
6277 QQmlComponent component(&engine, testFileUrl("realToInt.qml"));
6278 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6279 QVERIFY(object != 0);
6281 QMetaObject::invokeMethod(object, "test1");
6282 QCOMPARE(object->value(), int(4));
6283 QMetaObject::invokeMethod(object, "test2");
6284 QCOMPARE(object->value(), int(8));
6287 void tst_qqmlecmascript::urlProperty()
6290 QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml"));
6291 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6292 QVERIFY(object != 0);
6293 object->setStringProperty("http://qt-project.org");
6294 QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
6295 QCOMPARE(object->intProperty(), 123);
6296 QCOMPARE(object->value(), 1);
6297 QCOMPARE(object->property("result").toBool(), true);
6301 void tst_qqmlecmascript::urlPropertyWithEncoding()
6304 QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml"));
6305 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6306 QVERIFY(object != 0);
6307 object->setStringProperty("http://qt-project.org");
6309 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6310 QCOMPARE(object->urlProperty(), encoded);
6311 QCOMPARE(object->value(), 0); // Interpreting URL as string yields canonicalised version
6312 QCOMPARE(object->property("result").toBool(), true);
6316 void tst_qqmlecmascript::urlListPropertyWithEncoding()
6319 QQmlComponent component(&engine, testFileUrl("urlListProperty.qml"));
6320 QObject *object = component.create();
6321 QVERIFY(object != 0);
6322 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
6323 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
6324 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
6325 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
6326 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0);
6328 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6329 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded));
6330 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded));
6331 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6332 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6337 void tst_qqmlecmascript::dynamicString()
6339 QQmlComponent component(&engine, testFileUrl("dynamicString.qml"));
6340 QObject *object = component.create();
6341 QVERIFY(object != 0);
6342 QCOMPARE(object->property("stringProperty").toString(),
6343 QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
6346 void tst_qqmlecmascript::deleteLaterObjectMethodCall()
6348 QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml"));
6349 QObject *object = component.create();
6350 QVERIFY(object != 0);
6353 void tst_qqmlecmascript::automaticSemicolon()
6355 QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml"));
6356 QObject *object = component.create();
6357 QVERIFY(object != 0);
6360 void tst_qqmlecmascript::unaryExpression()
6362 QQmlComponent component(&engine, testFileUrl("unaryExpression.qml"));
6363 QObject *object = component.create();
6364 QVERIFY(object != 0);
6367 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
6368 void tst_qqmlecmascript::doubleEvaluate()
6370 QQmlComponent component(&engine, testFileUrl("doubleEvaluate.qml"));
6371 QObject *object = component.create();
6372 QVERIFY(object != 0);
6373 WriteCounter *wc = qobject_cast<WriteCounter *>(object);
6375 QCOMPARE(wc->count(), 1);
6377 wc->setProperty("x", 9);
6379 QCOMPARE(wc->count(), 2);
6384 static QStringList messages;
6385 static void captureMsgHandler(QtMsgType, const char *msg)
6387 messages.append(QLatin1String(msg));
6390 void tst_qqmlecmascript::nonNotifyable()
6392 QV4Compiler::enableV4(false);
6393 QQmlComponent component(&engine, testFileUrl("nonNotifyable.qml"));
6394 QV4Compiler::enableV4(true);
6396 QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
6398 QObject *object = component.create();
6399 qInstallMsgHandler(old);
6401 QVERIFY(object != 0);
6403 QString expected1 = QLatin1String("QQmlExpression: Expression ") +
6404 component.url().toString() +
6405 QLatin1String(":5 depends on non-NOTIFYable properties:");
6406 QString expected2 = QLatin1String(" ") +
6407 QLatin1String(object->metaObject()->className()) +
6408 QLatin1String("::value");
6410 QCOMPARE(messages.length(), 2);
6411 QCOMPARE(messages.at(0), expected1);
6412 QCOMPARE(messages.at(1), expected2);
6417 void tst_qqmlecmascript::forInLoop()
6419 QQmlComponent component(&engine, testFileUrl("forInLoop.qml"));
6420 QObject *object = component.create();
6421 QVERIFY(object != 0);
6423 QMetaObject::invokeMethod(object, "listProperty");
6425 QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
6426 QCOMPARE(r.size(), 3);
6427 QCOMPARE(r[0],QLatin1String("0=obj1"));
6428 QCOMPARE(r[1],QLatin1String("1=obj2"));
6429 QCOMPARE(r[2],QLatin1String("2=obj3"));
6431 //TODO: should test for in loop for other objects (such as QObjects) as well.
6436 // An object the binding depends on is deleted while the binding is still running
6437 void tst_qqmlecmascript::deleteWhileBindingRunning()
6439 QQmlComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml"));
6440 QObject *object = component.create();
6441 QVERIFY(object != 0);
6445 void tst_qqmlecmascript::qtbug_22679()
6448 object.setStringProperty(QLatin1String("Please work correctly"));
6449 engine.rootContext()->setContextProperty("contextProp", &object);
6451 QQmlComponent component(&engine, testFileUrl("qtbug_22679.qml"));
6452 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6453 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6455 QObject *o = component.create();
6457 QCOMPARE(warningsSpy.count(), 0);
6461 void tst_qqmlecmascript::qtbug_22843_data()
6463 QTest::addColumn<bool>("library");
6465 QTest::newRow("without .pragma library") << false;
6466 QTest::newRow("with .pragma library") << true;
6469 void tst_qqmlecmascript::qtbug_22843()
6471 QFETCH(bool, library);
6473 QString fileName("qtbug_22843");
6475 fileName += QLatin1String(".library");
6476 fileName += QLatin1String(".qml");
6478 QQmlComponent component(&engine, testFileUrl(fileName));
6479 QString url = component.url().toString();
6480 QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )");
6481 QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'");
6483 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6484 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6485 for (int x = 0; x < 3; ++x) {
6486 warningsSpy.clear();
6487 // For libraries, only the first import attempt should produce a
6488 // SyntaxError warning; subsequent component creation should not
6489 // attempt to reload the script.
6490 bool expectSyntaxError = !library || (x == 0);
6491 if (expectSyntaxError)
6492 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
6493 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
6494 QObject *object = component.create();
6495 QVERIFY(object != 0);
6496 QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
6502 void tst_qqmlecmascript::switchStatement()
6505 QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml"));
6506 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6507 QVERIFY(object != 0);
6509 // `object->value()' is the number of executed statements
6511 object->setStringProperty("A");
6512 QCOMPARE(object->value(), 5);
6514 object->setStringProperty("S");
6515 QCOMPARE(object->value(), 3);
6517 object->setStringProperty("D");
6518 QCOMPARE(object->value(), 3);
6520 object->setStringProperty("F");
6521 QCOMPARE(object->value(), 4);
6523 object->setStringProperty("something else");
6524 QCOMPARE(object->value(), 1);
6528 QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml"));
6529 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6530 QVERIFY(object != 0);
6532 // `object->value()' is the number of executed statements
6534 object->setStringProperty("A");
6535 QCOMPARE(object->value(), 5);
6537 object->setStringProperty("S");
6538 QCOMPARE(object->value(), 3);
6540 object->setStringProperty("D");
6541 QCOMPARE(object->value(), 3);
6543 object->setStringProperty("F");
6544 QCOMPARE(object->value(), 3);
6546 object->setStringProperty("something else");
6547 QCOMPARE(object->value(), 4);
6551 QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml"));
6552 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6553 QVERIFY(object != 0);
6555 // `object->value()' is the number of executed statements
6557 object->setStringProperty("A");
6558 QCOMPARE(object->value(), 5);
6560 object->setStringProperty("S");
6561 QCOMPARE(object->value(), 3);
6563 object->setStringProperty("D");
6564 QCOMPARE(object->value(), 3);
6566 object->setStringProperty("F");
6567 QCOMPARE(object->value(), 3);
6569 object->setStringProperty("something else");
6570 QCOMPARE(object->value(), 6);
6574 QQmlComponent component(&engine, testFileUrl("switchStatement.4.qml"));
6576 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
6577 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6579 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6580 QVERIFY(object != 0);
6582 // `object->value()' is the number of executed statements
6584 object->setStringProperty("A");
6585 QCOMPARE(object->value(), 5);
6587 object->setStringProperty("S");
6588 QCOMPARE(object->value(), 3);
6590 object->setStringProperty("D");
6591 QCOMPARE(object->value(), 3);
6593 object->setStringProperty("F");
6594 QCOMPARE(object->value(), 3);
6596 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6598 object->setStringProperty("something else");
6602 QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml"));
6603 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6604 QVERIFY(object != 0);
6606 // `object->value()' is the number of executed statements
6608 object->setStringProperty("A");
6609 QCOMPARE(object->value(), 1);
6611 object->setStringProperty("S");
6612 QCOMPARE(object->value(), 1);
6614 object->setStringProperty("D");
6615 QCOMPARE(object->value(), 1);
6617 object->setStringProperty("F");
6618 QCOMPARE(object->value(), 1);
6620 object->setStringProperty("something else");
6621 QCOMPARE(object->value(), 1);
6625 QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml"));
6626 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6627 QVERIFY(object != 0);
6629 // `object->value()' is the number of executed statements
6631 object->setStringProperty("A");
6632 QCOMPARE(object->value(), 123);
6634 object->setStringProperty("S");
6635 QCOMPARE(object->value(), 123);
6637 object->setStringProperty("D");
6638 QCOMPARE(object->value(), 321);
6640 object->setStringProperty("F");
6641 QCOMPARE(object->value(), 321);
6643 object->setStringProperty("something else");
6644 QCOMPARE(object->value(), 0);
6648 void tst_qqmlecmascript::withStatement()
6651 QQmlComponent component(&engine, testFileUrl("withStatement.1.qml"));
6652 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6653 QVERIFY(object != 0);
6655 QCOMPARE(object->value(), 123);
6659 void tst_qqmlecmascript::tryStatement()
6662 QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml"));
6663 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6664 QVERIFY(object != 0);
6666 QCOMPARE(object->value(), 123);
6670 QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml"));
6671 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6672 QVERIFY(object != 0);
6674 QCOMPARE(object->value(), 321);
6678 QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml"));
6679 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6680 QVERIFY(object != 0);
6682 QCOMPARE(object->value(), 1);
6686 QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml"));
6687 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6688 QVERIFY(object != 0);
6690 QCOMPARE(object->value(), 1);
6694 class CppInvokableWithQObjectDerived : public QObject
6698 CppInvokableWithQObjectDerived() {}
6699 ~CppInvokableWithQObjectDerived() {}
6701 Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data)
6703 MyQmlObject *obj = new MyQmlObject();
6704 obj->setStringProperty(data);
6708 Q_INVOKABLE QString getStringProperty(MyQmlObject *obj)
6710 return obj->stringProperty();
6714 void tst_qqmlecmascript::invokableWithQObjectDerived()
6716 CppInvokableWithQObjectDerived invokable;
6720 engine.rootContext()->setContextProperty("invokable", &invokable);
6722 QQmlComponent component(&engine, testFileUrl("qobjectDerivedArgument.qml"));
6724 QObject *object = component.create();
6726 QVERIFY(object != 0);
6727 QVERIFY(object->property("result").value<bool>() == true);
6733 void tst_qqmlecmascript::realTypePrecision()
6735 // Properties and signal parameters of type real should have double precision.
6736 QQmlComponent component(&engine, testFileUrl("realTypePrecision.qml"));
6737 QScopedPointer<QObject> object(component.create());
6738 QVERIFY(object != 0);
6739 QCOMPARE(object->property("test").toDouble(), 1234567890.);
6740 QCOMPARE(object->property("test2").toDouble(), 1234567890.);
6741 QCOMPARE(object->property("test3").toDouble(), 1234567890.);
6742 QCOMPARE(object->property("test4").toDouble(), 1234567890.);
6743 QCOMPARE(object->property("test5").toDouble(), 1234567890.);
6744 QCOMPARE(object->property("test6").toDouble(), 1234567890.*2);
6747 void tst_qqmlecmascript::registeredFlagMethod()
6750 QQmlComponent component(&engine, testFileUrl("registeredFlagMethod.qml"));
6751 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6752 QVERIFY(object != 0);
6754 QCOMPARE(object->buttons(), 0);
6755 emit object->basicSignal();
6756 QCOMPARE(object->buttons(), Qt::RightButton);
6762 void tst_qqmlecmascript::replaceBinding()
6765 QQmlComponent c(&engine, testFileUrl("replaceBinding.qml"));
6766 QObject *obj = c.create();
6769 QVERIFY(obj->property("success").toBool());
6773 void tst_qqmlecmascript::deleteRootObjectInCreation()
6777 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.qml"));
6778 QObject *obj = c.create();
6780 QVERIFY(obj->property("rootIndestructible").toBool());
6781 QVERIFY(!obj->property("childDestructible").toBool());
6783 QVERIFY(obj->property("childDestructible").toBool());
6788 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.2.qml"));
6789 QObject *object = c.create();
6790 QVERIFY(object != 0);
6791 QVERIFY(object->property("testConditionsMet").toBool());
6796 void tst_qqmlecmascript::onDestruction()
6799 // Delete object manually to invoke the associated handlers,
6800 // prior to engine destruction.
6802 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
6803 QObject *obj = c.create();
6809 // In this case, the teardown of the engine causes deletion
6810 // of contexts and child items. This triggers the
6811 // onDestruction handler of a (previously .destroy()ed)
6812 // component instance. This shouldn't crash.
6814 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
6815 QObject *obj = c.create();
6820 struct EventProcessor : public QObject
6824 Q_INVOKABLE void process()
6826 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
6827 QCoreApplication::processEvents();
6831 void tst_qqmlecmascript::bindingSuppression()
6834 EventProcessor processor;
6835 engine.rootContext()->setContextProperty("pendingEvents", &processor);
6837 transientErrorsMsgCount = 0;
6838 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
6840 QQmlComponent c(&engine, testFileUrl("bindingSuppression.qml"));
6841 QObject *obj = c.create();
6845 qInstallMsgHandler(old);
6846 QCOMPARE(transientErrorsMsgCount, 0);
6849 void tst_qqmlecmascript::signalEmitted()
6852 // calling destroy on the sibling.
6854 QQmlComponent c(&engine, testFileUrl("signalEmitted.2.qml"));
6855 QObject *obj = c.create();
6857 QTRY_VERIFY(obj->property("success").toBool());
6862 // allowing gc to clean up the sibling.
6864 QQmlComponent c(&engine, testFileUrl("signalEmitted.3.qml"));
6865 QObject *obj = c.create();
6867 gc(engine); // should collect c1.
6868 QTRY_VERIFY(obj->property("success").toBool());
6873 // allowing gc to clean up the sibling after manually destroying target.
6875 QQmlComponent c(&engine, testFileUrl("signalEmitted.4.qml"));
6876 QObject *obj = c.create();
6878 gc(engine); // should collect c1.
6879 QMetaObject::invokeMethod(obj, "destroyC2");
6880 QTRY_VERIFY(obj->property("success").toBool()); // handles events (incl. delete later).
6886 void tst_qqmlecmascript::threadSignal()
6889 QQmlComponent c(&engine, testFileUrl("threadSignal.qml"));
6890 QObject *object = c.create();
6891 QVERIFY(object != 0);
6892 QTRY_VERIFY(object->property("passed").toBool());
6896 QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml"));
6897 QObject *object = c.create();
6898 QVERIFY(object != 0);
6899 QSignalSpy doneSpy(object, SIGNAL(done(const QString &)));
6900 QMetaObject::invokeMethod(object, "doIt");
6901 QTRY_VERIFY(object->property("passed").toBool());
6902 QCOMPARE(doneSpy.count(), 1);
6907 // ensure that the qqmldata::destroyed() handler doesn't cause problems
6908 void tst_qqmlecmascript::qqmldataDestroyed()
6910 // gc cleans up a qobject, later the qqmldata destroyed handler will run.
6912 QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.qml"));
6913 QObject *object = c.create();
6914 QVERIFY(object != 0);
6915 // now gc causing the collection of the dynamically constructed object.
6916 engine.collectGarbage();
6917 engine.collectGarbage();
6918 // now process events to allow deletion (calling qqmldata::destroyed())
6919 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
6920 QCoreApplication::processEvents();
6925 // in this case, the object has CPP ownership, and the gc will
6926 // be triggered during its beginCreate stage.
6928 QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.2.qml"));
6929 QObject *object = c.create();
6930 QVERIFY(object != 0);
6931 QVERIFY(object->property("testConditionsMet").toBool());
6932 // the gc() within the handler will have triggered the weak
6933 // qobject reference callback. If that incorrectly disposes
6934 // the handle, when the qqmldata::destroyed() handler is
6935 // called due to object deletion we will see a crash.
6937 // shouldn't have crashed.
6941 void tst_qqmlecmascript::secondAlias()
6943 QQmlComponent c(&engine, testFileUrl("secondAlias.qml"));
6944 QObject *object = c.create();
6945 QVERIFY(object != 0);
6946 QCOMPARE(object->property("test").toInt(), 200);
6950 // An alias to a var property works
6951 void tst_qqmlecmascript::varAlias()
6953 QQmlComponent c(&engine, testFileUrl("varAlias.qml"));
6954 QObject *object = c.create();
6955 QVERIFY(object != 0);
6956 QCOMPARE(object->property("test").toInt(), 192);
6960 // Used to trigger an assert in the lazy meta object creation stage
6961 void tst_qqmlecmascript::overrideDataAssert()
6963 QQmlComponent c(&engine, testFileUrl("overrideDataAssert.qml"));
6964 QObject *object = c.create();
6965 QVERIFY(object != 0);
6966 object->metaObject();
6970 QTEST_MAIN(tst_qqmlecmascript)
6972 #include "tst_qqmlecmascript.moc"