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 idShortcutInvalidates();
77 void boolPropertiesEvaluateAsBool();
79 void signalAssignment();
81 void basicExpressions();
82 void basicExpressions_data();
83 void arrayExpressions();
84 void contextPropertiesTriggerReeval();
85 void objectPropertiesTriggerReeval();
86 void deferredProperties();
87 void deferredPropertiesErrors();
88 void extensionObjects();
89 void overrideExtensionProperties();
90 void attachedProperties();
92 void valueTypeFunctions();
93 void constantsOverrideBindings();
94 void outerBindingOverridesInnerBinding();
95 void aliasPropertyAndBinding();
96 void aliasPropertyReset();
97 void nonExistentAttachedObject();
100 void signalParameterTypes();
101 void objectsCompareAsEqual();
102 void componentCreation_data();
103 void componentCreation();
104 void dynamicCreation_data();
105 void dynamicCreation();
106 void dynamicDestruction();
107 void objectToString();
108 void objectHasOwnProperty();
109 void selfDeletingBinding();
110 void extendedObjectPropertyLookup();
111 void extendedObjectPropertyLookup2();
113 void functionErrors();
114 void propertyAssignmentErrors();
115 void signalTriggeredBindings();
116 void listProperties();
117 void exceptionClearsOnReeval();
118 void exceptionSlotProducesWarning();
119 void exceptionBindingProducesWarning();
120 void compileInvalidBinding();
121 void transientErrors();
122 void shutdownErrors();
123 void compositePropertyType();
125 void undefinedResetsProperty();
126 void listToVariant();
127 void listAssignment();
128 void multiEngineObject();
129 void deletedObject();
130 void attachedPropertyScope();
131 void scriptConnect();
132 void scriptDisconnect();
134 void cppOwnershipReturnValue();
135 void ownershipCustomReturnValue();
136 void ownershipRootObject();
137 void ownershipConsistency();
138 void ownershipQmlIncubated();
139 void qlistqobjectMethods();
140 void strictlyEquals();
142 void numberAssignment();
143 void propertySplicing();
144 void signalWithUnknownTypes();
145 void signalWithJSValueInVariant_data();
146 void signalWithJSValueInVariant();
147 void signalWithJSValueInVariant_twoEngines_data();
148 void signalWithJSValueInVariant_twoEngines();
149 void signalWithQJSValue_data();
150 void signalWithQJSValue();
151 void moduleApi_data();
153 void importScripts_data();
154 void importScripts();
155 void scarceResources();
156 void scarceResources_data();
157 void scarceResources_other();
158 void propertyChangeSlots();
159 void propertyVar_data();
161 void propertyVarCpp();
162 void propertyVarOwnership();
163 void propertyVarImplicitOwnership();
164 void propertyVarReparent();
165 void propertyVarReparentNullContext();
166 void propertyVarCircular();
167 void propertyVarCircular2();
168 void propertyVarInheritance();
169 void propertyVarInheritance2();
170 void elementAssign();
171 void objectPassThroughSignals();
172 void objectConversion();
173 void booleanConversion();
174 void handleReferenceManagement();
176 void readonlyDeclaration();
177 void sequenceConversionRead();
178 void sequenceConversionWrite();
179 void sequenceConversionArray();
180 void sequenceConversionIndexes();
181 void sequenceConversionThreads();
182 void sequenceConversionBindings();
183 void sequenceConversionCopy();
184 void assignSequenceTypes();
187 void singleV8BindingDestroyedDuringEvaluation();
190 void dynamicCreationCrash();
191 void dynamicCreationOwnership();
193 void nullObjectBinding();
194 void deletedEngine();
195 void libraryScriptAssert();
196 void variantsAssignedUndefined();
198 void qtcreatorbug_1289();
199 void noSpuriousWarningsAtShutdown();
200 void canAssignNullToQObject();
201 void functionAssignment_fromBinding();
202 void functionAssignment_fromJS();
203 void functionAssignment_fromJS_data();
204 void functionAssignmentfromJS_invalid();
207 void functionException();
212 void qobjectConnectionListExceptionHandling();
213 void nonscriptable();
218 void sharedAttachedObject();
220 void writeRemovesBinding();
221 void aliasBindingsAssignCorrectly();
222 void aliasBindingsOverrideTarget();
223 void aliasWritesOverrideBindings();
224 void aliasToCompositeElement();
227 void urlPropertyWithEncoding();
228 void urlListPropertyWithEncoding();
229 void dynamicString();
231 void signalHandlers();
232 void doubleEvaluate();
234 void nonNotifyable();
235 void deleteWhileBindingRunning();
236 void callQtInvokables();
237 void invokableObjectArg();
238 void invokableObjectRet();
241 void qtbug_22843_data();
243 void rewriteMultiLineStrings();
244 void revisionErrors();
246 void invokableWithQObjectDerived();
247 void realTypePrecision();
248 void registeredFlagMethod();
250 void automaticSemicolon();
251 void unaryExpression();
252 void switchStatement();
253 void withStatement();
257 static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
261 void tst_qqmlecmascript::initTestCase()
263 QQmlDataTest::initTestCase();
266 QString dataDir(dataDirectory() + QLatin1Char('/') + QLatin1String("lib"));
267 engine.addImportPath(dataDir);
270 void tst_qqmlecmascript::assignBasicTypes()
273 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.qml"));
274 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
275 QVERIFY(object != 0);
276 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
277 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
278 QCOMPARE(object->stringProperty(), QString("Hello World!"));
279 QCOMPARE(object->uintProperty(), uint(10));
280 QCOMPARE(object->intProperty(), -19);
281 QCOMPARE((float)object->realProperty(), float(23.2));
282 QCOMPARE((float)object->doubleProperty(), float(-19.75));
283 QCOMPARE((float)object->floatProperty(), float(8.5));
284 QCOMPARE(object->colorProperty(), QColor("red"));
285 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
286 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
287 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
288 QCOMPARE(object->pointProperty(), QPoint(99,13));
289 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
290 QCOMPARE(object->sizeProperty(), QSize(99, 13));
291 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
292 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
293 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
294 QCOMPARE(object->boolProperty(), true);
295 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
296 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
297 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
301 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.2.qml"));
302 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
303 QVERIFY(object != 0);
304 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
305 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
306 QCOMPARE(object->stringProperty(), QString("Hello World!"));
307 QCOMPARE(object->uintProperty(), uint(10));
308 QCOMPARE(object->intProperty(), -19);
309 QCOMPARE((float)object->realProperty(), float(23.2));
310 QCOMPARE((float)object->doubleProperty(), float(-19.75));
311 QCOMPARE((float)object->floatProperty(), float(8.5));
312 QCOMPARE(object->colorProperty(), QColor("red"));
313 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
314 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
315 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
316 QCOMPARE(object->pointProperty(), QPoint(99,13));
317 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
318 QCOMPARE(object->sizeProperty(), QSize(99, 13));
319 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
320 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
321 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
322 QCOMPARE(object->boolProperty(), true);
323 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
324 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
325 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
330 void tst_qqmlecmascript::assignDate_data()
332 QTest::addColumn<QUrl>("source");
333 QTest::newRow("Component.onComplete JS") << testFileUrl("assignDate.qml");
334 QTest::newRow("Binding JS") << testFileUrl("assignDate.2.qml");
335 QTest::newRow("Binding UTC") << testFileUrl("assignDate.3.qml");
336 QTest::newRow("Binding JS UTC") << testFileUrl("assignDate.4.qml");
337 QTest::newRow("Binding UTC+2") << testFileUrl("assignDate.5.qml");
338 QTest::newRow("Binding JS UTC+2 ") << testFileUrl("assignDate.6.qml");
341 void tst_qqmlecmascript::assignDate()
343 QFETCH(QUrl, source);
344 QQmlComponent component(&engine, source);
345 QScopedPointer<QObject> obj(component.create());
346 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
347 QVERIFY(object != 0);
348 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
349 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
352 void tst_qqmlecmascript::idShortcutInvalidates()
355 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.qml"));
356 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
357 QVERIFY(object != 0);
358 QVERIFY(object->objectProperty() != 0);
359 delete object->objectProperty();
360 QVERIFY(object->objectProperty() == 0);
365 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.1.qml"));
366 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
367 QVERIFY(object != 0);
368 QVERIFY(object->objectProperty() != 0);
369 delete object->objectProperty();
370 QVERIFY(object->objectProperty() == 0);
375 void tst_qqmlecmascript::boolPropertiesEvaluateAsBool()
378 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.1.qml"));
379 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
380 QVERIFY(object != 0);
381 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
385 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.2.qml"));
386 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
387 QVERIFY(object != 0);
388 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
393 void tst_qqmlecmascript::signalAssignment()
396 QQmlComponent component(&engine, testFileUrl("signalAssignment.1.qml"));
397 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
398 QVERIFY(object != 0);
399 QCOMPARE(object->string(), QString());
400 emit object->basicSignal();
401 QCOMPARE(object->string(), QString("pass"));
406 QQmlComponent component(&engine, testFileUrl("signalAssignment.2.qml"));
407 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
408 QVERIFY(object != 0);
409 QCOMPARE(object->string(), QString());
410 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
411 QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
416 QQmlComponent component(&engine, testFileUrl("signalAssignment.3.qml"));
417 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
418 QVERIFY(object != 0);
419 QCOMPARE(object->string(), QString());
420 emit object->unnamedArgumentSignal(19, 10.25, "Hello world!");
421 QEXPECT_FAIL("", "QTBUG-24481", Continue);
422 QCOMPARE(object->string(), QString("pass 19 Hello world!"));
427 QQmlComponent component(&engine, testFileUrl("signalAssignment.4.qml"));
428 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
429 QVERIFY(object != 0);
430 QCOMPARE(object->string(), QString());
431 emit object->signalWithGlobalName(19);
432 QCOMPARE(object->string(), QString("pass 5"));
437 void tst_qqmlecmascript::methods()
440 QQmlComponent component(&engine, testFileUrl("methods.1.qml"));
441 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
442 QVERIFY(object != 0);
443 QCOMPARE(object->methodCalled(), false);
444 QCOMPARE(object->methodIntCalled(), false);
445 emit object->basicSignal();
446 QCOMPARE(object->methodCalled(), true);
447 QCOMPARE(object->methodIntCalled(), false);
452 QQmlComponent component(&engine, testFileUrl("methods.2.qml"));
453 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
454 QVERIFY(object != 0);
455 QCOMPARE(object->methodCalled(), false);
456 QCOMPARE(object->methodIntCalled(), false);
457 emit object->basicSignal();
458 QCOMPARE(object->methodCalled(), false);
459 QCOMPARE(object->methodIntCalled(), true);
464 QQmlComponent component(&engine, testFileUrl("methods.3.qml"));
465 QObject *object = component.create();
466 QVERIFY(object != 0);
467 QCOMPARE(object->property("test").toInt(), 19);
472 QQmlComponent component(&engine, testFileUrl("methods.4.qml"));
473 QObject *object = component.create();
474 QVERIFY(object != 0);
475 QCOMPARE(object->property("test").toInt(), 19);
476 QCOMPARE(object->property("test2").toInt(), 17);
477 QCOMPARE(object->property("test3").toInt(), 16);
482 QQmlComponent component(&engine, testFileUrl("methods.5.qml"));
483 QObject *object = component.create();
484 QVERIFY(object != 0);
485 QCOMPARE(object->property("test").toInt(), 9);
490 void tst_qqmlecmascript::bindingLoop()
492 QQmlComponent component(&engine, testFileUrl("bindingLoop.qml"));
493 QString warning = component.url().toString() + ":5:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\"";
494 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
495 QObject *object = component.create();
496 QVERIFY(object != 0);
500 void tst_qqmlecmascript::basicExpressions_data()
502 QTest::addColumn<QString>("expression");
503 QTest::addColumn<QVariant>("result");
504 QTest::addColumn<bool>("nest");
506 QTest::newRow("Syntax error (self test)") << "{console.log({'a':1'}.a)}" << QVariant() << false;
507 QTest::newRow("Context property") << "a" << QVariant(1944) << false;
508 QTest::newRow("Context property") << "a" << QVariant(1944) << true;
509 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << false;
510 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << true;
511 QTest::newRow("Overridden context property") << "b" << QVariant("Milk") << false;
512 QTest::newRow("Overridden context property") << "b" << QVariant("Cow") << true;
513 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << false;
514 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << true;
515 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object2") << false;
516 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object3") << true;
517 QTest::newRow("Default object property") << "horseLegs" << QVariant(4) << false;
518 QTest::newRow("Default object property") << "antLegs" << QVariant(6) << false;
519 QTest::newRow("Default object property") << "emuLegs" << QVariant(2) << false;
520 QTest::newRow("Nested default object property") << "horseLegs" << QVariant(4) << true;
521 QTest::newRow("Nested default object property") << "antLegs" << QVariant(7) << true;
522 QTest::newRow("Nested default object property") << "emuLegs" << QVariant(2) << true;
523 QTest::newRow("Nested default object property") << "humanLegs" << QVariant(2) << true;
524 QTest::newRow("Context property override default object property") << "millipedeLegs" << QVariant(100) << true;
527 void tst_qqmlecmascript::basicExpressions()
529 QFETCH(QString, expression);
530 QFETCH(QVariant, result);
536 MyDefaultObject1 default1;
537 MyDefaultObject3 default3;
538 object1.setStringProperty("Object1");
539 object2.setStringProperty("Object2");
540 object3.setStringProperty("Object3");
542 QQmlContext context(engine.rootContext());
543 QQmlContext nestedContext(&context);
545 context.setContextObject(&default1);
546 context.setContextProperty("a", QVariant(1944));
547 context.setContextProperty("b", QVariant("Milk"));
548 context.setContextProperty("object", &object1);
549 context.setContextProperty("objectOverride", &object2);
550 nestedContext.setContextObject(&default3);
551 nestedContext.setContextProperty("b", QVariant("Cow"));
552 nestedContext.setContextProperty("objectOverride", &object3);
553 nestedContext.setContextProperty("millipedeLegs", QVariant(100));
555 MyExpression expr(nest?&nestedContext:&context, expression);
556 QCOMPARE(expr.evaluate(), result);
559 void tst_qqmlecmascript::arrayExpressions()
565 QQmlContext context(engine.rootContext());
566 context.setContextProperty("a", &obj1);
567 context.setContextProperty("b", &obj2);
568 context.setContextProperty("c", &obj3);
570 MyExpression expr(&context, "[a, b, c, 10]");
571 QVariant result = expr.evaluate();
572 QCOMPARE(result.userType(), qMetaTypeId<QList<QObject *> >());
573 QList<QObject *> list = qvariant_cast<QList<QObject *> >(result);
574 QCOMPARE(list.count(), 4);
575 QCOMPARE(list.at(0), &obj1);
576 QCOMPARE(list.at(1), &obj2);
577 QCOMPARE(list.at(2), &obj3);
578 QCOMPARE(list.at(3), (QObject *)0);
581 // Tests that modifying a context property will reevaluate expressions
582 void tst_qqmlecmascript::contextPropertiesTriggerReeval()
584 QQmlContext context(engine.rootContext());
587 MyQmlObject *object3 = new MyQmlObject;
589 object1.setStringProperty("Hello");
590 object2.setStringProperty("World");
592 context.setContextProperty("testProp", QVariant(1));
593 context.setContextProperty("testObj", &object1);
594 context.setContextProperty("testObj2", object3);
597 MyExpression expr(&context, "testProp + 1");
598 QCOMPARE(expr.changed, false);
599 QCOMPARE(expr.evaluate(), QVariant(2));
601 context.setContextProperty("testProp", QVariant(2));
602 QCOMPARE(expr.changed, true);
603 QCOMPARE(expr.evaluate(), QVariant(3));
607 MyExpression expr(&context, "testProp + testProp + testProp");
608 QCOMPARE(expr.changed, false);
609 QCOMPARE(expr.evaluate(), QVariant(6));
611 context.setContextProperty("testProp", QVariant(4));
612 QCOMPARE(expr.changed, true);
613 QCOMPARE(expr.evaluate(), QVariant(12));
617 MyExpression expr(&context, "testObj.stringProperty");
618 QCOMPARE(expr.changed, false);
619 QCOMPARE(expr.evaluate(), QVariant("Hello"));
621 context.setContextProperty("testObj", &object2);
622 QCOMPARE(expr.changed, true);
623 QCOMPARE(expr.evaluate(), QVariant("World"));
627 MyExpression expr(&context, "testObj.stringProperty /**/");
628 QCOMPARE(expr.changed, false);
629 QCOMPARE(expr.evaluate(), QVariant("World"));
631 context.setContextProperty("testObj", &object1);
632 QCOMPARE(expr.changed, true);
633 QCOMPARE(expr.evaluate(), QVariant("Hello"));
637 MyExpression expr(&context, "testObj2");
638 QCOMPARE(expr.changed, false);
639 QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3));
645 void tst_qqmlecmascript::objectPropertiesTriggerReeval()
647 QQmlContext context(engine.rootContext());
651 context.setContextProperty("testObj", &object1);
653 object1.setStringProperty(QLatin1String("Hello"));
654 object2.setStringProperty(QLatin1String("Dog"));
655 object3.setStringProperty(QLatin1String("Cat"));
658 MyExpression expr(&context, "testObj.stringProperty");
659 QCOMPARE(expr.changed, false);
660 QCOMPARE(expr.evaluate(), QVariant("Hello"));
662 object1.setStringProperty(QLatin1String("World"));
663 QCOMPARE(expr.changed, true);
664 QCOMPARE(expr.evaluate(), QVariant("World"));
668 MyExpression expr(&context, "testObj.objectProperty.stringProperty");
669 QCOMPARE(expr.changed, false);
670 QCOMPARE(expr.evaluate(), QVariant());
672 object1.setObjectProperty(&object2);
673 QCOMPARE(expr.changed, true);
674 expr.changed = false;
675 QCOMPARE(expr.evaluate(), QVariant("Dog"));
677 object1.setObjectProperty(&object3);
678 QCOMPARE(expr.changed, true);
679 expr.changed = false;
680 QCOMPARE(expr.evaluate(), QVariant("Cat"));
682 object1.setObjectProperty(0);
683 QCOMPARE(expr.changed, true);
684 expr.changed = false;
685 QCOMPARE(expr.evaluate(), QVariant());
687 object1.setObjectProperty(&object3);
688 QCOMPARE(expr.changed, true);
689 expr.changed = false;
690 QCOMPARE(expr.evaluate(), QVariant("Cat"));
692 object3.setStringProperty("Donkey");
693 QCOMPARE(expr.changed, true);
694 expr.changed = false;
695 QCOMPARE(expr.evaluate(), QVariant("Donkey"));
699 void tst_qqmlecmascript::deferredProperties()
701 QQmlComponent component(&engine, testFileUrl("deferredProperties.qml"));
702 MyDeferredObject *object =
703 qobject_cast<MyDeferredObject *>(component.create());
704 QVERIFY(object != 0);
705 QCOMPARE(object->value(), 0);
706 QVERIFY(object->objectProperty() == 0);
707 QVERIFY(object->objectProperty2() != 0);
708 qmlExecuteDeferred(object);
709 QCOMPARE(object->value(), 10);
710 QVERIFY(object->objectProperty() != 0);
711 MyQmlObject *qmlObject =
712 qobject_cast<MyQmlObject *>(object->objectProperty());
713 QVERIFY(qmlObject != 0);
714 QCOMPARE(qmlObject->value(), 10);
715 object->setValue(19);
716 QCOMPARE(qmlObject->value(), 19);
721 // Check errors on deferred properties are correctly emitted
722 void tst_qqmlecmascript::deferredPropertiesErrors()
724 QQmlComponent component(&engine, testFileUrl("deferredPropertiesErrors.qml"));
725 MyDeferredObject *object =
726 qobject_cast<MyDeferredObject *>(component.create());
727 QVERIFY(object != 0);
728 QCOMPARE(object->value(), 0);
729 QVERIFY(object->objectProperty() == 0);
730 QVERIFY(object->objectProperty2() == 0);
732 QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject*";
733 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
735 qmlExecuteDeferred(object);
740 void tst_qqmlecmascript::extensionObjects()
742 QQmlComponent component(&engine, testFileUrl("extensionObjects.qml"));
743 MyExtendedObject *object =
744 qobject_cast<MyExtendedObject *>(component.create());
745 QVERIFY(object != 0);
746 QCOMPARE(object->baseProperty(), 13);
747 QCOMPARE(object->coreProperty(), 9);
748 object->setProperty("extendedProperty", QVariant(11));
749 object->setProperty("baseExtendedProperty", QVariant(92));
750 QCOMPARE(object->coreProperty(), 11);
751 QCOMPARE(object->baseProperty(), 92);
753 MyExtendedObject *nested = qobject_cast<MyExtendedObject*>(qvariant_cast<QObject *>(object->property("nested")));
755 QCOMPARE(nested->baseProperty(), 13);
756 QCOMPARE(nested->coreProperty(), 9);
757 nested->setProperty("extendedProperty", QVariant(11));
758 nested->setProperty("baseExtendedProperty", QVariant(92));
759 QCOMPARE(nested->coreProperty(), 11);
760 QCOMPARE(nested->baseProperty(), 92);
765 void tst_qqmlecmascript::overrideExtensionProperties()
767 QQmlComponent component(&engine, testFileUrl("extensionObjectsPropertyOverride.qml"));
768 OverrideDefaultPropertyObject *object =
769 qobject_cast<OverrideDefaultPropertyObject *>(component.create());
770 QVERIFY(object != 0);
771 QVERIFY(object->secondProperty() != 0);
772 QVERIFY(object->firstProperty() == 0);
777 void tst_qqmlecmascript::attachedProperties()
780 QQmlComponent component(&engine, testFileUrl("attachedProperty.qml"));
781 QObject *object = component.create();
782 QVERIFY(object != 0);
783 QCOMPARE(object->property("a").toInt(), 19);
784 QCOMPARE(object->property("b").toInt(), 19);
785 QCOMPARE(object->property("c").toInt(), 19);
786 QCOMPARE(object->property("d").toInt(), 19);
791 QQmlComponent component(&engine, testFileUrl("attachedProperty.2.qml"));
792 QObject *object = component.create();
793 QVERIFY(object != 0);
794 QCOMPARE(object->property("a").toInt(), 26);
795 QCOMPARE(object->property("b").toInt(), 26);
796 QCOMPARE(object->property("c").toInt(), 26);
797 QCOMPARE(object->property("d").toInt(), 26);
803 QQmlComponent component(&engine, testFileUrl("writeAttachedProperty.qml"));
804 QObject *object = component.create();
805 QVERIFY(object != 0);
807 QMetaObject::invokeMethod(object, "writeValue2");
809 MyQmlAttachedObject *attached =
810 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
811 QVERIFY(attached != 0);
813 QCOMPARE(attached->value2(), 9);
818 void tst_qqmlecmascript::enums()
822 QQmlComponent component(&engine, testFileUrl("enums.1.qml"));
823 QObject *object = component.create();
824 QVERIFY(object != 0);
826 QCOMPARE(object->property("a").toInt(), 0);
827 QCOMPARE(object->property("b").toInt(), 1);
828 QCOMPARE(object->property("c").toInt(), 2);
829 QCOMPARE(object->property("d").toInt(), 3);
830 QCOMPARE(object->property("e").toInt(), 0);
831 QCOMPARE(object->property("f").toInt(), 1);
832 QCOMPARE(object->property("g").toInt(), 2);
833 QCOMPARE(object->property("h").toInt(), 3);
834 QCOMPARE(object->property("i").toInt(), 19);
835 QCOMPARE(object->property("j").toInt(), 19);
839 // Non-existent enums
841 QQmlComponent component(&engine, testFileUrl("enums.2.qml"));
843 QString warning1 = component.url().toString() + ":5: Unable to assign [undefined] to int";
844 QString warning2 = component.url().toString() + ":6: Unable to assign [undefined] to int";
845 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
846 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
848 QObject *object = component.create();
849 QVERIFY(object != 0);
850 QCOMPARE(object->property("a").toInt(), 0);
851 QCOMPARE(object->property("b").toInt(), 0);
857 QQmlComponent component(&engine, testFileUrl("enums.3.qml"));
858 QObject *object = component.create();
859 QVERIFY(object != 0);
861 // check the values are what we expect
862 QCOMPARE(object->property("a").toInt(), 4);
863 QCOMPARE(object->property("b").toInt(), 5);
864 QCOMPARE(object->property("c").toInt(), 9);
865 QCOMPARE(object->property("d").toInt(), 13);
866 QCOMPARE(object->property("e").toInt(), 2);
867 QCOMPARE(object->property("f").toInt(), 3);
868 QCOMPARE(object->property("h").toInt(), 2);
869 QCOMPARE(object->property("i").toInt(), 3);
871 // count of change signals
872 QCOMPARE(object->property("ac").toInt(), 0);
873 QCOMPARE(object->property("bc").toInt(), 0);
874 QCOMPARE(object->property("cc").toInt(), 0);
875 QCOMPARE(object->property("dc").toInt(), 0);
876 QCOMPARE(object->property("ec").toInt(), 0);
877 QCOMPARE(object->property("fc").toInt(), 0);
878 QCOMPARE(object->property("hc").toInt(), 1); // namespace -> binding
879 QCOMPARE(object->property("ic").toInt(), 1); // namespace -> binding
885 void tst_qqmlecmascript::valueTypeFunctions()
887 QQmlComponent component(&engine, testFileUrl("valueTypeFunctions.qml"));
888 MyTypeObject *obj = qobject_cast<MyTypeObject*>(component.create());
890 QCOMPARE(obj->rectProperty(), QRect(0,0,100,100));
891 QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5));
897 Tests that writing a constant to a property with a binding on it disables the
900 void tst_qqmlecmascript::constantsOverrideBindings()
904 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.1.qml"));
905 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
906 QVERIFY(object != 0);
908 QCOMPARE(object->property("c2").toInt(), 0);
909 object->setProperty("c1", QVariant(9));
910 QCOMPARE(object->property("c2").toInt(), 9);
912 emit object->basicSignal();
914 QCOMPARE(object->property("c2").toInt(), 13);
915 object->setProperty("c1", QVariant(8));
916 QCOMPARE(object->property("c2").toInt(), 13);
921 // During construction
923 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.2.qml"));
924 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
925 QVERIFY(object != 0);
927 QCOMPARE(object->property("c1").toInt(), 0);
928 QCOMPARE(object->property("c2").toInt(), 10);
929 object->setProperty("c1", QVariant(9));
930 QCOMPARE(object->property("c1").toInt(), 9);
931 QCOMPARE(object->property("c2").toInt(), 10);
939 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.3.qml"));
940 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
941 QVERIFY(object != 0);
943 QCOMPARE(object->property("c2").toInt(), 0);
944 object->setProperty("c1", QVariant(9));
945 QCOMPARE(object->property("c2").toInt(), 9);
947 object->setProperty("c2", QVariant(13));
948 QCOMPARE(object->property("c2").toInt(), 13);
949 object->setProperty("c1", QVariant(7));
950 QCOMPARE(object->property("c1").toInt(), 7);
951 QCOMPARE(object->property("c2").toInt(), 13);
959 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.4.qml"));
960 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
961 QVERIFY(object != 0);
963 QCOMPARE(object->property("c1").toInt(), 0);
964 QCOMPARE(object->property("c3").toInt(), 10);
965 object->setProperty("c1", QVariant(9));
966 QCOMPARE(object->property("c1").toInt(), 9);
967 QCOMPARE(object->property("c3").toInt(), 10);
974 Tests that assigning a binding to a property that already has a binding causes
975 the original binding to be disabled.
977 void tst_qqmlecmascript::outerBindingOverridesInnerBinding()
979 QQmlComponent component(&engine,
980 testFileUrl("outerBindingOverridesInnerBinding.qml"));
981 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
982 QVERIFY(object != 0);
984 QCOMPARE(object->property("c1").toInt(), 0);
985 QCOMPARE(object->property("c2").toInt(), 0);
986 QCOMPARE(object->property("c3").toInt(), 0);
988 object->setProperty("c1", QVariant(9));
989 QCOMPARE(object->property("c1").toInt(), 9);
990 QCOMPARE(object->property("c2").toInt(), 0);
991 QCOMPARE(object->property("c3").toInt(), 0);
993 object->setProperty("c3", QVariant(8));
994 QCOMPARE(object->property("c1").toInt(), 9);
995 QCOMPARE(object->property("c2").toInt(), 8);
996 QCOMPARE(object->property("c3").toInt(), 8);
1002 Access a non-existent attached object.
1004 Tests for a regression where this used to crash.
1006 void tst_qqmlecmascript::nonExistentAttachedObject()
1008 QQmlComponent component(&engine, testFileUrl("nonExistentAttachedObject.qml"));
1010 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString";
1011 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
1013 QObject *object = component.create();
1014 QVERIFY(object != 0);
1019 void tst_qqmlecmascript::scope()
1022 QQmlComponent component(&engine, testFileUrl("scope.qml"));
1023 QObject *object = component.create();
1024 QVERIFY(object != 0);
1026 QCOMPARE(object->property("test1").toInt(), 1);
1027 QCOMPARE(object->property("test2").toInt(), 2);
1028 QCOMPARE(object->property("test3").toString(), QString("1Test"));
1029 QCOMPARE(object->property("test4").toString(), QString("2Test"));
1030 QCOMPARE(object->property("test5").toInt(), 1);
1031 QCOMPARE(object->property("test6").toInt(), 1);
1032 QCOMPARE(object->property("test7").toInt(), 2);
1033 QCOMPARE(object->property("test8").toInt(), 2);
1034 QCOMPARE(object->property("test9").toInt(), 1);
1035 QCOMPARE(object->property("test10").toInt(), 3);
1041 QQmlComponent component(&engine, testFileUrl("scope.2.qml"));
1042 QObject *object = component.create();
1043 QVERIFY(object != 0);
1045 QCOMPARE(object->property("test1").toInt(), 19);
1046 QCOMPARE(object->property("test2").toInt(), 19);
1047 QCOMPARE(object->property("test3").toInt(), 14);
1048 QCOMPARE(object->property("test4").toInt(), 14);
1049 QCOMPARE(object->property("test5").toInt(), 24);
1050 QCOMPARE(object->property("test6").toInt(), 24);
1056 QQmlComponent component(&engine, testFileUrl("scope.3.qml"));
1057 QObject *object = component.create();
1058 QVERIFY(object != 0);
1060 QCOMPARE(object->property("test1").toBool(), true);
1061 QCOMPARE(object->property("test2").toBool(), true);
1062 QCOMPARE(object->property("test3").toBool(), true);
1067 // Signal argument scope
1069 QQmlComponent component(&engine, testFileUrl("scope.4.qml"));
1070 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1071 QVERIFY(object != 0);
1073 QCOMPARE(object->property("test").toInt(), 0);
1074 QCOMPARE(object->property("test2").toString(), QString());
1076 emit object->argumentSignal(13, "Argument Scope", 9, MyQmlObject::EnumValue4, Qt::RightButton);
1078 QCOMPARE(object->property("test").toInt(), 13);
1079 QCOMPARE(object->property("test2").toString(), QString("Argument Scope"));
1085 QQmlComponent component(&engine, testFileUrl("scope.5.qml"));
1086 QObject *object = component.create();
1087 QVERIFY(object != 0);
1089 QCOMPARE(object->property("test1").toBool(), true);
1090 QCOMPARE(object->property("test2").toBool(), true);
1096 QQmlComponent component(&engine, testFileUrl("scope.6.qml"));
1097 QObject *object = component.create();
1098 QVERIFY(object != 0);
1100 QCOMPARE(object->property("test").toBool(), true);
1106 // In 4.7, non-library javascript files that had no imports shared the imports of their
1107 // importing context
1108 void tst_qqmlecmascript::importScope()
1110 QQmlComponent component(&engine, testFileUrl("importScope.qml"));
1111 QObject *o = component.create();
1114 QCOMPARE(o->property("test").toInt(), 240);
1120 Tests that "any" type passes through a synthesized signal parameter. This
1121 is essentially a test of QQmlMetaType::copy()
1123 void tst_qqmlecmascript::signalParameterTypes()
1125 QQmlComponent component(&engine, testFileUrl("signalParameterTypes.qml"));
1126 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1127 QVERIFY(object != 0);
1129 emit object->basicSignal();
1131 QCOMPARE(object->property("intProperty").toInt(), 10);
1132 QCOMPARE(object->property("realProperty").toReal(), 19.2);
1133 QVERIFY(object->property("colorProperty").value<QColor>() == QColor(255, 255, 0, 255));
1134 QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255)));
1135 QVERIFY(object->property("enumProperty") == MyQmlObject::EnumValue3);
1136 QVERIFY(object->property("qtEnumProperty") == Qt::LeftButton);
1142 Test that two JS objects for the same QObject compare as equal.
1144 void tst_qqmlecmascript::objectsCompareAsEqual()
1146 QQmlComponent component(&engine, testFileUrl("objectsCompareAsEqual.qml"));
1147 QObject *object = component.create();
1148 QVERIFY(object != 0);
1150 QCOMPARE(object->property("test1").toBool(), true);
1151 QCOMPARE(object->property("test2").toBool(), true);
1152 QCOMPARE(object->property("test3").toBool(), true);
1153 QCOMPARE(object->property("test4").toBool(), true);
1154 QCOMPARE(object->property("test5").toBool(), true);
1160 Confirm bindings and alias properties can coexist.
1162 Tests for a regression where the binding would not reevaluate.
1164 void tst_qqmlecmascript::aliasPropertyAndBinding()
1166 QQmlComponent component(&engine, testFileUrl("aliasPropertyAndBinding.qml"));
1167 QObject *object = component.create();
1168 QVERIFY(object != 0);
1170 QCOMPARE(object->property("c2").toInt(), 3);
1171 QCOMPARE(object->property("c3").toInt(), 3);
1173 object->setProperty("c2", QVariant(19));
1175 QCOMPARE(object->property("c2").toInt(), 19);
1176 QCOMPARE(object->property("c3").toInt(), 19);
1182 Ensure that we can write undefined value to an alias property,
1183 and that the aliased property is reset correctly if possible.
1185 void tst_qqmlecmascript::aliasPropertyReset()
1187 QObject *object = 0;
1189 // test that a manual write (of undefined) to a resettable aliased property succeeds
1190 QQmlComponent c1(&engine, testFileUrl("aliasreset/aliasPropertyReset.1.qml"));
1191 object = c1.create();
1192 QVERIFY(object != 0);
1193 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1194 QCOMPARE(object->property("aliasIsUndefined"), QVariant(false));
1195 QMetaObject::invokeMethod(object, "resetAliased");
1196 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1197 QCOMPARE(object->property("aliasIsUndefined"), QVariant(true));
1200 // test that a manual write (of undefined) to a resettable alias property succeeds
1201 QQmlComponent c2(&engine, testFileUrl("aliasreset/aliasPropertyReset.2.qml"));
1202 object = c2.create();
1203 QVERIFY(object != 0);
1204 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1205 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false));
1206 QMetaObject::invokeMethod(object, "resetAlias");
1207 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1208 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(true));
1211 // test that an alias to a bound property works correctly
1212 QQmlComponent c3(&engine, testFileUrl("aliasreset/aliasPropertyReset.3.qml"));
1213 object = c3.create();
1214 QVERIFY(object != 0);
1215 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1216 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false));
1217 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1218 QMetaObject::invokeMethod(object, "resetAlias");
1219 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1220 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(true));
1221 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1224 // test that a manual write (of undefined) to a resettable alias property
1225 // whose aliased property's object has been deleted, does not crash.
1226 QQmlComponent c4(&engine, testFileUrl("aliasreset/aliasPropertyReset.4.qml"));
1227 object = c4.create();
1228 QVERIFY(object != 0);
1229 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1230 QObject *loader = object->findChild<QObject*>("loader");
1231 QVERIFY(loader != 0);
1233 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // deletion should have caused value unset.
1234 QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash.
1235 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1236 QMetaObject::invokeMethod(object, "setAlias"); // shouldn't crash, and shouldn't change value (since it's no longer referencing anything).
1237 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1240 // test that binding an alias property to an undefined value works correctly
1241 QQmlComponent c5(&engine, testFileUrl("aliasreset/aliasPropertyReset.5.qml"));
1242 object = c5.create();
1243 QVERIFY(object != 0);
1244 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // bound to undefined value.
1247 // test that a manual write (of undefined) to a non-resettable property fails properly
1248 QUrl url = testFileUrl("aliasreset/aliasPropertyReset.error.1.qml");
1249 QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int");
1250 QQmlComponent e1(&engine, url);
1251 object = e1.create();
1252 QVERIFY(object != 0);
1253 QCOMPARE(object->property("intAlias").value<int>(), 12);
1254 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1255 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1256 QMetaObject::invokeMethod(object, "resetAlias");
1257 QCOMPARE(object->property("intAlias").value<int>(), 12);
1258 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1262 void tst_qqmlecmascript::componentCreation_data()
1264 QTest::addColumn<QString>("method");
1265 QTest::addColumn<QString>("creationError");
1266 QTest::addColumn<QString>("createdParent");
1268 QTest::newRow("url")
1272 QTest::newRow("urlMode")
1276 QTest::newRow("urlParent")
1280 QTest::newRow("urlNullParent")
1284 QTest::newRow("urlModeParent")
1288 QTest::newRow("urlModeNullParent")
1289 << "urlModeNullParent"
1292 QTest::newRow("invalidSecondArg")
1293 << "invalidSecondArg"
1294 << ":40: Error: Qt.createComponent(): Invalid arguments"
1296 QTest::newRow("invalidThirdArg")
1297 << "invalidThirdArg"
1298 << ":45: Error: Qt.createComponent(): Invalid parent object"
1300 QTest::newRow("invalidMode")
1302 << ":50: Error: Qt.createComponent(): Invalid arguments"
1307 Test using createComponent to dynamically generate a component.
1309 void tst_qqmlecmascript::componentCreation()
1311 QFETCH(QString, method);
1312 QFETCH(QString, creationError);
1313 QFETCH(QString, createdParent);
1315 QUrl testUrl(testFileUrl("componentCreation.qml"));
1317 if (!creationError.isEmpty()) {
1318 QString warning = testUrl.toString() + creationError;
1319 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1322 QQmlComponent component(&engine, testUrl);
1323 MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create());
1324 QVERIFY(object != 0);
1326 QMetaObject::invokeMethod(object, method.toUtf8());
1327 QQmlComponent *created = object->componentProperty();
1329 if (creationError.isEmpty()) {
1332 QObject *expectedParent;
1333 if (createdParent.isEmpty()) {
1334 // For now, the parent should be the engine; this will change for QTBUG-24841
1335 expectedParent = &engine;
1336 } else if (createdParent == QLatin1String("obj")) {
1337 expectedParent = object;
1338 } else if (createdParent == QLatin1String("null")) {
1341 QCOMPARE(created->parent(), expectedParent);
1345 void tst_qqmlecmascript::dynamicCreation_data()
1347 QTest::addColumn<QString>("method");
1348 QTest::addColumn<QString>("createdName");
1350 QTest::newRow("One") << "createOne" << "objectOne";
1351 QTest::newRow("Two") << "createTwo" << "objectTwo";
1352 QTest::newRow("Three") << "createThree" << "objectThree";
1356 Test using createQmlObject to dynamically generate an item
1357 Also using createComponent is tested.
1359 void tst_qqmlecmascript::dynamicCreation()
1361 QFETCH(QString, method);
1362 QFETCH(QString, createdName);
1364 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
1365 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1366 QVERIFY(object != 0);
1368 QMetaObject::invokeMethod(object, method.toUtf8());
1369 QObject *created = object->objectProperty();
1371 QCOMPARE(created->objectName(), createdName);
1377 Tests the destroy function
1379 void tst_qqmlecmascript::dynamicDestruction()
1382 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.qml"));
1383 QQmlGuard<MyQmlObject> object = qobject_cast<MyQmlObject*>(component.create());
1384 QVERIFY(object != 0);
1385 QQmlGuard<QObject> createdQmlObject = 0;
1387 QMetaObject::invokeMethod(object, "create");
1388 createdQmlObject = object->objectProperty();
1389 QVERIFY(createdQmlObject);
1390 QCOMPARE(createdQmlObject->objectName(), QString("emptyObject"));
1392 QMetaObject::invokeMethod(object, "killOther");
1393 QVERIFY(createdQmlObject);
1395 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1396 QCoreApplication::processEvents();
1397 QVERIFY(createdQmlObject);
1398 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1399 if (createdQmlObject) {
1401 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1402 QCoreApplication::processEvents();
1405 QVERIFY(!createdQmlObject);
1407 QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
1408 QMetaObject::invokeMethod(object, "killMe");
1410 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1411 QCoreApplication::processEvents();
1416 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.2.qml"));
1417 QObject *o = component.create();
1420 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1422 QMetaObject::invokeMethod(o, "create");
1424 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) != 0);
1426 QMetaObject::invokeMethod(o, "destroy");
1428 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1429 QCoreApplication::processEvents();
1431 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1438 QQmlGuard<QObject> createdQmlObject = 0;
1439 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.3.qml"));
1440 QObject *o = component.create();
1442 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1443 QMetaObject::invokeMethod(o, "create");
1444 createdQmlObject = qvariant_cast<QObject*>(o->property("objectProperty"));
1445 QVERIFY(createdQmlObject);
1446 QMetaObject::invokeMethod(o, "destroy");
1447 QVERIFY(qvariant_cast<bool>(o->property("test")) == false);
1448 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1450 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1451 QCoreApplication::processEvents();
1453 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1454 QVERIFY(qvariant_cast<bool>(o->property("test")) == true);
1460 tests that id.toString() works
1462 void tst_qqmlecmascript::objectToString()
1464 QQmlComponent component(&engine, testFileUrl("qmlToString.qml"));
1465 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1466 QVERIFY(object != 0);
1467 QMetaObject::invokeMethod(object, "testToString");
1468 QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_"));
1469 QVERIFY(object->stringProperty().endsWith(", \"objName\")"));
1475 tests that id.hasOwnProperty() works
1477 void tst_qqmlecmascript::objectHasOwnProperty()
1479 QUrl url = testFileUrl("qmlHasOwnProperty.qml");
1480 QString warning1 = url.toString() + ":59: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1481 QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1482 QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1484 QQmlComponent component(&engine, url);
1485 QObject *object = component.create();
1486 QVERIFY(object != 0);
1488 // test QObjects in QML
1489 QMetaObject::invokeMethod(object, "testHasOwnPropertySuccess");
1490 QVERIFY(object->property("result").value<bool>() == true);
1491 QMetaObject::invokeMethod(object, "testHasOwnPropertyFailure");
1492 QVERIFY(object->property("result").value<bool>() == false);
1494 // now test other types in QML
1495 QObject *child = object->findChild<QObject*>("typeObj");
1496 QVERIFY(child != 0);
1497 QMetaObject::invokeMethod(child, "testHasOwnPropertySuccess");
1498 QCOMPARE(child->property("valueTypeHasOwnProperty").toBool(), true);
1499 QCOMPARE(child->property("valueTypeHasOwnProperty2").toBool(), true);
1500 QCOMPARE(child->property("variantTypeHasOwnProperty").toBool(), true);
1501 QCOMPARE(child->property("stringTypeHasOwnProperty").toBool(), true);
1502 QCOMPARE(child->property("listTypeHasOwnProperty").toBool(), true);
1503 QCOMPARE(child->property("emptyListTypeHasOwnProperty").toBool(), true);
1504 QCOMPARE(child->property("enumTypeHasOwnProperty").toBool(), true);
1505 QCOMPARE(child->property("typenameHasOwnProperty").toBool(), true);
1506 QCOMPARE(child->property("typenameHasOwnProperty2").toBool(), true);
1507 QCOMPARE(child->property("moduleApiTypeHasOwnProperty").toBool(), true);
1508 QCOMPARE(child->property("moduleApiPropertyTypeHasOwnProperty").toBool(), true);
1510 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1511 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureOne");
1512 QCOMPARE(child->property("enumNonValueHasOwnProperty").toBool(), false);
1513 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1514 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureTwo");
1515 QCOMPARE(child->property("moduleApiNonPropertyHasOwnProperty").toBool(), false);
1516 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1517 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureThree");
1518 QCOMPARE(child->property("listAtInvalidHasOwnProperty").toBool(), false);
1524 Tests bindings that indirectly cause their own deletion work.
1526 This test is best run under valgrind to ensure no invalid memory access occur.
1528 void tst_qqmlecmascript::selfDeletingBinding()
1531 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.qml"));
1532 QObject *object = component.create();
1533 QVERIFY(object != 0);
1534 object->setProperty("triggerDelete", true);
1539 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.2.qml"));
1540 QObject *object = component.create();
1541 QVERIFY(object != 0);
1542 object->setProperty("triggerDelete", true);
1548 Test that extended object properties can be accessed.
1550 This test a regression where this used to crash. The issue was specificially
1551 for extended objects that did not include a synthesized meta object (so non-root
1552 and no synthesiszed properties).
1554 void tst_qqmlecmascript::extendedObjectPropertyLookup()
1556 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup.qml"));
1557 QObject *object = component.create();
1558 QVERIFY(object != 0);
1563 Test that extended object properties can be accessed correctly.
1565 void tst_qqmlecmascript::extendedObjectPropertyLookup2()
1567 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup2.qml"));
1568 QObject *object = component.create();
1569 QVERIFY(object != 0);
1571 QVariant returnValue;
1572 QVERIFY(QMetaObject::invokeMethod(object, "getValue", Q_RETURN_ARG(QVariant, returnValue)));
1573 QCOMPARE(returnValue.toInt(), 42);
1578 Test file/lineNumbers for binding/Script errors.
1580 void tst_qqmlecmascript::scriptErrors()
1582 QQmlComponent component(&engine, testFileUrl("scriptErrors.qml"));
1583 QString url = component.url().toString();
1585 QString warning1 = url.left(url.length() - 3) + "js:2: Error: Invalid write to global property \"a\"";
1586 QString warning2 = url + ":5: ReferenceError: a is not defined";
1587 QString warning3 = url.left(url.length() - 3) + "js:4: Error: Invalid write to global property \"a\"";
1588 QString warning4 = url + ":13: ReferenceError: a is not defined";
1589 QString warning5 = url + ":11: ReferenceError: a is not defined";
1590 QString warning6 = url + ":10: Unable to assign [undefined] to int";
1591 QString warning7 = url + ":15: Error: Cannot assign to read-only property \"trueProperty\"";
1592 QString warning8 = url + ":16: Error: Cannot assign to non-existent property \"fakeProperty\"";
1594 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1595 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1596 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1597 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
1598 QTest::ignoreMessage(QtWarningMsg, warning6.toLatin1().constData());
1599 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1600 QVERIFY(object != 0);
1602 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
1603 emit object->basicSignal();
1605 QTest::ignoreMessage(QtWarningMsg, warning7.toLatin1().constData());
1606 emit object->anotherBasicSignal();
1608 QTest::ignoreMessage(QtWarningMsg, warning8.toLatin1().constData());
1609 emit object->thirdBasicSignal();
1615 Test file/lineNumbers for inline functions.
1617 void tst_qqmlecmascript::functionErrors()
1619 QQmlComponent component(&engine, testFileUrl("functionErrors.qml"));
1620 QString url = component.url().toString();
1622 QString warning = url + ":5: Error: Invalid write to global property \"a\"";
1624 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1626 QObject *object = component.create();
1627 QVERIFY(object != 0);
1630 // test that if an exception occurs while invoking js function from cpp, it is reported as expected.
1631 QQmlComponent componentTwo(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
1632 url = componentTwo.url().toString();
1633 object = componentTwo.create();
1634 QVERIFY(object != 0);
1636 QString srpname = object->property("srp_name").toString();
1638 warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srpname
1639 + QLatin1String(" is not a function");
1640 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed.
1641 QMetaObject::invokeMethod(object, "retrieveScarceResource");
1646 Test various errors that can occur when assigning a property from script
1648 void tst_qqmlecmascript::propertyAssignmentErrors()
1650 QQmlComponent component(&engine, testFileUrl("propertyAssignmentErrors.qml"));
1652 QString url = component.url().toString();
1654 QObject *object = component.create();
1655 QVERIFY(object != 0);
1657 QCOMPARE(object->property("test1").toBool(), true);
1658 QCOMPARE(object->property("test2").toBool(), true);
1664 Test bindings still work when the reeval is triggered from within
1667 void tst_qqmlecmascript::signalTriggeredBindings()
1669 QQmlComponent component(&engine, testFileUrl("signalTriggeredBindings.qml"));
1670 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1671 QVERIFY(object != 0);
1673 QCOMPARE(object->property("base").toReal(), 50.);
1674 QCOMPARE(object->property("test1").toReal(), 50.);
1675 QCOMPARE(object->property("test2").toReal(), 50.);
1677 object->basicSignal();
1679 QCOMPARE(object->property("base").toReal(), 200.);
1680 QCOMPARE(object->property("test1").toReal(), 200.);
1681 QCOMPARE(object->property("test2").toReal(), 200.);
1683 object->argumentSignal(10, QString(), 10, MyQmlObject::EnumValue4, Qt::RightButton);
1685 QCOMPARE(object->property("base").toReal(), 400.);
1686 QCOMPARE(object->property("test1").toReal(), 400.);
1687 QCOMPARE(object->property("test2").toReal(), 400.);
1693 Test that list properties can be iterated from ECMAScript
1695 void tst_qqmlecmascript::listProperties()
1697 QQmlComponent component(&engine, testFileUrl("listProperties.qml"));
1698 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1699 QVERIFY(object != 0);
1701 QCOMPARE(object->property("test1").toInt(), 21);
1702 QCOMPARE(object->property("test2").toInt(), 2);
1703 QCOMPARE(object->property("test3").toBool(), true);
1704 QCOMPARE(object->property("test4").toBool(), true);
1709 void tst_qqmlecmascript::exceptionClearsOnReeval()
1711 QQmlComponent component(&engine, testFileUrl("exceptionClearsOnReeval.qml"));
1712 QString url = component.url().toString();
1714 QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null";
1716 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1717 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1718 QVERIFY(object != 0);
1720 QCOMPARE(object->property("test").toBool(), false);
1722 MyQmlObject object2;
1723 MyQmlObject object3;
1724 object2.setObjectProperty(&object3);
1725 object->setObjectProperty(&object2);
1727 QCOMPARE(object->property("test").toBool(), true);
1732 void tst_qqmlecmascript::exceptionSlotProducesWarning()
1734 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning.qml"));
1735 QString url = component.url().toString();
1737 QString warning = component.url().toString() + ":6: Error: JS exception";
1739 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1740 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1741 QVERIFY(object != 0);
1745 void tst_qqmlecmascript::exceptionBindingProducesWarning()
1747 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning2.qml"));
1748 QString url = component.url().toString();
1750 QString warning = component.url().toString() + ":5: Error: JS exception";
1752 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1753 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1754 QVERIFY(object != 0);
1758 void tst_qqmlecmascript::compileInvalidBinding()
1760 // QTBUG-23387: ensure that invalid bindings don't cause a crash.
1761 QQmlComponent component(&engine, testFileUrl("v8bindingException.qml"));
1762 QObject *object = component.create();
1763 QVERIFY(object != 0);
1767 static int transientErrorsMsgCount = 0;
1768 static void transientErrorsMsgHandler(QtMsgType, const char *)
1770 ++transientErrorsMsgCount;
1773 // Check that transient binding errors are not displayed
1774 void tst_qqmlecmascript::transientErrors()
1777 QQmlComponent component(&engine, testFileUrl("transientErrors.qml"));
1779 transientErrorsMsgCount = 0;
1780 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1782 QObject *object = component.create();
1783 QVERIFY(object != 0);
1785 qInstallMsgHandler(old);
1787 QCOMPARE(transientErrorsMsgCount, 0);
1792 // One binding erroring multiple times, but then resolving
1794 QQmlComponent component(&engine, testFileUrl("transientErrors.2.qml"));
1796 transientErrorsMsgCount = 0;
1797 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1799 QObject *object = component.create();
1800 QVERIFY(object != 0);
1802 qInstallMsgHandler(old);
1804 QCOMPARE(transientErrorsMsgCount, 0);
1810 // Check that errors during shutdown are minimized
1811 void tst_qqmlecmascript::shutdownErrors()
1813 QQmlComponent component(&engine, testFileUrl("shutdownErrors.qml"));
1814 QObject *object = component.create();
1815 QVERIFY(object != 0);
1817 transientErrorsMsgCount = 0;
1818 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1822 qInstallMsgHandler(old);
1823 QCOMPARE(transientErrorsMsgCount, 0);
1826 void tst_qqmlecmascript::compositePropertyType()
1828 QQmlComponent component(&engine, testFileUrl("compositePropertyType.qml"));
1830 QTest::ignoreMessage(QtDebugMsg, "hello world");
1831 QObject *object = qobject_cast<QObject *>(component.create());
1836 void tst_qqmlecmascript::jsObject()
1838 QQmlComponent component(&engine, testFileUrl("jsObject.qml"));
1839 QObject *object = component.create();
1840 QVERIFY(object != 0);
1842 QCOMPARE(object->property("test").toInt(), 92);
1847 void tst_qqmlecmascript::undefinedResetsProperty()
1850 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.qml"));
1851 QObject *object = component.create();
1852 QVERIFY(object != 0);
1854 QCOMPARE(object->property("resettableProperty").toInt(), 92);
1856 object->setProperty("setUndefined", true);
1858 QCOMPARE(object->property("resettableProperty").toInt(), 13);
1860 object->setProperty("setUndefined", false);
1862 QCOMPARE(object->property("resettableProperty").toInt(), 92);
1867 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.2.qml"));
1868 QObject *object = component.create();
1869 QVERIFY(object != 0);
1871 QCOMPARE(object->property("resettableProperty").toInt(), 19);
1873 QMetaObject::invokeMethod(object, "doReset");
1875 QCOMPARE(object->property("resettableProperty").toInt(), 13);
1881 // Aliases to variant properties should work
1882 void tst_qqmlecmascript::qtbug_22464()
1884 QQmlComponent component(&engine, testFileUrl("qtbug_22464.qml"));
1885 QObject *object = component.create();
1886 QVERIFY(object != 0);
1888 QCOMPARE(object->property("test").toBool(), true);
1893 void tst_qqmlecmascript::qtbug_21580()
1895 QQmlComponent component(&engine, testFileUrl("qtbug_21580.qml"));
1897 QObject *object = component.create();
1898 QVERIFY(object != 0);
1900 QCOMPARE(object->property("test").toBool(), true);
1905 // Causes a v8 binding, but not all v8 bindings to be destroyed during evaluation
1906 void tst_qqmlecmascript::singleV8BindingDestroyedDuringEvaluation()
1908 QQmlComponent component(&engine, testFileUrl("singleV8BindingDestroyedDuringEvaluation.qml"));
1910 QObject *object = component.create();
1911 QVERIFY(object != 0);
1916 void tst_qqmlecmascript::bug1()
1918 QQmlComponent component(&engine, testFileUrl("bug.1.qml"));
1919 QObject *object = component.create();
1920 QVERIFY(object != 0);
1922 QCOMPARE(object->property("test").toInt(), 14);
1924 object->setProperty("a", 11);
1926 QCOMPARE(object->property("test").toInt(), 3);
1928 object->setProperty("b", true);
1930 QCOMPARE(object->property("test").toInt(), 9);
1935 void tst_qqmlecmascript::bug2()
1937 QQmlComponent component(&engine);
1938 component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl());
1940 QObject *object = component.create();
1941 QVERIFY(object != 0);
1946 // Don't crash in createObject when the component has errors.
1947 void tst_qqmlecmascript::dynamicCreationCrash()
1949 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
1950 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1951 QVERIFY(object != 0);
1953 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
1954 QMetaObject::invokeMethod(object, "dontCrash");
1955 QObject *created = object->objectProperty();
1956 QVERIFY(created == 0);
1961 // ownership transferred to JS, ensure that GC runs the dtor
1962 void tst_qqmlecmascript::dynamicCreationOwnership()
1965 int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
1967 // allow the engine to go out of scope too.
1969 QQmlEngine dcoEngine;
1970 QQmlComponent component(&dcoEngine, testFileUrl("dynamicCreationOwnership.qml"));
1971 QObject *object = component.create();
1972 QVERIFY(object != 0);
1973 MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo");
1974 QVERIFY(mdcdo != 0);
1975 mdcdo->setDtorCount(&dtorCount);
1977 for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
1978 QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
1980 // we do this once manually, but it should be done automatically
1981 // when the engine goes out of scope (since it should gc in dtor)
1982 QMetaObject::invokeMethod(object, "performGc");
1985 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1986 QCoreApplication::processEvents();
1992 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1993 QCoreApplication::processEvents();
1994 QCOMPARE(dtorCount, expectedDtorCount);
1997 void tst_qqmlecmascript::regExpBug()
2001 QQmlComponent component(&engine, testFileUrl("regExp.qml"));
2002 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2003 QVERIFY(object != 0);
2004 QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]"));
2010 QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString());
2011 QQmlComponent component(&engine, testFileUrl("regExp.2.qml"));
2012 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2013 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2015 QCOMPARE(component.errorString(), err);
2019 static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source)
2021 QString functionSource = QLatin1String("(function(object) { return ") +
2022 QLatin1String(source) + QLatin1String(" })");
2024 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2027 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2028 if (function.IsEmpty())
2030 v8::Handle<v8::Value> args[] = { o };
2031 function->Call(engine->global(), 1, args);
2032 return tc.HasCaught();
2035 static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o,
2036 const char *source, v8::Handle<v8::Value> result)
2038 QString functionSource = QLatin1String("(function(object) { return ") +
2039 QLatin1String(source) + QLatin1String(" })");
2041 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2044 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2045 if (function.IsEmpty())
2047 v8::Handle<v8::Value> args[] = { o };
2049 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2054 return value->StrictEquals(result);
2057 static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o,
2060 QString functionSource = QLatin1String("(function(object) { return ") +
2061 QLatin1String(source) + QLatin1String(" })");
2063 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2065 return v8::Handle<v8::Value>();
2066 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2067 if (function.IsEmpty())
2068 return v8::Handle<v8::Value>();
2069 v8::Handle<v8::Value> args[] = { o };
2071 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2074 return v8::Handle<v8::Value>();
2078 #define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
2079 #define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result)
2080 #define EVALUATE(source) evaluate(engine, object, source)
2082 void tst_qqmlecmascript::callQtInvokables()
2084 MyInvokableObject o;
2086 QQmlEngine qmlengine;
2087 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine);
2089 QV8Engine *engine = ep->v8engine();
2091 v8::HandleScope handle_scope;
2092 v8::Context::Scope scope(engine->context());
2094 v8::Local<v8::Object> object = engine->newQObject(&o)->ToObject();
2096 // Non-existent methods
2098 QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
2099 QCOMPARE(o.error(), false);
2100 QCOMPARE(o.invoked(), -1);
2101 QCOMPARE(o.actuals().count(), 0);
2104 QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)"));
2105 QCOMPARE(o.error(), false);
2106 QCOMPARE(o.invoked(), -1);
2107 QCOMPARE(o.actuals().count(), 0);
2109 // Insufficient arguments
2111 QVERIFY(EVALUATE_ERROR("object.method_int()"));
2112 QCOMPARE(o.error(), false);
2113 QCOMPARE(o.invoked(), -1);
2114 QCOMPARE(o.actuals().count(), 0);
2117 QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
2118 QCOMPARE(o.error(), false);
2119 QCOMPARE(o.invoked(), -1);
2120 QCOMPARE(o.actuals().count(), 0);
2122 // Excessive arguments
2124 QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", v8::Undefined()));
2125 QCOMPARE(o.error(), false);
2126 QCOMPARE(o.invoked(), 8);
2127 QCOMPARE(o.actuals().count(), 1);
2128 QCOMPARE(o.actuals().at(0), QVariant(10));
2131 QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", v8::Undefined()));
2132 QCOMPARE(o.error(), false);
2133 QCOMPARE(o.invoked(), 9);
2134 QCOMPARE(o.actuals().count(), 2);
2135 QCOMPARE(o.actuals().at(0), QVariant(10));
2136 QCOMPARE(o.actuals().at(1), QVariant(11));
2138 // Test return types
2140 QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", v8::Undefined()));
2141 QCOMPARE(o.error(), false);
2142 QCOMPARE(o.invoked(), 0);
2143 QCOMPARE(o.actuals().count(), 0);
2146 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", v8::Integer::New(6)));
2147 QCOMPARE(o.error(), false);
2148 QCOMPARE(o.invoked(), 1);
2149 QCOMPARE(o.actuals().count(), 0);
2152 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", v8::Number::New(19.75)));
2153 QCOMPARE(o.error(), false);
2154 QCOMPARE(o.invoked(), 2);
2155 QCOMPARE(o.actuals().count(), 0);
2159 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QPointF()");
2160 QVERIFY(!ret.IsEmpty());
2161 QCOMPARE(engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5)));
2162 QCOMPARE(o.error(), false);
2163 QCOMPARE(o.invoked(), 3);
2164 QCOMPARE(o.actuals().count(), 0);
2169 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QObject()");
2170 QCOMPARE(engine->toQObject(ret), (QObject *)&o);
2171 QCOMPARE(o.error(), false);
2172 QCOMPARE(o.invoked(), 4);
2173 QCOMPARE(o.actuals().count(), 0);
2177 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_unknown()", v8::Undefined()));
2178 QCOMPARE(o.error(), false);
2179 QCOMPARE(o.invoked(), 5);
2180 QCOMPARE(o.actuals().count(), 0);
2184 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QScriptValue()");
2185 QVERIFY(ret->IsString());
2186 QCOMPARE(engine->toString(ret), QString("Hello world"));
2187 QCOMPARE(o.error(), false);
2188 QCOMPARE(o.invoked(), 6);
2189 QCOMPARE(o.actuals().count(), 0);
2193 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks")));
2194 QCOMPARE(o.error(), false);
2195 QCOMPARE(o.invoked(), 7);
2196 QCOMPARE(o.actuals().count(), 0);
2200 QVERIFY(EVALUATE_VALUE("object.method_int(94)", v8::Undefined()));
2201 QCOMPARE(o.error(), false);
2202 QCOMPARE(o.invoked(), 8);
2203 QCOMPARE(o.actuals().count(), 1);
2204 QCOMPARE(o.actuals().at(0), QVariant(94));
2207 QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", v8::Undefined()));
2208 QCOMPARE(o.error(), false);
2209 QCOMPARE(o.invoked(), 8);
2210 QCOMPARE(o.actuals().count(), 1);
2211 QCOMPARE(o.actuals().at(0), QVariant(94));
2214 QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", v8::Undefined()));
2215 QCOMPARE(o.error(), false);
2216 QCOMPARE(o.invoked(), 8);
2217 QCOMPARE(o.actuals().count(), 1);
2218 QCOMPARE(o.actuals().at(0), QVariant(0));
2221 QVERIFY(EVALUATE_VALUE("object.method_int(null)", v8::Undefined()));
2222 QCOMPARE(o.error(), false);
2223 QCOMPARE(o.invoked(), 8);
2224 QCOMPARE(o.actuals().count(), 1);
2225 QCOMPARE(o.actuals().at(0), QVariant(0));
2228 QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", v8::Undefined()));
2229 QCOMPARE(o.error(), false);
2230 QCOMPARE(o.invoked(), 8);
2231 QCOMPARE(o.actuals().count(), 1);
2232 QCOMPARE(o.actuals().at(0), QVariant(0));
2235 QVERIFY(EVALUATE_VALUE("object.method_int(object)", v8::Undefined()));
2236 QCOMPARE(o.error(), false);
2237 QCOMPARE(o.invoked(), 8);
2238 QCOMPARE(o.actuals().count(), 1);
2239 QCOMPARE(o.actuals().at(0), QVariant(0));
2242 QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", v8::Undefined()));
2243 QCOMPARE(o.error(), false);
2244 QCOMPARE(o.invoked(), 9);
2245 QCOMPARE(o.actuals().count(), 2);
2246 QCOMPARE(o.actuals().at(0), QVariant(122));
2247 QCOMPARE(o.actuals().at(1), QVariant(9));
2250 QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", v8::Undefined()));
2251 QCOMPARE(o.error(), false);
2252 QCOMPARE(o.invoked(), 10);
2253 QCOMPARE(o.actuals().count(), 1);
2254 QCOMPARE(o.actuals().at(0), QVariant(94.3));
2257 QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", v8::Undefined()));
2258 QCOMPARE(o.error(), false);
2259 QCOMPARE(o.invoked(), 10);
2260 QCOMPARE(o.actuals().count(), 1);
2261 QCOMPARE(o.actuals().at(0), QVariant(94.3));
2264 QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", v8::Undefined()));
2265 QCOMPARE(o.error(), false);
2266 QCOMPARE(o.invoked(), 10);
2267 QCOMPARE(o.actuals().count(), 1);
2268 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2271 QVERIFY(EVALUATE_VALUE("object.method_real(null)", v8::Undefined()));
2272 QCOMPARE(o.error(), false);
2273 QCOMPARE(o.invoked(), 10);
2274 QCOMPARE(o.actuals().count(), 1);
2275 QCOMPARE(o.actuals().at(0), QVariant(0));
2278 QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", v8::Undefined()));
2279 QCOMPARE(o.error(), false);
2280 QCOMPARE(o.invoked(), 10);
2281 QCOMPARE(o.actuals().count(), 1);
2282 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2285 QVERIFY(EVALUATE_VALUE("object.method_real(object)", v8::Undefined()));
2286 QCOMPARE(o.error(), false);
2287 QCOMPARE(o.invoked(), 10);
2288 QCOMPARE(o.actuals().count(), 1);
2289 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2292 QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", v8::Undefined()));
2293 QCOMPARE(o.error(), false);
2294 QCOMPARE(o.invoked(), 11);
2295 QCOMPARE(o.actuals().count(), 1);
2296 QCOMPARE(o.actuals().at(0), QVariant("Hello world"));
2299 QVERIFY(EVALUATE_VALUE("object.method_QString(19)", v8::Undefined()));
2300 QCOMPARE(o.error(), false);
2301 QCOMPARE(o.invoked(), 11);
2302 QCOMPARE(o.actuals().count(), 1);
2303 QCOMPARE(o.actuals().at(0), QVariant("19"));
2307 QString expected = "MyInvokableObject(0x" + QString::number((quintptr)&o, 16) + ")";
2308 QVERIFY(EVALUATE_VALUE("object.method_QString(object)", v8::Undefined()));
2309 QCOMPARE(o.error(), false);
2310 QCOMPARE(o.invoked(), 11);
2311 QCOMPARE(o.actuals().count(), 1);
2312 QCOMPARE(o.actuals().at(0), QVariant(expected));
2316 QVERIFY(EVALUATE_VALUE("object.method_QString(null)", v8::Undefined()));
2317 QCOMPARE(o.error(), false);
2318 QCOMPARE(o.invoked(), 11);
2319 QCOMPARE(o.actuals().count(), 1);
2320 QCOMPARE(o.actuals().at(0), QVariant(QString()));
2323 QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", v8::Undefined()));
2324 QCOMPARE(o.error(), false);
2325 QCOMPARE(o.invoked(), 11);
2326 QCOMPARE(o.actuals().count(), 1);
2327 QCOMPARE(o.actuals().at(0), QVariant(QString()));
2330 QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", v8::Undefined()));
2331 QCOMPARE(o.error(), false);
2332 QCOMPARE(o.invoked(), 12);
2333 QCOMPARE(o.actuals().count(), 1);
2334 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2337 QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", v8::Undefined()));
2338 QCOMPARE(o.error(), false);
2339 QCOMPARE(o.invoked(), 12);
2340 QCOMPARE(o.actuals().count(), 1);
2341 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2344 QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", v8::Undefined()));
2345 QCOMPARE(o.error(), false);
2346 QCOMPARE(o.invoked(), 12);
2347 QCOMPARE(o.actuals().count(), 1);
2348 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2351 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", v8::Undefined()));
2352 QCOMPARE(o.error(), false);
2353 QCOMPARE(o.invoked(), 12);
2354 QCOMPARE(o.actuals().count(), 1);
2355 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2358 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", v8::Undefined()));
2359 QCOMPARE(o.error(), false);
2360 QCOMPARE(o.invoked(), 12);
2361 QCOMPARE(o.actuals().count(), 1);
2362 QCOMPARE(o.actuals().at(0), QVariant(QPointF(99.3, -10.2)));
2365 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", v8::Undefined()));
2366 QCOMPARE(o.error(), false);
2367 QCOMPARE(o.invoked(), 12);
2368 QCOMPARE(o.actuals().count(), 1);
2369 QCOMPARE(o.actuals().at(0), QVariant(QPointF(9, 12)));
2372 QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", v8::Undefined()));
2373 QCOMPARE(o.error(), false);
2374 QCOMPARE(o.invoked(), 13);
2375 QCOMPARE(o.actuals().count(), 1);
2376 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2379 QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", v8::Undefined()));
2380 QCOMPARE(o.error(), false);
2381 QCOMPARE(o.invoked(), 13);
2382 QCOMPARE(o.actuals().count(), 1);
2383 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2386 QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", v8::Undefined()));
2387 QCOMPARE(o.error(), false);
2388 QCOMPARE(o.invoked(), 13);
2389 QCOMPARE(o.actuals().count(), 1);
2390 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2393 QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", v8::Undefined()));
2394 QCOMPARE(o.error(), false);
2395 QCOMPARE(o.invoked(), 13);
2396 QCOMPARE(o.actuals().count(), 1);
2397 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2400 QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", v8::Undefined()));
2401 QCOMPARE(o.error(), false);
2402 QCOMPARE(o.invoked(), 13);
2403 QCOMPARE(o.actuals().count(), 1);
2404 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)&o));
2407 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined()));
2408 QCOMPARE(o.error(), false);
2409 QCOMPARE(o.invoked(), 14);
2410 QCOMPARE(o.actuals().count(), 1);
2411 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isNull());
2414 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined()));
2415 QCOMPARE(o.error(), false);
2416 QCOMPARE(o.invoked(), 14);
2417 QCOMPARE(o.actuals().count(), 1);
2418 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isUndefined());
2421 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined()));
2422 QCOMPARE(o.error(), false);
2423 QCOMPARE(o.invoked(), 14);
2424 QCOMPARE(o.actuals().count(), 1);
2425 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).strictlyEquals(QJSValue(19)));
2428 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined()));
2429 QCOMPARE(o.error(), false);
2430 QCOMPARE(o.invoked(), 14);
2431 QCOMPARE(o.actuals().count(), 1);
2432 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isArray());
2435 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined()));
2436 QCOMPARE(o.error(), false);
2437 QCOMPARE(o.invoked(), 15);
2438 QCOMPARE(o.actuals().count(), 2);
2439 QCOMPARE(o.actuals().at(0), QVariant(4));
2440 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isNull());
2443 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined()));
2444 QCOMPARE(o.error(), false);
2445 QCOMPARE(o.invoked(), 15);
2446 QCOMPARE(o.actuals().count(), 2);
2447 QCOMPARE(o.actuals().at(0), QVariant(8));
2448 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isUndefined());
2451 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined()));
2452 QCOMPARE(o.error(), false);
2453 QCOMPARE(o.invoked(), 15);
2454 QCOMPARE(o.actuals().count(), 2);
2455 QCOMPARE(o.actuals().at(0), QVariant(3));
2456 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).strictlyEquals(QJSValue(19)));
2459 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined()));
2460 QCOMPARE(o.error(), false);
2461 QCOMPARE(o.invoked(), 15);
2462 QCOMPARE(o.actuals().count(), 2);
2463 QCOMPARE(o.actuals().at(0), QVariant(44));
2464 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isArray());
2467 QVERIFY(EVALUATE_ERROR("object.method_overload()"));
2468 QCOMPARE(o.error(), false);
2469 QCOMPARE(o.invoked(), -1);
2470 QCOMPARE(o.actuals().count(), 0);
2473 QVERIFY(EVALUATE_VALUE("object.method_overload(10)", v8::Undefined()));
2474 QCOMPARE(o.error(), false);
2475 QCOMPARE(o.invoked(), 16);
2476 QCOMPARE(o.actuals().count(), 1);
2477 QCOMPARE(o.actuals().at(0), QVariant(10));
2480 QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", v8::Undefined()));
2481 QCOMPARE(o.error(), false);
2482 QCOMPARE(o.invoked(), 17);
2483 QCOMPARE(o.actuals().count(), 2);
2484 QCOMPARE(o.actuals().at(0), QVariant(10));
2485 QCOMPARE(o.actuals().at(1), QVariant(11));
2488 QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", v8::Undefined()));
2489 QCOMPARE(o.error(), false);
2490 QCOMPARE(o.invoked(), 18);
2491 QCOMPARE(o.actuals().count(), 1);
2492 QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2495 QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", v8::Undefined()));
2496 QCOMPARE(o.error(), false);
2497 QCOMPARE(o.invoked(), 19);
2498 QCOMPARE(o.actuals().count(), 1);
2499 QCOMPARE(o.actuals().at(0), QVariant(9));
2502 QVERIFY(EVALUATE_VALUE("object.method_default(10)", v8::Integer::New(19)));
2503 QCOMPARE(o.error(), false);
2504 QCOMPARE(o.invoked(), 20);
2505 QCOMPARE(o.actuals().count(), 2);
2506 QCOMPARE(o.actuals().at(0), QVariant(10));
2507 QCOMPARE(o.actuals().at(1), QVariant(19));
2510 QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", v8::Integer::New(13)));
2511 QCOMPARE(o.error(), false);
2512 QCOMPARE(o.invoked(), 20);
2513 QCOMPARE(o.actuals().count(), 2);
2514 QCOMPARE(o.actuals().at(0), QVariant(10));
2515 QCOMPARE(o.actuals().at(1), QVariant(13));
2518 QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", v8::Undefined()));
2519 QCOMPARE(o.error(), false);
2520 QCOMPARE(o.invoked(), -3);
2521 QCOMPARE(o.actuals().count(), 1);
2522 QCOMPARE(o.actuals().at(0), QVariant(9));
2525 QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", v8::Undefined()));
2526 QCOMPARE(o.error(), false);
2527 QCOMPARE(o.invoked(), 21);
2528 QCOMPARE(o.actuals().count(), 2);
2529 QCOMPARE(o.actuals().at(0), QVariant(9));
2530 QCOMPARE(o.actuals().at(1), QVariant());
2533 QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", v8::Undefined()));
2534 QCOMPARE(o.error(), false);
2535 QCOMPARE(o.invoked(), 21);
2536 QCOMPARE(o.actuals().count(), 2);
2537 QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2538 QCOMPARE(o.actuals().at(1), QVariant(QString("World")));
2541 // QTBUG-13047 (check that you can pass registered object types as args)
2542 void tst_qqmlecmascript::invokableObjectArg()
2544 QQmlComponent component(&engine, testFileUrl("invokableObjectArg.qml"));
2546 QObject *o = component.create();
2548 MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o);
2550 QCOMPARE(qmlobject->myinvokableObject, qmlobject);
2555 // QTBUG-13047 (check that you can return registered object types from methods)
2556 void tst_qqmlecmascript::invokableObjectRet()
2558 QQmlComponent component(&engine, testFileUrl("invokableObjectRet.qml"));
2560 QObject *o = component.create();
2562 QCOMPARE(o->property("test").toBool(), true);
2567 void tst_qqmlecmascript::listToVariant()
2569 QQmlComponent component(&engine, testFileUrl("listToVariant.qml"));
2571 MyQmlContainer container;
2573 QQmlContext context(engine.rootContext());
2574 context.setContextObject(&container);
2576 QObject *object = component.create(&context);
2577 QVERIFY(object != 0);
2579 QVariant v = object->property("test");
2580 QCOMPARE(v.userType(), qMetaTypeId<QQmlListReference>());
2581 QVERIFY(qvariant_cast<QQmlListReference>(v).object() == &container);
2587 Q_DECLARE_METATYPE(QQmlListProperty<MyQmlObject>)
2588 void tst_qqmlecmascript::listAssignment()
2590 QQmlComponent component(&engine, testFileUrl("listAssignment.qml"));
2591 QObject *obj = component.create();
2592 QCOMPARE(obj->property("list1length").toInt(), 2);
2593 QQmlListProperty<MyQmlObject> list1 = obj->property("list1").value<QQmlListProperty<MyQmlObject> >();
2594 QQmlListProperty<MyQmlObject> list2 = obj->property("list2").value<QQmlListProperty<MyQmlObject> >();
2595 QCOMPARE(list1.count(&list1), list2.count(&list2));
2596 QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0));
2597 QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1));
2602 void tst_qqmlecmascript::multiEngineObject()
2605 obj.setStringProperty("Howdy planet");
2608 e1.rootContext()->setContextProperty("thing", &obj);
2609 QQmlComponent c1(&e1, testFileUrl("multiEngineObject.qml"));
2612 e2.rootContext()->setContextProperty("thing", &obj);
2613 QQmlComponent c2(&e2, testFileUrl("multiEngineObject.qml"));
2615 QObject *o1 = c1.create();
2616 QObject *o2 = c2.create();
2618 QCOMPARE(o1->property("test").toString(), QString("Howdy planet"));
2619 QCOMPARE(o2->property("test").toString(), QString("Howdy planet"));
2625 // Test that references to QObjects are cleanup when the object is destroyed
2626 void tst_qqmlecmascript::deletedObject()
2628 QQmlComponent component(&engine, testFileUrl("deletedObject.qml"));
2630 QObject *object = component.create();
2632 QCOMPARE(object->property("test1").toBool(), true);
2633 QCOMPARE(object->property("test2").toBool(), true);
2634 QCOMPARE(object->property("test3").toBool(), true);
2635 QCOMPARE(object->property("test4").toBool(), true);
2640 void tst_qqmlecmascript::attachedPropertyScope()
2642 QQmlComponent component(&engine, testFileUrl("attachedPropertyScope.qml"));
2644 QObject *object = component.create();
2645 QVERIFY(object != 0);
2647 MyQmlAttachedObject *attached =
2648 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
2649 QVERIFY(attached != 0);
2651 QCOMPARE(object->property("value2").toInt(), 0);
2653 attached->emitMySignal();
2655 QCOMPARE(object->property("value2").toInt(), 9);
2660 void tst_qqmlecmascript::scriptConnect()
2663 QQmlComponent component(&engine, testFileUrl("scriptConnect.1.qml"));
2665 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2666 QVERIFY(object != 0);
2668 QCOMPARE(object->property("test").toBool(), false);
2669 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2670 QCOMPARE(object->property("test").toBool(), true);
2676 QQmlComponent component(&engine, testFileUrl("scriptConnect.2.qml"));
2678 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2679 QVERIFY(object != 0);
2681 QCOMPARE(object->property("test").toBool(), false);
2682 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2683 QCOMPARE(object->property("test").toBool(), true);
2689 QQmlComponent component(&engine, testFileUrl("scriptConnect.3.qml"));
2691 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2692 QVERIFY(object != 0);
2694 QCOMPARE(object->property("test").toBool(), false);
2695 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2696 QCOMPARE(object->property("test").toBool(), true);
2702 QQmlComponent component(&engine, testFileUrl("scriptConnect.4.qml"));
2704 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2705 QVERIFY(object != 0);
2707 QCOMPARE(object->methodCalled(), false);
2708 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2709 QCOMPARE(object->methodCalled(), true);
2715 QQmlComponent component(&engine, testFileUrl("scriptConnect.5.qml"));
2717 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2718 QVERIFY(object != 0);
2720 QCOMPARE(object->methodCalled(), false);
2721 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2722 QCOMPARE(object->methodCalled(), true);
2728 QQmlComponent component(&engine, testFileUrl("scriptConnect.6.qml"));
2730 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2731 QVERIFY(object != 0);
2733 QCOMPARE(object->property("test").toInt(), 0);
2734 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2735 QCOMPARE(object->property("test").toInt(), 2);
2741 void tst_qqmlecmascript::scriptDisconnect()
2744 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.1.qml"));
2746 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2747 QVERIFY(object != 0);
2749 QCOMPARE(object->property("test").toInt(), 0);
2750 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2751 QCOMPARE(object->property("test").toInt(), 1);
2752 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2753 QCOMPARE(object->property("test").toInt(), 2);
2754 emit object->basicSignal();
2755 QCOMPARE(object->property("test").toInt(), 2);
2756 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2757 QCOMPARE(object->property("test").toInt(), 2);
2763 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.2.qml"));
2765 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2766 QVERIFY(object != 0);
2768 QCOMPARE(object->property("test").toInt(), 0);
2769 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2770 QCOMPARE(object->property("test").toInt(), 1);
2771 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2772 QCOMPARE(object->property("test").toInt(), 2);
2773 emit object->basicSignal();
2774 QCOMPARE(object->property("test").toInt(), 2);
2775 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2776 QCOMPARE(object->property("test").toInt(), 2);
2782 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.3.qml"));
2784 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2785 QVERIFY(object != 0);
2787 QCOMPARE(object->property("test").toInt(), 0);
2788 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2789 QCOMPARE(object->property("test").toInt(), 1);
2790 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2791 QCOMPARE(object->property("test").toInt(), 2);
2792 emit object->basicSignal();
2793 QCOMPARE(object->property("test").toInt(), 2);
2794 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2795 QCOMPARE(object->property("test").toInt(), 3);
2800 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.4.qml"));
2802 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2803 QVERIFY(object != 0);
2805 QCOMPARE(object->property("test").toInt(), 0);
2806 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2807 QCOMPARE(object->property("test").toInt(), 1);
2808 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2809 QCOMPARE(object->property("test").toInt(), 2);
2810 emit object->basicSignal();
2811 QCOMPARE(object->property("test").toInt(), 2);
2812 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2813 QCOMPARE(object->property("test").toInt(), 3);
2819 class OwnershipObject : public QObject
2823 OwnershipObject() { object = new QObject; }
2825 QPointer<QObject> object;
2828 QObject *getObject() { return object; }
2831 void tst_qqmlecmascript::ownership()
2833 OwnershipObject own;
2834 QQmlContext *context = new QQmlContext(engine.rootContext());
2835 context->setContextObject(&own);
2838 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
2840 QVERIFY(own.object != 0);
2842 QObject *object = component.create(context);
2844 engine.collectGarbage();
2846 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2847 QCoreApplication::processEvents();
2849 QVERIFY(own.object == 0);
2854 own.object = new QObject(&own);
2857 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
2859 QVERIFY(own.object != 0);
2861 QObject *object = component.create(context);
2863 engine.collectGarbage();
2865 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2866 QCoreApplication::processEvents();
2868 QVERIFY(own.object != 0);
2876 class CppOwnershipReturnValue : public QObject
2880 CppOwnershipReturnValue() : value(0) {}
2881 ~CppOwnershipReturnValue() { delete value; }
2883 Q_INVOKABLE QObject *create() {
2884 value = new QObject;
2885 QQmlEngine::setObjectOwnership(value, QQmlEngine::CppOwnership);
2889 Q_INVOKABLE MyQmlObject *createQmlObject() {
2890 MyQmlObject *rv = new MyQmlObject;
2895 QPointer<QObject> value;
2899 // Test setObjectOwnership(CppOwnership) works even when there is no QQmlData
2900 void tst_qqmlecmascript::cppOwnershipReturnValue()
2902 CppOwnershipReturnValue source;
2906 engine.rootContext()->setContextProperty("source", &source);
2908 QVERIFY(source.value == 0);
2910 QQmlComponent component(&engine);
2911 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl());
2913 QObject *object = component.create();
2915 QVERIFY(object != 0);
2916 QVERIFY(source.value != 0);
2921 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2922 QCoreApplication::processEvents();
2924 QVERIFY(source.value != 0);
2928 void tst_qqmlecmascript::ownershipCustomReturnValue()
2930 CppOwnershipReturnValue source;
2934 engine.rootContext()->setContextProperty("source", &source);
2936 QVERIFY(source.value == 0);
2938 QQmlComponent component(&engine);
2939 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl());
2941 QObject *object = component.create();
2943 QVERIFY(object != 0);
2944 QVERIFY(source.value != 0);
2949 engine.collectGarbage();
2950 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2951 QCoreApplication::processEvents();
2953 QVERIFY(source.value == 0);
2956 //the return value from getObject will be JS ownership,
2957 //unless strong Cpp ownership has been set
2958 class OwnershipChangingObject : public QObject
2962 OwnershipChangingObject(): object(0) { }
2964 QPointer<QObject> object;
2967 QObject *getObject() { return object; }
2968 void setObject(QObject *obj) { object = obj; }
2971 void tst_qqmlecmascript::ownershipRootObject()
2973 OwnershipChangingObject own;
2974 QQmlContext *context = new QQmlContext(engine.rootContext());
2975 context->setContextObject(&own);
2977 QQmlComponent component(&engine, testFileUrl("ownershipRootObject.qml"));
2978 QQmlGuard<QObject> object = component.create(context);
2981 engine.collectGarbage();
2983 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2984 QCoreApplication::processEvents();
2986 QVERIFY(own.object != 0);
2992 void tst_qqmlecmascript::ownershipConsistency()
2994 OwnershipChangingObject own;
2995 QQmlContext *context = new QQmlContext(engine.rootContext());
2996 context->setContextObject(&own);
2998 QString expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":19: Error: Invalid attempt to destroy() an indestructible object");
2999 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3000 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":15: Error: Invalid attempt to destroy() an indestructible object");
3001 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3002 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":6: Error: Invalid attempt to destroy() an indestructible object");
3003 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3004 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":10: Error: Invalid attempt to destroy() an indestructible object");
3005 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3007 QQmlComponent component(&engine, testFileUrl("ownershipConsistency.qml"));
3008 QQmlGuard<QObject> object = component.create(context);
3011 engine.collectGarbage();
3013 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3014 QCoreApplication::processEvents();
3016 QVERIFY(own.object != 0);
3022 void tst_qqmlecmascript::ownershipQmlIncubated()
3024 QQmlComponent component(&engine, testFileUrl("ownershipQmlIncubated.qml"));
3025 QObject *object = component.create();
3028 QTRY_VERIFY(object->property("incubatedItem").value<QObject*>() != 0);
3030 QMetaObject::invokeMethod(object, "deleteIncubatedItem");
3032 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3033 QCoreApplication::processEvents();
3035 QVERIFY(object->property("incubatedItem").value<QObject*>() == 0);
3040 class QListQObjectMethodsObject : public QObject
3044 QListQObjectMethodsObject() {
3045 m_objects.append(new MyQmlObject());
3046 m_objects.append(new MyQmlObject());
3049 ~QListQObjectMethodsObject() {
3050 qDeleteAll(m_objects);
3054 QList<QObject *> getObjects() { return m_objects; }
3057 QList<QObject *> m_objects;
3060 // Tests that returning a QList<QObject*> from a method works
3061 void tst_qqmlecmascript::qlistqobjectMethods()
3063 QListQObjectMethodsObject obj;
3064 QQmlContext *context = new QQmlContext(engine.rootContext());
3065 context->setContextObject(&obj);
3067 QQmlComponent component(&engine, testFileUrl("qlistqobjectMethods.qml"));
3069 QObject *object = component.create(context);
3071 QCOMPARE(object->property("test").toInt(), 2);
3072 QCOMPARE(object->property("test2").toBool(), true);
3079 void tst_qqmlecmascript::strictlyEquals()
3081 QQmlComponent component(&engine, testFileUrl("strictlyEquals.qml"));
3083 QObject *object = component.create();
3084 QVERIFY(object != 0);
3086 QCOMPARE(object->property("test1").toBool(), true);
3087 QCOMPARE(object->property("test2").toBool(), true);
3088 QCOMPARE(object->property("test3").toBool(), true);
3089 QCOMPARE(object->property("test4").toBool(), true);
3090 QCOMPARE(object->property("test5").toBool(), true);
3091 QCOMPARE(object->property("test6").toBool(), true);
3092 QCOMPARE(object->property("test7").toBool(), true);
3093 QCOMPARE(object->property("test8").toBool(), true);
3098 void tst_qqmlecmascript::compiled()
3100 QQmlComponent component(&engine, testFileUrl("compiled.qml"));
3102 QObject *object = component.create();
3103 QVERIFY(object != 0);
3105 QCOMPARE(object->property("test1").toReal(), qreal(15.7));
3106 QCOMPARE(object->property("test2").toReal(), qreal(-6.7));
3107 QCOMPARE(object->property("test3").toBool(), true);
3108 QCOMPARE(object->property("test4").toBool(), false);
3109 QCOMPARE(object->property("test5").toBool(), false);
3110 QCOMPARE(object->property("test6").toBool(), true);
3112 QCOMPARE(object->property("test7").toInt(), 185);
3113 QCOMPARE(object->property("test8").toInt(), 167);
3114 QCOMPARE(object->property("test9").toBool(), true);
3115 QCOMPARE(object->property("test10").toBool(), false);
3116 QCOMPARE(object->property("test11").toBool(), false);
3117 QCOMPARE(object->property("test12").toBool(), true);
3119 QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld"));
3120 QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World"));
3121 QCOMPARE(object->property("test15").toBool(), false);
3122 QCOMPARE(object->property("test16").toBool(), true);
3124 QCOMPARE(object->property("test17").toInt(), 5);
3125 QCOMPARE(object->property("test18").toReal(), qreal(176));
3126 QCOMPARE(object->property("test19").toInt(), 7);
3127 QCOMPARE(object->property("test20").toReal(), qreal(6.7));
3128 QCOMPARE(object->property("test21").toString(), QLatin1String("6.7"));
3129 QCOMPARE(object->property("test22").toString(), QLatin1String("!"));
3130 QCOMPARE(object->property("test23").toBool(), true);
3131 QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33));
3132 QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA));
3137 // Test that numbers assigned in bindings as strings work consistently
3138 void tst_qqmlecmascript::numberAssignment()
3140 QQmlComponent component(&engine, testFileUrl("numberAssignment.qml"));
3142 QObject *object = component.create();
3143 QVERIFY(object != 0);
3145 QCOMPARE(object->property("test1"), QVariant((qreal)6.7));
3146 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3147 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3148 QCOMPARE(object->property("test3"), QVariant((qreal)6));
3149 QCOMPARE(object->property("test4"), QVariant((qreal)6));
3151 QCOMPARE(object->property("test5"), QVariant((int)7));
3152 QCOMPARE(object->property("test6"), QVariant((int)7));
3153 QCOMPARE(object->property("test7"), QVariant((int)6));
3154 QCOMPARE(object->property("test8"), QVariant((int)6));
3156 QCOMPARE(object->property("test9"), QVariant((unsigned int)7));
3157 QCOMPARE(object->property("test10"), QVariant((unsigned int)7));
3158 QCOMPARE(object->property("test11"), QVariant((unsigned int)6));
3159 QCOMPARE(object->property("test12"), QVariant((unsigned int)6));
3164 void tst_qqmlecmascript::propertySplicing()
3166 QQmlComponent component(&engine, testFileUrl("propertySplicing.qml"));
3168 QObject *object = component.create();
3169 QVERIFY(object != 0);
3171 QCOMPARE(object->property("test").toBool(), true);
3177 void tst_qqmlecmascript::signalWithUnknownTypes()
3179 QQmlComponent component(&engine, testFileUrl("signalWithUnknownTypes.qml"));
3181 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3182 QVERIFY(object != 0);
3184 MyQmlObject::MyType type;
3185 type.value = 0x8971123;
3186 emit object->signalWithUnknownType(type);
3188 MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant());
3190 QCOMPARE(result.value, type.value);
3196 void tst_qqmlecmascript::signalWithJSValueInVariant_data()
3198 QTest::addColumn<QString>("expression");
3199 QTest::addColumn<QString>("compare");
3201 QString compareStrict("(function(a, b) { return a === b; })");
3202 QTest::newRow("true") << "true" << compareStrict;
3203 QTest::newRow("undefined") << "undefined" << compareStrict;
3204 QTest::newRow("null") << "null" << compareStrict;
3205 QTest::newRow("123") << "123" << compareStrict;
3206 QTest::newRow("'ciao'") << "'ciao'" << compareStrict;
3208 QString comparePropertiesStrict(
3210 " if (typeof b != 'object')"
3212 " var props = Object.getOwnPropertyNames(b);"
3213 " for (var i = 0; i < props.length; ++i) {"
3214 " var p = props[i];"
3215 " return arguments.callee(a[p], b[p]);"
3218 QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })" << comparePropertiesStrict;
3219 QTest::newRow("[10,20,30]") << "[10,20,30]" << comparePropertiesStrict;
3222 void tst_qqmlecmascript::signalWithJSValueInVariant()
3224 QFETCH(QString, expression);
3225 QFETCH(QString, compare);
3227 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3228 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3229 QVERIFY(object != 0);
3231 QJSValue value = engine.evaluate(expression);
3232 QVERIFY(!engine.hasUncaughtException());
3233 object->setProperty("expression", expression);
3234 object->setProperty("compare", compare);
3235 object->setProperty("pass", false);
3237 emit object->signalWithVariant(QVariant::fromValue(value));
3238 QVERIFY(object->property("pass").toBool());
3241 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines_data()
3243 signalWithJSValueInVariant_data();
3246 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines()
3248 QFETCH(QString, expression);
3249 QFETCH(QString, compare);
3251 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3252 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3253 QVERIFY(object != 0);
3256 QJSValue value = engine2.evaluate(expression);
3257 QVERIFY(!engine2.hasUncaughtException());
3258 object->setProperty("expression", expression);
3259 object->setProperty("compare", compare);
3260 object->setProperty("pass", false);
3262 QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine.");
3263 emit object->signalWithVariant(QVariant::fromValue(value));
3264 QVERIFY(!object->property("pass").toBool());
3267 void tst_qqmlecmascript::signalWithQJSValue_data()
3269 signalWithJSValueInVariant_data();
3272 void tst_qqmlecmascript::signalWithQJSValue()
3274 QFETCH(QString, expression);
3275 QFETCH(QString, compare);
3277 QQmlComponent component(&engine, testFileUrl("signalWithQJSValue.qml"));
3278 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3279 QVERIFY(object != 0);
3281 QJSValue value = engine.evaluate(expression);
3282 QVERIFY(!engine.hasUncaughtException());
3283 object->setProperty("expression", expression);
3284 object->setProperty("compare", compare);
3285 object->setProperty("pass", false);
3287 emit object->signalWithQJSValue(value);
3289 QVERIFY(object->property("pass").toBool());
3290 QVERIFY(object->qjsvalue().strictlyEquals(value));
3293 void tst_qqmlecmascript::moduleApi_data()
3295 QTest::addColumn<QUrl>("testfile");
3296 QTest::addColumn<QString>("errorMessage");
3297 QTest::addColumn<QStringList>("warningMessages");
3298 QTest::addColumn<QStringList>("readProperties");
3299 QTest::addColumn<QVariantList>("readExpectedValues");
3300 QTest::addColumn<QStringList>("writeProperties");
3301 QTest::addColumn<QVariantList>("writeValues");
3302 QTest::addColumn<QStringList>("readBackProperties");
3303 QTest::addColumn<QVariantList>("readBackExpectedValues");
3305 QTest::newRow("qobject, register + read + method")
3306 << testFileUrl("moduleapi/qobjectModuleApi.qml")
3309 << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
3310 << "qobjectMinorVersionTest" << "qobjectMajorVersionTest" << "qobjectParentedTest")
3311 << (QVariantList() << 20 << 20 << 1 << 20 << 20 << 26)
3317 QTest::newRow("script, register + read")
3318 << testFileUrl("moduleapi/scriptModuleApi.qml")
3321 << (QStringList() << "scriptTest")
3322 << (QVariantList() << 13)
3328 QTest::newRow("qobject, caching + read")
3329 << testFileUrl("moduleapi/qobjectModuleApiCaching.qml")
3332 << (QStringList() << "existingUriTest" << "qobjectParentedTest")
3333 << (QVariantList() << 20 << 26) // 26, shouldn't have incremented to 27.
3339 QTest::newRow("script, caching + read")
3340 << testFileUrl("moduleapi/scriptModuleApiCaching.qml")
3343 << (QStringList() << "scriptTest")
3344 << (QVariantList() << 13) // 13, shouldn't have incremented to 14.
3350 QTest::newRow("qobject, writing + readonly constraints")
3351 << testFileUrl("moduleapi/qobjectModuleApiWriting.qml")
3353 << (QStringList() << QString(testFileUrl("moduleapi/qobjectModuleApiWriting.qml").toString() + QLatin1String(":15: Error: Cannot assign to read-only property \"qobjectTestProperty\"")))
3354 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3355 << (QVariantList() << 20 << 50 << 10)
3356 << (QStringList() << "firstProperty" << "secondProperty")
3357 << (QVariantList() << 30 << 30)
3358 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3359 << (QVariantList() << 20 << 30 << 30);
3361 QTest::newRow("script, writing + readonly constraints")
3362 << testFileUrl("moduleapi/scriptModuleApiWriting.qml")
3364 << (QStringList() << QString(testFileUrl("moduleapi/scriptModuleApiWriting.qml").toString() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\"")))
3365 << (QStringList() << "readBack" << "unchanged")
3366 << (QVariantList() << 13 << 42)
3367 << (QStringList() << "firstProperty" << "secondProperty")
3368 << (QVariantList() << 30 << 30)
3369 << (QStringList() << "readBack" << "unchanged")
3370 << (QVariantList() << 30 << 42);
3372 QTest::newRow("qobject module API enum values in JS")
3373 << testFileUrl("moduleapi/qobjectModuleApiEnums.qml")
3376 << (QStringList() << "enumValue" << "enumMethod")
3377 << (QVariantList() << 42 << 30)
3383 QTest::newRow("qobject, invalid major version fail")
3384 << testFileUrl("moduleapi/moduleApiMajorVersionFail.qml")
3385 << QString("QQmlComponent: Component is not ready")
3394 QTest::newRow("qobject, invalid minor version fail")
3395 << testFileUrl("moduleapi/moduleApiMinorVersionFail.qml")
3396 << QString("QQmlComponent: Component is not ready")
3405 QTest::newRow("legacy module api registration")
3406 << testFileUrl("moduleapi/qobjectModuleApiLegacy.qml")
3408 << QStringList() // warning doesn't occur in the test, but in registerTypes()
3409 << (QStringList() << "legacyModulePropertyTest" << "legacyModuleMethodTest")
3410 << (QVariantList() << 20 << 2)
3417 void tst_qqmlecmascript::moduleApi()
3419 QFETCH(QUrl, testfile);
3420 QFETCH(QString, errorMessage);
3421 QFETCH(QStringList, warningMessages);
3422 QFETCH(QStringList, readProperties);
3423 QFETCH(QVariantList, readExpectedValues);
3424 QFETCH(QStringList, writeProperties);
3425 QFETCH(QVariantList, writeValues);
3426 QFETCH(QStringList, readBackProperties);
3427 QFETCH(QVariantList, readBackExpectedValues);
3429 QQmlComponent component(&engine, testfile);
3431 if (!errorMessage.isEmpty())
3432 QTest::ignoreMessage(QtWarningMsg, errorMessage.toAscii().constData());
3434 if (warningMessages.size())
3435 foreach (const QString &warning, warningMessages)
3436 QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
3438 QObject *object = component.create();
3439 if (!errorMessage.isEmpty()) {
3440 QVERIFY(object == 0);
3442 QVERIFY(object != 0);
3443 for (int i = 0; i < readProperties.size(); ++i)
3444 QCOMPARE(object->property(readProperties.at(i).toAscii().constData()), readExpectedValues.at(i));
3445 for (int i = 0; i < writeProperties.size(); ++i)
3446 QVERIFY(object->setProperty(writeProperties.at(i).toAscii().constData(), writeValues.at(i)));
3447 for (int i = 0; i < readBackProperties.size(); ++i)
3448 QCOMPARE(object->property(readBackProperties.at(i).toAscii().constData()), readBackExpectedValues.at(i));
3453 void tst_qqmlecmascript::importScripts_data()
3455 QTest::addColumn<QUrl>("testfile");
3456 QTest::addColumn<QString>("errorMessage");
3457 QTest::addColumn<QStringList>("warningMessages");
3458 QTest::addColumn<QStringList>("propertyNames");
3459 QTest::addColumn<QVariantList>("propertyValues");
3461 QTest::newRow("basic functionality")
3462 << testFileUrl("jsimport/testImport.qml")
3465 << (QStringList() << QLatin1String("importedScriptStringValue")
3466 << QLatin1String("importedScriptFunctionValue")
3467 << QLatin1String("importedModuleAttachedPropertyValue")
3468 << QLatin1String("importedModuleEnumValue"))
3469 << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3474 QTest::newRow("import scoping")
3475 << testFileUrl("jsimport/testImportScoping.qml")
3478 << (QStringList() << QLatin1String("componentError"))
3479 << (QVariantList() << QVariant(5));
3481 QTest::newRow("parent scope shouldn't be inherited by import with imports")
3482 << testFileUrl("jsimportfail/failOne.qml")
3484 << (QStringList() << QString(testFileUrl("jsimportfail/failOne.qml").toString() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
3485 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3486 << (QVariantList() << QVariant(QString()));
3488 QTest::newRow("javascript imports in an import should be private to the import scope")
3489 << testFileUrl("jsimportfail/failTwo.qml")
3491 << (QStringList() << QString(testFileUrl("jsimportfail/failTwo.qml").toString() + QLatin1String(":6: ReferenceError: ImportOneJs is not defined")))
3492 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3493 << (QVariantList() << QVariant(QString()));
3495 QTest::newRow("module imports in an import should be private to the import scope")
3496 << testFileUrl("jsimportfail/failThree.qml")
3498 << (QStringList() << QString(testFileUrl("jsimportfail/failThree.qml").toString() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
3499 << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
3500 << (QVariantList() << QVariant(false));
3502 QTest::newRow("typenames in an import should be private to the import scope")
3503 << testFileUrl("jsimportfail/failFour.qml")
3505 << (QStringList() << QString(testFileUrl("jsimportfail/failFour.qml").toString() + QLatin1String(":6: ReferenceError: JsQtTest is not defined")))
3506 << (QStringList() << QLatin1String("importedModuleEnumValue"))
3507 << (QVariantList() << QVariant(0));
3509 QTest::newRow("import with imports has it's own activation scope")
3510 << testFileUrl("jsimportfail/failFive.qml")
3512 << (QStringList() << QString(testFileUrl("jsimportfail/importWithImports.js").toString() + QLatin1String(":8: ReferenceError: Component is not defined")))
3513 << (QStringList() << QLatin1String("componentError"))
3514 << (QVariantList() << QVariant(0));
3516 QTest::newRow("import pragma library script")
3517 << testFileUrl("jsimport/testImportPragmaLibrary.qml")
3520 << (QStringList() << QLatin1String("testValue"))
3521 << (QVariantList() << QVariant(31));
3523 QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3524 << testFileUrl("jsimportfail/testImportPragmaLibrary.qml")
3526 << (QStringList() << QString(testFileUrl("jsimportfail/importPragmaLibrary.js").toString() + QLatin1String(":6: ReferenceError: Component is not defined")))
3527 << (QStringList() << QLatin1String("testValue"))
3528 << (QVariantList() << QVariant(0));
3530 QTest::newRow("import pragma library script which has an import")
3531 << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
3534 << (QStringList() << QLatin1String("testValue"))
3535 << (QVariantList() << QVariant(55));
3537 QTest::newRow("import pragma library script which has a pragma library import")
3538 << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3541 << (QStringList() << QLatin1String("testValue"))
3542 << (QVariantList() << QVariant(18));
3544 QTest::newRow("import module api into js import")
3545 << testFileUrl("jsimport/testImportModuleApi.qml")
3548 << (QStringList() << QLatin1String("testValue"))
3549 << (QVariantList() << QVariant(20));
3551 QTest::newRow("import module which exports a script")
3552 << testFileUrl("jsimport/testJsImport.qml")
3555 << (QStringList() << QLatin1String("importedScriptStringValue")
3556 << QLatin1String("renamedScriptStringValue")
3557 << QLatin1String("reimportedScriptStringValue"))
3558 << (QVariantList() << QVariant(QString("Hello"))
3559 << QVariant(QString("Hello"))
3560 << QVariant(QString("Hello")));
3563 void tst_qqmlecmascript::importScripts()
3565 QFETCH(QUrl, testfile);
3566 QFETCH(QString, errorMessage);
3567 QFETCH(QStringList, warningMessages);
3568 QFETCH(QStringList, propertyNames);
3569 QFETCH(QVariantList, propertyValues);
3571 QQmlComponent component(&engine, testfile);
3573 if (!errorMessage.isEmpty())
3574 QTest::ignoreMessage(QtWarningMsg, errorMessage.toAscii().constData());
3576 if (warningMessages.size())
3577 foreach (const QString &warning, warningMessages)
3578 QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
3580 QObject *object = component.create();
3581 if (!errorMessage.isEmpty()) {
3582 QVERIFY(object == 0);
3584 QVERIFY(object != 0);
3585 for (int i = 0; i < propertyNames.size(); ++i)
3586 QCOMPARE(object->property(propertyNames.at(i).toAscii().constData()), propertyValues.at(i));
3591 void tst_qqmlecmascript::scarceResources_other()
3593 /* These tests require knowledge of state, since we test values after
3594 performing signal or function invocation. */
3596 QPixmap origPixmap(100, 100);
3597 origPixmap.fill(Qt::blue);
3598 QString srp_name, expectedWarning;
3599 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
3600 ScarceResourceObject *eo = 0;
3602 QObject *object = 0;
3604 /* property var semantics */
3606 // test that scarce resources are handled properly in signal invocation
3607 QQmlComponent varComponentTen(&engine, testFileUrl("scarceResourceSignal.var.qml"));
3608 object = varComponentTen.create();
3609 srsc = object->findChild<QObject*>("srsc");
3611 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3612 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3613 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3614 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3615 QMetaObject::invokeMethod(srsc, "testSignal");
3616 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3617 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3618 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3619 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3620 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3621 QVERIFY(srsc->property("scarceResourceCopy").isValid());
3622 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3623 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3624 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3625 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3628 // test that scarce resources are handled properly from js functions in qml files
3629 QQmlComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml"));
3630 object = varComponentEleven.create();
3631 QVERIFY(object != 0);
3632 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3633 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3634 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3635 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3636 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3637 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3638 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3639 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3640 QMetaObject::invokeMethod(object, "releaseScarceResource");
3641 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3642 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3643 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3644 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3647 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3648 QQmlComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
3649 object = varComponentTwelve.create();
3650 QVERIFY(object != 0);
3651 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3652 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3653 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3654 srp_name = object->property("srp_name").toString();
3655 expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3656 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3657 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3658 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3659 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3660 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3661 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3664 // test that if an Item which has JS ownership but has a scarce resource property is garbage collected,
3665 // that the scarce resource is removed from the engine's list of scarce resources to clean up.
3666 QQmlComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml"));
3667 object = varComponentThirteen.create();
3668 QVERIFY(object != 0);
3669 QVERIFY(!object->property("varProperty").isValid()); // not assigned yet
3670 QMetaObject::invokeMethod(object, "assignVarProperty");
3671 QVERIFY(ep->scarceResources.isEmpty()); // the scarce resource is a VME property.
3672 QMetaObject::invokeMethod(object, "deassignVarProperty");
3673 QVERIFY(ep->scarceResources.isEmpty()); // should still be empty; the resource should have been released on gc.
3676 /* property variant semantics */
3678 // test that scarce resources are handled properly in signal invocation
3679 QQmlComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml"));
3680 object = variantComponentTen.create();
3681 QVERIFY(object != 0);
3682 srsc = object->findChild<QObject*>("srsc");
3684 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3685 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3686 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3687 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3688 QMetaObject::invokeMethod(srsc, "testSignal");
3689 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3690 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3691 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3692 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3693 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3694 QVERIFY(srsc->property("scarceResourceCopy").isValid());
3695 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3696 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3697 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3698 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3701 // test that scarce resources are handled properly from js functions in qml files
3702 QQmlComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml"));
3703 object = variantComponentEleven.create();
3704 QVERIFY(object != 0);
3705 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3706 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3707 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3708 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3709 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3710 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3711 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3712 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3713 QMetaObject::invokeMethod(object, "releaseScarceResource");
3714 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3715 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3716 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3717 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3720 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3721 QQmlComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml"));
3722 object = variantComponentTwelve.create();
3723 QVERIFY(object != 0);
3724 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3725 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3726 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3727 srp_name = object->property("srp_name").toString();
3728 expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3729 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3730 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3731 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3732 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3733 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3734 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3738 void tst_qqmlecmascript::scarceResources_data()
3740 QTest::addColumn<QUrl>("qmlFile");
3741 QTest::addColumn<bool>("readDetachStatus");
3742 QTest::addColumn<bool>("expectedDetachStatus");
3743 QTest::addColumn<QStringList>("propertyNames");
3744 QTest::addColumn<QVariantList>("expectedValidity");
3745 QTest::addColumn<QVariantList>("expectedValues");
3746 QTest::addColumn<QStringList>("expectedErrors");
3748 QPixmap origPixmap(100, 100);
3749 origPixmap.fill(Qt::blue);
3751 /* property var semantics */
3753 // in the following three cases, the instance created from the component
3754 // has a property which is a copy of the scarce resource; hence, the
3755 // resource should NOT be detached prior to deletion of the object instance,
3756 // unless the resource is destroyed explicitly.
3757 QTest::newRow("var: import scarce resource copy directly")
3758 << testFileUrl("scarceResourceCopy.var.qml")
3760 << false // won't be detached, because assigned to property and not explicitly released
3761 << (QStringList() << QLatin1String("scarceResourceCopy"))
3762 << (QList<QVariant>() << true)
3763 << (QList<QVariant>() << origPixmap)
3766 QTest::newRow("var: import scarce resource copy from JS")
3767 << testFileUrl("scarceResourceCopyFromJs.var.qml")
3769 << false // won't be detached, because assigned to property and not explicitly released
3770 << (QStringList() << QLatin1String("scarceResourceCopy"))
3771 << (QList<QVariant>() << true)
3772 << (QList<QVariant>() << origPixmap)
3775 QTest::newRow("var: import released scarce resource copy from JS")
3776 << testFileUrl("scarceResourceDestroyedCopy.var.qml")
3778 << true // explicitly released, so it will be detached
3779 << (QStringList() << QLatin1String("scarceResourceCopy"))
3780 << (QList<QVariant>() << false)
3781 << (QList<QVariant>() << QVariant())
3784 // in the following three cases, no other copy should exist in memory,
3785 // and so it should be detached (unless explicitly preserved).
3786 QTest::newRow("var: import auto-release SR from JS in binding side-effect")
3787 << testFileUrl("scarceResourceTest.var.qml")
3789 << true // auto released, so it will be detached
3790 << (QStringList() << QLatin1String("scarceResourceTest"))
3791 << (QList<QVariant>() << true)
3792 << (QList<QVariant>() << QVariant(100))
3794 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
3795 << testFileUrl("scarceResourceTestPreserve.var.qml")
3797 << false // won't be detached because we explicitly preserve it
3798 << (QStringList() << QLatin1String("scarceResourceTest"))
3799 << (QList<QVariant>() << true)
3800 << (QList<QVariant>() << QVariant(100))
3802 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
3803 << testFileUrl("scarceResourceTestMultiple.var.qml")
3805 << true // will be detached because all resources were released manually or automatically.
3806 << (QStringList() << QLatin1String("scarceResourceTest"))
3807 << (QList<QVariant>() << true)
3808 << (QList<QVariant>() << QVariant(100))
3811 // In the following three cases, test that scarce resources are handled
3812 // correctly for imports.
3813 QTest::newRow("var: import with no binding")
3814 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
3815 << false // cannot check detach status.
3818 << QList<QVariant>()
3819 << QList<QVariant>()
3821 QTest::newRow("var: import with binding without explicit preserve")
3822 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
3825 << (QStringList() << QLatin1String("scarceResourceCopy"))
3826 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
3827 << (QList<QVariant>() << QVariant())
3829 QTest::newRow("var: import with explicit release after binding evaluation")
3830 << testFileUrl("scarceResourceCopyImport.var.qml")
3833 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
3834 << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated.
3835 << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true))
3837 QTest::newRow("var: import with different js objects")
3838 << testFileUrl("scarceResourceCopyImportDifferent.var.qml")
3841 << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
3842 << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
3843 << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false))
3845 QTest::newRow("var: import with different js objects and explicit release")
3846 << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml")
3849 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
3850 << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
3851 << (QList<QVariant>() << QVariant(origPixmap) << QVariant())
3853 QTest::newRow("var: import with same js objects and explicit release")
3854 << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml")
3857 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
3858 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
3859 << (QList<QVariant>() << QVariant() << QVariant())
3861 QTest::newRow("var: binding with same js objects and explicit release")
3862 << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml")
3865 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
3866 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
3867 << (QList<QVariant>() << QVariant() << QVariant())
3871 /* property variant semantics */
3873 // in the following three cases, the instance created from the component
3874 // has a property which is a copy of the scarce resource; hence, the
3875 // resource should NOT be detached prior to deletion of the object instance,
3876 // unless the resource is destroyed explicitly.
3877 QTest::newRow("variant: import scarce resource copy directly")
3878 << testFileUrl("scarceResourceCopy.variant.qml")
3880 << false // won't be detached, because assigned to property and not explicitly released
3881 << (QStringList() << QLatin1String("scarceResourceCopy"))
3882 << (QList<QVariant>() << true)
3883 << (QList<QVariant>() << origPixmap)
3886 QTest::newRow("variant: import scarce resource copy from JS")
3887 << testFileUrl("scarceResourceCopyFromJs.variant.qml")
3889 << false // won't be detached, because assigned to property and not explicitly released
3890 << (QStringList() << QLatin1String("scarceResourceCopy"))
3891 << (QList<QVariant>() << true)
3892 << (QList<QVariant>() << origPixmap)
3895 QTest::newRow("variant: import released scarce resource copy from JS")
3896 << testFileUrl("scarceResourceDestroyedCopy.variant.qml")
3898 << true // explicitly released, so it will be detached
3899 << (QStringList() << QLatin1String("scarceResourceCopy"))
3900 << (QList<QVariant>() << false)
3901 << (QList<QVariant>() << QVariant())
3904 // in the following three cases, no other copy should exist in memory,
3905 // and so it should be detached (unless explicitly preserved).
3906 QTest::newRow("variant: import auto-release SR from JS in binding side-effect")
3907 << testFileUrl("scarceResourceTest.variant.qml")
3909 << true // auto released, so it will be detached
3910 << (QStringList() << QLatin1String("scarceResourceTest"))
3911 << (QList<QVariant>() << true)
3912 << (QList<QVariant>() << QVariant(100))
3914 QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect")
3915 << testFileUrl("scarceResourceTestPreserve.variant.qml")
3917 << false // won't be detached because we explicitly preserve it
3918 << (QStringList() << QLatin1String("scarceResourceTest"))
3919 << (QList<QVariant>() << true)
3920 << (QList<QVariant>() << QVariant(100))
3922 QTest::newRow("variant: import multiple scarce resources")
3923 << testFileUrl("scarceResourceTestMultiple.variant.qml")
3925 << true // will be detached because all resources were released manually or automatically.
3926 << (QStringList() << QLatin1String("scarceResourceTest"))
3927 << (QList<QVariant>() << true)
3928 << (QList<QVariant>() << QVariant(100))
3931 // In the following three cases, test that scarce resources are handled
3932 // correctly for imports.
3933 QTest::newRow("variant: import with no binding")
3934 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
3935 << false // cannot check detach status.
3938 << QList<QVariant>()
3939 << QList<QVariant>()
3941 QTest::newRow("variant: import with binding without explicit preserve")
3942 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
3945 << (QStringList() << QLatin1String("scarceResourceCopy"))
3946 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
3947 << (QList<QVariant>() << QVariant())
3949 QTest::newRow("variant: import with explicit release after binding evaluation")
3950 << testFileUrl("scarceResourceCopyImport.variant.qml")
3953 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo"))
3954 << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies.
3955 << (QList<QVariant>() << origPixmap << origPixmap << QVariant())
3959 void tst_qqmlecmascript::scarceResources()
3961 QFETCH(QUrl, qmlFile);
3962 QFETCH(bool, readDetachStatus);
3963 QFETCH(bool, expectedDetachStatus);
3964 QFETCH(QStringList, propertyNames);
3965 QFETCH(QVariantList, expectedValidity);
3966 QFETCH(QVariantList, expectedValues);
3967 QFETCH(QStringList, expectedErrors);
3969 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
3970 ScarceResourceObject *eo = 0;
3971 QObject *object = 0;
3973 QQmlComponent c(&engine, qmlFile);
3974 object = c.create();
3975 QVERIFY(object != 0);
3976 for (int i = 0; i < propertyNames.size(); ++i) {
3977 QString prop = propertyNames.at(i);
3978 bool validity = expectedValidity.at(i).toBool();
3979 QVariant value = expectedValues.at(i);
3981 QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity);
3982 if (value.type() == QVariant::Int) {
3983 QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt());
3984 } else if (value.type() == QVariant::Pixmap) {
3985 QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>());
3989 if (readDetachStatus) {
3990 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3991 QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
3994 QVERIFY(ep->scarceResources.isEmpty());
3998 void tst_qqmlecmascript::propertyChangeSlots()
4000 // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
4001 QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml"));
4002 QObject *object = component.create();
4003 QVERIFY(object != 0);
4006 // ensure that invalid property names fail properly.
4007 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4008 QQmlComponent e1(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.1.qml"));
4009 QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
4010 QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
4011 object = e1.create();
4012 QVERIFY(object == 0);
4015 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4016 QQmlComponent e2(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.2.qml"));
4017 expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
4018 QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
4019 object = e2.create();
4020 QVERIFY(object == 0);
4023 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4024 QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml"));
4025 expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
4026 QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
4027 object = e3.create();
4028 QVERIFY(object == 0);
4031 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4032 QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml"));
4033 expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
4034 QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
4035 object = e4.create();
4036 QVERIFY(object == 0);
4040 void tst_qqmlecmascript::propertyVar_data()
4042 QTest::addColumn<QUrl>("qmlFile");
4045 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyVar.1.qml");
4046 QTest::newRow("non-bindable object changed") << testFileUrl("propertyVar.2.qml");
4047 QTest::newRow("primitive changed") << testFileUrl("propertyVar.3.qml");
4048 QTest::newRow("javascript array modification") << testFileUrl("propertyVar.4.qml");
4049 QTest::newRow("javascript map modification") << testFileUrl("propertyVar.5.qml");
4050 QTest::newRow("javascript array assignment") << testFileUrl("propertyVar.6.qml");
4051 QTest::newRow("javascript map assignment") << testFileUrl("propertyVar.7.qml");
4052 QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml");
4053 QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml");
4054 QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml");
4057 void tst_qqmlecmascript::propertyVar()
4059 QFETCH(QUrl, qmlFile);
4061 QQmlComponent component(&engine, qmlFile);
4062 QObject *object = component.create();
4063 QVERIFY(object != 0);
4065 QCOMPARE(object->property("test").toBool(), true);
4070 // Tests that we can write QVariant values to var properties from C++
4071 void tst_qqmlecmascript::propertyVarCpp()
4073 QObject *object = 0;
4075 // ensure that writing to and reading from a var property from cpp works as required.
4076 // Literal values stored in var properties can be read and written as QVariants
4077 // of a specific type, whereas object values are read as QVariantMaps.
4078 QQmlComponent component(&engine, testFileUrl("propertyVarCpp.qml"));
4079 object = component.create();
4080 QVERIFY(object != 0);
4081 // assign int to property var that currently has int assigned
4082 QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
4083 QCOMPARE(object->property("varBound"), QVariant(15));
4084 QCOMPARE(object->property("intBound"), QVariant(15));
4085 QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
4086 QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
4087 // assign string to property var that current has bool assigned
4088 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
4089 QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
4090 QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
4091 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
4092 // now enforce behaviour when accessing JavaScript objects from cpp.
4093 QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
4097 static void gc(QQmlEngine &engine)
4099 engine.collectGarbage();
4100 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4101 QCoreApplication::processEvents();
4104 void tst_qqmlecmascript::propertyVarOwnership()
4106 // Referenced JS objects are not collected
4108 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.qml"));
4109 QObject *object = component.create();
4110 QVERIFY(object != 0);
4111 QCOMPARE(object->property("test").toBool(), false);
4112 QMetaObject::invokeMethod(object, "runTest");
4113 QCOMPARE(object->property("test").toBool(), true);
4116 // Referenced JS objects are not collected
4118 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml"));
4119 QObject *object = component.create();
4120 QVERIFY(object != 0);
4121 QCOMPARE(object->property("test").toBool(), false);
4122 QMetaObject::invokeMethod(object, "runTest");
4123 QCOMPARE(object->property("test").toBool(), true);
4126 // Qt objects are not collected until they've been dereferenced
4128 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml"));
4129 QObject *object = component.create();
4130 QVERIFY(object != 0);
4132 QCOMPARE(object->property("test2").toBool(), false);
4133 QCOMPARE(object->property("test2").toBool(), false);
4135 QMetaObject::invokeMethod(object, "runTest");
4136 QCOMPARE(object->property("test1").toBool(), true);
4138 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4139 QVERIFY(!referencedObject.isNull());
4141 QVERIFY(!referencedObject.isNull());
4143 QMetaObject::invokeMethod(object, "runTest2");
4144 QCOMPARE(object->property("test2").toBool(), true);
4146 QVERIFY(referencedObject.isNull());
4150 // Self reference does not prevent Qt object collection
4152 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml"));
4153 QObject *object = component.create();
4154 QVERIFY(object != 0);
4156 QCOMPARE(object->property("test").toBool(), true);
4158 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4159 QVERIFY(!referencedObject.isNull());
4161 QVERIFY(!referencedObject.isNull());
4163 QMetaObject::invokeMethod(object, "runTest");
4165 QVERIFY(referencedObject.isNull());
4169 // Garbage collection cannot result in attempted dereference of empty handle
4171 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.5.qml"));
4172 QObject *object = component.create();
4173 QVERIFY(object != 0);
4174 QMetaObject::invokeMethod(object, "runTest");
4175 QCOMPARE(object->property("test").toBool(), true);
4180 void tst_qqmlecmascript::propertyVarImplicitOwnership()
4182 // The childObject has a reference to a different QObject. We want to ensure
4183 // that the different item will not be cleaned up until required. IE, the childObject
4184 // has implicit ownership of the constructed QObject.
4185 QQmlComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml"));
4186 QObject *object = component.create();
4187 QVERIFY(object != 0);
4188 QMetaObject::invokeMethod(object, "assignCircular");
4189 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4190 QCoreApplication::processEvents();
4191 QObject *rootObject = object->property("vp").value<QObject*>();
4192 QVERIFY(rootObject != 0);
4193 QObject *childObject = rootObject->findChild<QObject*>("text");
4194 QVERIFY(childObject != 0);
4195 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4196 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4197 QMetaObject::invokeMethod(childObject, "constructQObject"); // creates a reference to a constructed QObject.
4198 QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
4199 QVERIFY(!qobjectGuard.isNull());
4200 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4201 QCoreApplication::processEvents();
4202 QVERIFY(!qobjectGuard.isNull());
4203 QMetaObject::invokeMethod(object, "deassignCircular");
4204 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4205 QCoreApplication::processEvents();
4206 QVERIFY(qobjectGuard.isNull()); // should have been collected now.
4210 void tst_qqmlecmascript::propertyVarReparent()
4212 // ensure that nothing breaks if we re-parent objects
4213 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4214 QObject *object = component.create();
4215 QVERIFY(object != 0);
4216 QMetaObject::invokeMethod(object, "assignVarProp");
4217 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4218 QCoreApplication::processEvents();
4219 QObject *rect = object->property("vp").value<QObject*>();
4220 QObject *text = rect->findChild<QObject*>("textOne");
4221 QObject *text2 = rect->findChild<QObject*>("textTwo");
4222 QWeakPointer<QObject> rectGuard(rect);
4223 QWeakPointer<QObject> textGuard(text);
4224 QWeakPointer<QObject> text2Guard(text2);
4225 QVERIFY(!rectGuard.isNull());
4226 QVERIFY(!textGuard.isNull());
4227 QVERIFY(!text2Guard.isNull());
4228 QCOMPARE(text->property("textCanary").toInt(), 11);
4229 QCOMPARE(text2->property("textCanary").toInt(), 12);
4230 // now construct an image which we will reparent.
4231 QMetaObject::invokeMethod(text2, "constructQObject");
4232 QObject *image = text2->property("vp").value<QObject*>();
4233 QWeakPointer<QObject> imageGuard(image);
4234 QVERIFY(!imageGuard.isNull());
4235 QCOMPARE(image->property("imageCanary").toInt(), 13);
4236 // now reparent the "Image" object (currently, it has JS ownership)
4237 image->setParent(text); // shouldn't be collected after deassignVp now, since has a parent.
4238 QMetaObject::invokeMethod(text2, "deassignVp");
4239 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4240 QCoreApplication::processEvents();
4241 QCOMPARE(text->property("textCanary").toInt(), 11);
4242 QCOMPARE(text2->property("textCanary").toInt(), 22);
4243 QVERIFY(!imageGuard.isNull()); // should still be alive.
4244 QCOMPARE(image->property("imageCanary").toInt(), 13); // still able to access var properties
4245 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4246 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4247 QCoreApplication::processEvents();
4248 QVERIFY(imageGuard.isNull()); // should now have been deleted, due to parent being deleted.
4252 void tst_qqmlecmascript::propertyVarReparentNullContext()
4254 // sometimes reparenting can cause problems
4255 // (eg, if the ctxt is collected, varproperties are no longer available)
4256 // this test ensures that no crash occurs in that situation.
4257 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4258 QObject *object = component.create();
4259 QVERIFY(object != 0);
4260 QMetaObject::invokeMethod(object, "assignVarProp");
4261 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4262 QCoreApplication::processEvents();
4263 QObject *rect = object->property("vp").value<QObject*>();
4264 QObject *text = rect->findChild<QObject*>("textOne");
4265 QObject *text2 = rect->findChild<QObject*>("textTwo");
4266 QWeakPointer<QObject> rectGuard(rect);
4267 QWeakPointer<QObject> textGuard(text);
4268 QWeakPointer<QObject> text2Guard(text2);
4269 QVERIFY(!rectGuard.isNull());
4270 QVERIFY(!textGuard.isNull());
4271 QVERIFY(!text2Guard.isNull());
4272 QCOMPARE(text->property("textCanary").toInt(), 11);
4273 QCOMPARE(text2->property("textCanary").toInt(), 12);
4274 // now construct an image which we will reparent.
4275 QMetaObject::invokeMethod(text2, "constructQObject");
4276 QObject *image = text2->property("vp").value<QObject*>();
4277 QWeakPointer<QObject> imageGuard(image);
4278 QVERIFY(!imageGuard.isNull());
4279 QCOMPARE(image->property("imageCanary").toInt(), 13);
4280 // now reparent the "Image" object (currently, it has JS ownership)
4281 image->setParent(object); // reparented to base object. after deassignVarProp, the ctxt will be invalid.
4282 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4283 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4284 QCoreApplication::processEvents();
4285 QVERIFY(!imageGuard.isNull()); // should still be alive.
4286 QVERIFY(!image->property("imageCanary").isValid()); // but varProperties won't be available (null context).
4288 QVERIFY(imageGuard.isNull()); // should now be dead.
4291 void tst_qqmlecmascript::propertyVarCircular()
4293 // enforce behaviour regarding circular references - ensure qdvmemo deletion.
4294 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.qml"));
4295 QObject *object = component.create();
4296 QVERIFY(object != 0);
4297 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4298 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4299 QCoreApplication::processEvents();
4300 QCOMPARE(object->property("canaryInt"), QVariant(5));
4301 QVariant canaryResourceVariant = object->property("canaryResource");
4302 QVERIFY(canaryResourceVariant.isValid());
4303 QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
4304 canaryResourceVariant = QVariant(); // invalidate it to remove one copy of the pixmap from memory.
4305 QMetaObject::invokeMethod(object, "deassignCanaryResource"); // remove one copy of the pixmap from memory
4306 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4307 QCoreApplication::processEvents();
4308 QVERIFY(!canaryResourcePixmap.isDetached()); // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
4309 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4310 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4311 QCoreApplication::processEvents();
4312 QCOMPARE(object->property("canaryInt"), QVariant(2));
4313 QCOMPARE(object->property("canaryResource"), QVariant(1));
4314 QVERIFY(canaryResourcePixmap.isDetached()); // now detached, since orig copy was member of qdvmemo which was deleted.
4318 void tst_qqmlecmascript::propertyVarCircular2()
4320 // track deletion of JS-owned parent item with Cpp-owned child
4321 // where the child has a var property referencing its parent.
4322 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4323 QObject *object = component.create();
4324 QVERIFY(object != 0);
4325 QMetaObject::invokeMethod(object, "assignCircular");
4326 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4327 QCoreApplication::processEvents();
4328 QObject *rootObject = object->property("vp").value<QObject*>();
4329 QVERIFY(rootObject != 0);
4330 QObject *childObject = rootObject->findChild<QObject*>("text");
4331 QVERIFY(childObject != 0);
4332 QWeakPointer<QObject> rootObjectTracker(rootObject);
4333 QVERIFY(!rootObjectTracker.isNull());
4334 QWeakPointer<QObject> childObjectTracker(childObject);
4335 QVERIFY(!childObjectTracker.isNull());
4337 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4338 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4339 QMetaObject::invokeMethod(object, "deassignCircular");
4340 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4341 QCoreApplication::processEvents();
4342 QVERIFY(rootObjectTracker.isNull()); // should have been collected
4343 QVERIFY(childObjectTracker.isNull()); // should have been collected
4347 void tst_qqmlecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
4349 *(int*)(parameter) += 1;
4350 qPersistentDispose(object);
4353 void tst_qqmlecmascript::propertyVarInheritance()
4355 int propertyVarWeakRefCallbackCount = 0;
4357 // enforce behaviour regarding element inheritance - ensure handle disposal.
4358 // The particular component under test here has a chain of references.
4359 QQmlComponent component(&engine, testFileUrl("propertyVar.inherit.qml"));
4360 QObject *object = component.create();
4361 QVERIFY(object != 0);
4362 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4363 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4364 QCoreApplication::processEvents();
4365 // we want to be able to track when the varProperties array of the last metaobject is disposed
4366 QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
4367 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*>();
4368 QQmlVMEMetaObject *icovmemo = ((QQmlVMEMetaObject *)(ico5->metaObject()));
4369 QQmlVMEMetaObject *ccovmemo = ((QQmlVMEMetaObject *)(cco5->metaObject()));
4370 v8::Persistent<v8::Value> icoCanaryHandle;
4371 v8::Persistent<v8::Value> ccoCanaryHandle;
4374 // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only
4375 // public function which can return us a handle to something in the varProperties array.
4376 icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ")));
4377 ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")));
4378 // we make them weak and invoke the gc, but we should not hit the weak-callback yet
4379 // as the varproperties array of each vmemo still references the resource.
4380 icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4381 ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4383 QVERIFY(propertyVarWeakRefCallbackCount == 0);
4385 // now we deassign the var prop, which should trigger collection of item subtrees.
4386 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4387 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4388 QCoreApplication::processEvents();
4389 // ensure that there are only weak handles to the underlying varProperties array remaining.
4391 QCOMPARE(propertyVarWeakRefCallbackCount, 2); // should have been called for both, since all refs should be weak.
4393 // since there are no parent vmemo's to keep implicit references alive, and the only handles
4394 // to what remains are weak, all varProperties arrays must have been collected.
4397 void tst_qqmlecmascript::propertyVarInheritance2()
4399 int propertyVarWeakRefCallbackCount = 0;
4401 // The particular component under test here does NOT have a chain of references; the
4402 // only link between rootObject and childObject is that rootObject is the parent of childObject.
4403 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4404 QObject *object = component.create();
4405 QVERIFY(object != 0);
4406 QMetaObject::invokeMethod(object, "assignCircular");
4407 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4408 QCoreApplication::processEvents();
4409 QObject *rootObject = object->property("vp").value<QObject*>();
4410 QVERIFY(rootObject != 0);
4411 QObject *childObject = rootObject->findChild<QObject*>("text");
4412 QVERIFY(childObject != 0);
4413 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4414 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4415 v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
4418 propertyVarWeakRefCallbackCount = 0; // reset callback count.
4419 childObjectVarArrayValueHandle = qPersistentNew(((QQmlVMEMetaObject *)(childObject->metaObject()))->vmeProperty(childObject->metaObject()->indexOfProperty("vp")));
4420 childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4422 QVERIFY(propertyVarWeakRefCallbackCount == 0); // should not have been collected yet.
4423 QCOMPARE(childObject->property("vp").value<QObject*>(), rootObject);
4424 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4426 QMetaObject::invokeMethod(object, "deassignCircular");
4427 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4428 QCoreApplication::processEvents();
4429 QVERIFY(propertyVarWeakRefCallbackCount == 1); // should have been collected now.
4433 // Ensure that QObject type conversion works on binding assignment
4434 void tst_qqmlecmascript::elementAssign()
4436 QQmlComponent component(&engine, testFileUrl("elementAssign.qml"));
4438 QObject *object = component.create();
4439 QVERIFY(object != 0);
4441 QCOMPARE(object->property("test").toBool(), true);
4447 void tst_qqmlecmascript::objectPassThroughSignals()
4449 QQmlComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml"));
4451 QObject *object = component.create();
4452 QVERIFY(object != 0);
4454 QCOMPARE(object->property("test").toBool(), true);
4460 void tst_qqmlecmascript::objectConversion()
4462 QQmlComponent component(&engine, testFileUrl("objectConversion.qml"));
4464 QObject *object = component.create();
4465 QVERIFY(object != 0);
4467 QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
4468 QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
4475 void tst_qqmlecmascript::booleanConversion()
4477 QQmlComponent component(&engine, testFileUrl("booleanConversion.qml"));
4479 QObject *object = component.create();
4480 QVERIFY(object != 0);
4482 QCOMPARE(object->property("test_true1").toBool(), true);
4483 QCOMPARE(object->property("test_true2").toBool(), true);
4484 QCOMPARE(object->property("test_true3").toBool(), true);
4485 QCOMPARE(object->property("test_true4").toBool(), true);
4486 QCOMPARE(object->property("test_true5").toBool(), true);
4488 QCOMPARE(object->property("test_false1").toBool(), false);
4489 QCOMPARE(object->property("test_false2").toBool(), false);
4490 QCOMPARE(object->property("test_false3").toBool(), false);
4495 void tst_qqmlecmascript::handleReferenceManagement()
4500 // Linear QObject reference
4501 QQmlEngine hrmEngine;
4502 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml"));
4503 QObject *object = component.create();
4504 QVERIFY(object != 0);
4505 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4506 cro->setEngine(&hrmEngine);
4507 cro->setDtorCount(&dtorCount);
4508 QMetaObject::invokeMethod(object, "createReference");
4510 QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
4512 hrmEngine.collectGarbage();
4513 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4514 QCoreApplication::processEvents();
4515 QCOMPARE(dtorCount, 3);
4520 // Circular QObject reference
4521 QQmlEngine hrmEngine;
4522 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml"));
4523 QObject *object = component.create();
4524 QVERIFY(object != 0);
4525 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4526 cro->setEngine(&hrmEngine);
4527 cro->setDtorCount(&dtorCount);
4528 QMetaObject::invokeMethod(object, "circularReference");
4530 QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
4532 hrmEngine.collectGarbage();
4533 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4534 QCoreApplication::processEvents();
4535 QCOMPARE(dtorCount, 3);
4540 // Linear handle reference
4541 QQmlEngine hrmEngine;
4542 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.1.qml"));
4543 QObject *object = component.create();
4544 QVERIFY(object != 0);
4545 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4547 crh->setEngine(&hrmEngine);
4548 crh->setDtorCount(&dtorCount);
4549 QMetaObject::invokeMethod(object, "createReference");
4550 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4551 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4552 QVERIFY(first != 0);
4553 QVERIFY(second != 0);
4554 first->addReference(QQmlData::get(second)->v8object); // create reference
4555 // now we have to reparent second and make second owned by JS.
4556 second->setParent(0);
4557 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
4559 QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
4561 hrmEngine.collectGarbage();
4562 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4563 QCoreApplication::processEvents();
4564 QCOMPARE(dtorCount, 3);
4569 // Circular handle reference
4570 QQmlEngine hrmEngine;
4571 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.2.qml"));
4572 QObject *object = component.create();
4573 QVERIFY(object != 0);
4574 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4576 crh->setEngine(&hrmEngine);
4577 crh->setDtorCount(&dtorCount);
4578 QMetaObject::invokeMethod(object, "circularReference");
4579 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4580 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4581 QVERIFY(first != 0);
4582 QVERIFY(second != 0);
4583 first->addReference(QQmlData::get(second)->v8object); // create circular reference
4584 second->addReference(QQmlData::get(first)->v8object); // note: must be weak.
4585 // now we have to reparent and change ownership.
4586 first->setParent(0);
4587 second->setParent(0);
4588 QQmlEngine::setObjectOwnership(first, QQmlEngine::JavaScriptOwnership);
4589 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
4591 QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
4593 hrmEngine.collectGarbage();
4594 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4595 QCoreApplication::processEvents();
4596 QCOMPARE(dtorCount, 3);
4601 // multiple engine interaction - linear reference
4602 QQmlEngine hrmEngine1;
4603 QQmlEngine hrmEngine2;
4604 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4605 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4606 QObject *object1 = component1.create();
4607 QObject *object2 = component2.create();
4608 QVERIFY(object1 != 0);
4609 QVERIFY(object2 != 0);
4610 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4611 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4614 crh1->setEngine(&hrmEngine1);
4615 crh2->setEngine(&hrmEngine2);
4616 crh1->setDtorCount(&dtorCount);
4617 crh2->setDtorCount(&dtorCount);
4618 QMetaObject::invokeMethod(object1, "createReference");
4619 QMetaObject::invokeMethod(object2, "createReference");
4620 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4621 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4622 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4623 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4624 QVERIFY(first1 != 0);
4625 QVERIFY(second1 != 0);
4626 QVERIFY(first2 != 0);
4627 QVERIFY(second2 != 0);
4628 first1->addReference(QQmlData::get(second2)->v8object); // create reference across engines
4629 // now we have to reparent second2 and make second2 owned by JS.
4630 second2->setParent(0);
4631 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4633 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4634 QCoreApplication::processEvents();
4635 QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
4638 hrmEngine1.collectGarbage();
4639 hrmEngine2.collectGarbage();
4640 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4641 QCoreApplication::processEvents();
4642 QCOMPARE(dtorCount, 6);
4647 // multiple engine interaction - circular reference
4648 QQmlEngine hrmEngine1;
4649 QQmlEngine hrmEngine2;
4650 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4651 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4652 QObject *object1 = component1.create();
4653 QObject *object2 = component2.create();
4654 QVERIFY(object1 != 0);
4655 QVERIFY(object2 != 0);
4656 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4657 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4660 crh1->setEngine(&hrmEngine1);
4661 crh2->setEngine(&hrmEngine2);
4662 crh1->setDtorCount(&dtorCount);
4663 crh2->setDtorCount(&dtorCount);
4664 QMetaObject::invokeMethod(object1, "createReference");
4665 QMetaObject::invokeMethod(object2, "createReference");
4666 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4667 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4668 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4669 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4670 QVERIFY(first1 != 0);
4671 QVERIFY(second1 != 0);
4672 QVERIFY(first2 != 0);
4673 QVERIFY(second2 != 0);
4674 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
4675 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
4676 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
4677 first2->addReference(QQmlData::get(first1)->v8object); // close the loop - circular ref across engines
4678 // now we have to reparent and change ownership to JS.
4679 first1->setParent(0);
4680 second1->setParent(0);
4681 first2->setParent(0);
4682 second2->setParent(0);
4683 QQmlEngine::setObjectOwnership(first1, QQmlEngine::JavaScriptOwnership);
4684 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
4685 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
4686 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4688 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4689 QCoreApplication::processEvents();
4690 QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
4693 hrmEngine1.collectGarbage();
4694 hrmEngine2.collectGarbage();
4695 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4696 QCoreApplication::processEvents();
4697 QCOMPARE(dtorCount, 6);
4702 // multiple engine interaction - linear reference with engine deletion
4703 QQmlEngine *hrmEngine1 = new QQmlEngine;
4704 QQmlEngine *hrmEngine2 = new QQmlEngine;
4705 QQmlComponent component1(hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4706 QQmlComponent component2(hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4707 QObject *object1 = component1.create();
4708 QObject *object2 = component2.create();
4709 QVERIFY(object1 != 0);
4710 QVERIFY(object2 != 0);
4711 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4712 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4715 crh1->setEngine(hrmEngine1);
4716 crh2->setEngine(hrmEngine2);
4717 crh1->setDtorCount(&dtorCount);
4718 crh2->setDtorCount(&dtorCount);
4719 QMetaObject::invokeMethod(object1, "createReference");
4720 QMetaObject::invokeMethod(object2, "createReference");
4721 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4722 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4723 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4724 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4725 QVERIFY(first1 != 0);
4726 QVERIFY(second1 != 0);
4727 QVERIFY(first2 != 0);
4728 QVERIFY(second2 != 0);
4729 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
4730 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
4731 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
4732 // now we have to reparent and change ownership to JS.
4733 first1->setParent(crh1);
4734 second1->setParent(0);
4735 first2->setParent(0);
4736 second2->setParent(0);
4737 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
4738 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
4739 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4741 QCOMPARE(dtorCount, 0);
4744 QCOMPARE(dtorCount, 0);
4747 hrmEngine1->collectGarbage();
4748 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4749 QCoreApplication::processEvents();
4750 QCOMPARE(dtorCount, 6);
4755 void tst_qqmlecmascript::stringArg()
4757 QQmlComponent component(&engine, testFileUrl("stringArg.qml"));
4758 QObject *object = component.create();
4759 QVERIFY(object != 0);
4760 QMetaObject::invokeMethod(object, "success");
4761 QVERIFY(object->property("returnValue").toBool());
4763 QString w1 = testFileUrl("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
4764 QTest::ignoreMessage(QtWarningMsg, w1.toAscii().constData());
4765 QMetaObject::invokeMethod(object, "failure");
4766 QVERIFY(object->property("returnValue").toBool());
4771 void tst_qqmlecmascript::readonlyDeclaration()
4773 QQmlComponent component(&engine, testFileUrl("readonlyDeclaration.qml"));
4775 QObject *object = component.create();
4776 QVERIFY(object != 0);
4778 QCOMPARE(object->property("test").toBool(), true);
4783 Q_DECLARE_METATYPE(QList<int>)
4784 Q_DECLARE_METATYPE(QList<qreal>)
4785 Q_DECLARE_METATYPE(QList<bool>)
4786 Q_DECLARE_METATYPE(QList<QString>)
4787 Q_DECLARE_METATYPE(QList<QUrl>)
4788 void tst_qqmlecmascript::sequenceConversionRead()
4791 QUrl qmlFile = testFileUrl("sequenceConversion.read.qml");
4792 QQmlComponent component(&engine, qmlFile);
4793 QObject *object = component.create();
4794 QVERIFY(object != 0);
4795 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4798 QMetaObject::invokeMethod(object, "readSequences");
4799 QList<int> intList; intList << 1 << 2 << 3 << 4;
4800 QCOMPARE(object->property("intListLength").toInt(), intList.length());
4801 QCOMPARE(object->property("intList").value<QList<int> >(), intList);
4802 QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
4803 QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
4804 QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
4805 QList<bool> boolList; boolList << true << false << true << false;
4806 QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
4807 QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
4808 QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
4809 QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
4810 QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
4811 QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
4812 QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
4813 QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
4814 QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
4815 QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
4816 QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
4818 QMetaObject::invokeMethod(object, "readSequenceElements");
4819 QCOMPARE(object->property("intVal").toInt(), 2);
4820 QCOMPARE(object->property("qrealVal").toReal(), 2.2);
4821 QCOMPARE(object->property("boolVal").toBool(), false);
4822 QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
4823 QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
4824 QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
4826 QMetaObject::invokeMethod(object, "enumerateSequenceElements");
4827 QCOMPARE(object->property("enumerationMatches").toBool(), true);
4829 intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
4830 QQmlProperty seqProp(seq, "intListProperty");
4831 QCOMPARE(seqProp.read().value<QList<int> >(), intList);
4832 QQmlProperty seqProp2(seq, "intListProperty", &engine);
4833 QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
4835 QMetaObject::invokeMethod(object, "testReferenceDeletion");
4836 QCOMPARE(object->property("referenceDeletion").toBool(), true);
4842 QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml");
4843 QQmlComponent component(&engine, qmlFile);
4844 QObject *object = component.create();
4845 QVERIFY(object != 0);
4846 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4849 // we haven't registered QList<QPoint> as a sequence type.
4850 QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
4851 QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
4852 QTest::ignoreMessage(QtWarningMsg, warningOne.toAscii().constData());
4853 QTest::ignoreMessage(QtWarningMsg, warningTwo.toAscii().constData());
4855 QMetaObject::invokeMethod(object, "performTest");
4857 // QList<QPoint> has not been registered as a sequence type.
4858 QCOMPARE(object->property("pointListLength").toInt(), 0);
4859 QVERIFY(!object->property("pointList").isValid());
4860 QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
4861 QQmlProperty seqProp(seq, "pointListProperty", &engine);
4862 QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
4868 void tst_qqmlecmascript::sequenceConversionWrite()
4871 QUrl qmlFile = testFileUrl("sequenceConversion.write.qml");
4872 QQmlComponent component(&engine, qmlFile);
4873 QObject *object = component.create();
4874 QVERIFY(object != 0);
4875 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4878 QMetaObject::invokeMethod(object, "writeSequences");
4879 QCOMPARE(object->property("success").toBool(), true);
4881 QMetaObject::invokeMethod(object, "writeSequenceElements");
4882 QCOMPARE(object->property("success").toBool(), true);
4884 QMetaObject::invokeMethod(object, "writeOtherElements");
4885 QCOMPARE(object->property("success").toBool(), true);
4887 QMetaObject::invokeMethod(object, "testReferenceDeletion");
4888 QCOMPARE(object->property("referenceDeletion").toBool(), true);
4894 QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml");
4895 QQmlComponent component(&engine, qmlFile);
4896 QObject *object = component.create();
4897 QVERIFY(object != 0);
4898 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4901 // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
4902 QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to void");
4903 QTest::ignoreMessage(QtWarningMsg, warningOne.toAscii().constData());
4905 QMetaObject::invokeMethod(object, "performTest");
4907 QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
4908 QCOMPARE(seq->pointListProperty(), pointList);
4914 void tst_qqmlecmascript::sequenceConversionArray()
4916 // ensure that in JS the returned sequences act just like normal JS Arrays.
4917 QUrl qmlFile = testFileUrl("sequenceConversion.array.qml");
4918 QQmlComponent component(&engine, qmlFile);
4919 QObject *object = component.create();
4920 QVERIFY(object != 0);
4921 QMetaObject::invokeMethod(object, "indexedAccess");
4922 QVERIFY(object->property("success").toBool());
4923 QMetaObject::invokeMethod(object, "arrayOperations");
4924 QVERIFY(object->property("success").toBool());
4925 QMetaObject::invokeMethod(object, "testEqualitySemantics");
4926 QVERIFY(object->property("success").toBool());
4927 QMetaObject::invokeMethod(object, "testReferenceDeletion");
4928 QCOMPARE(object->property("referenceDeletion").toBool(), true);
4933 void tst_qqmlecmascript::sequenceConversionIndexes()
4935 // ensure that we gracefully fail if unsupported index values are specified.
4936 // Qt container classes only support non-negative, signed integer index values.
4937 QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
4938 QQmlComponent component(&engine, qmlFile);
4939 QObject *object = component.create();
4940 QVERIFY(object != 0);
4941 QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
4942 QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
4943 QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
4944 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
4945 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
4946 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
4947 QMetaObject::invokeMethod(object, "indexedAccess");
4948 QVERIFY(object->property("success").toBool());
4952 void tst_qqmlecmascript::sequenceConversionThreads()
4954 // ensure that sequence conversion operations work correctly in a worker thread
4955 // and that serialisation between the main and worker thread succeeds.
4956 QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml");
4957 QQmlComponent component(&engine, qmlFile);
4958 QObject *object = component.create();
4959 QVERIFY(object != 0);
4961 QMetaObject::invokeMethod(object, "testIntSequence");
4962 QTRY_VERIFY(object->property("finished").toBool());
4963 QVERIFY(object->property("success").toBool());
4965 QMetaObject::invokeMethod(object, "testQrealSequence");
4966 QTRY_VERIFY(object->property("finished").toBool());
4967 QVERIFY(object->property("success").toBool());
4969 QMetaObject::invokeMethod(object, "testBoolSequence");
4970 QTRY_VERIFY(object->property("finished").toBool());
4971 QVERIFY(object->property("success").toBool());
4973 QMetaObject::invokeMethod(object, "testStringSequence");
4974 QTRY_VERIFY(object->property("finished").toBool());
4975 QVERIFY(object->property("success").toBool());
4977 QMetaObject::invokeMethod(object, "testQStringSequence");
4978 QTRY_VERIFY(object->property("finished").toBool());
4979 QVERIFY(object->property("success").toBool());
4981 QMetaObject::invokeMethod(object, "testUrlSequence");
4982 QTRY_VERIFY(object->property("finished").toBool());
4983 QVERIFY(object->property("success").toBool());
4985 QMetaObject::invokeMethod(object, "testVariantSequence");
4986 QTRY_VERIFY(object->property("finished").toBool());
4987 QVERIFY(object->property("success").toBool());
4992 void tst_qqmlecmascript::sequenceConversionBindings()
4995 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml");
4996 QQmlComponent component(&engine, qmlFile);
4997 QObject *object = component.create();
4998 QVERIFY(object != 0);
4999 QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
5000 QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
5001 QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
5002 QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
5003 QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
5008 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml");
5009 QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
5010 QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
5011 QQmlComponent component(&engine, qmlFile);
5012 QObject *object = component.create();
5013 QVERIFY(object != 0);
5018 void tst_qqmlecmascript::sequenceConversionCopy()
5020 QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml");
5021 QQmlComponent component(&engine, qmlFile);
5022 QObject *object = component.create();
5023 QVERIFY(object != 0);
5024 QMetaObject::invokeMethod(object, "testCopySequences");
5025 QCOMPARE(object->property("success").toBool(), true);
5026 QMetaObject::invokeMethod(object, "readSequenceCopyElements");
5027 QCOMPARE(object->property("success").toBool(), true);
5028 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5029 QCOMPARE(object->property("success").toBool(), true);
5033 void tst_qqmlecmascript::assignSequenceTypes()
5035 // test binding array to sequence type property
5037 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml"));
5038 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5039 QVERIFY(object != 0);
5040 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5041 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5042 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5043 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5044 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5045 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5049 // test binding literal to sequence type property
5051 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml"));
5052 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5053 QVERIFY(object != 0);
5054 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5055 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5056 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5057 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5058 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5059 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5063 // test binding single value to sequence type property
5065 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml"));
5066 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5067 QVERIFY(object != 0);
5068 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5069 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5070 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5071 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5075 // test assigning array to sequence type property in js function
5077 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml"));
5078 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5079 QVERIFY(object != 0);
5080 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5081 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5082 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5083 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5084 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5085 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5089 // test assigning literal to sequence type property in js function
5091 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml"));
5092 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5093 QVERIFY(object != 0);
5094 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5095 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5096 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5097 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5098 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5099 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5103 // test assigning single value to sequence type property in js function
5105 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml"));
5106 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5107 QVERIFY(object != 0);
5108 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5109 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5110 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5111 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5115 // test QList<QUrl> literal assignment and binding assignment causes url resolution when required
5117 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml"));
5118 QObject *object = component.create();
5119 QVERIFY(object != 0);
5120 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
5121 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
5122 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
5123 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
5124 MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco5"));
5125 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0);
5126 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5127 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5128 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5129 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5130 QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5135 // Test that assigning a null object works
5136 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
5137 void tst_qqmlecmascript::nullObjectBinding()
5139 QQmlComponent component(&engine, testFileUrl("nullObjectBinding.qml"));
5141 QObject *object = component.create();
5142 QVERIFY(object != 0);
5144 QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
5149 // Test that bindings don't evaluate once the engine has been destroyed
5150 void tst_qqmlecmascript::deletedEngine()
5152 QQmlEngine *engine = new QQmlEngine;
5153 QQmlComponent component(engine, testFileUrl("deletedEngine.qml"));
5155 QObject *object = component.create();
5156 QVERIFY(object != 0);
5158 QCOMPARE(object->property("a").toInt(), 39);
5159 object->setProperty("b", QVariant(9));
5160 QCOMPARE(object->property("a").toInt(), 117);
5164 QCOMPARE(object->property("a").toInt(), 117);
5165 object->setProperty("b", QVariant(10));
5166 QCOMPARE(object->property("a").toInt(), 117);
5171 // Test the crashing part of QTBUG-9705
5172 void tst_qqmlecmascript::libraryScriptAssert()
5174 QQmlComponent component(&engine, testFileUrl("libraryScriptAssert.qml"));
5176 QObject *object = component.create();
5177 QVERIFY(object != 0);
5182 void tst_qqmlecmascript::variantsAssignedUndefined()
5184 QQmlComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml"));
5186 QObject *object = component.create();
5187 QVERIFY(object != 0);
5189 QCOMPARE(object->property("test1").toInt(), 10);
5190 QCOMPARE(object->property("test2").toInt(), 11);
5192 object->setProperty("runTest", true);
5194 QCOMPARE(object->property("test1"), QVariant());
5195 QCOMPARE(object->property("test2"), QVariant());
5201 void tst_qqmlecmascript::qtbug_9792()
5203 QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml"));
5205 QQmlContext *context = new QQmlContext(engine.rootContext());
5207 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
5208 QVERIFY(object != 0);
5210 QTest::ignoreMessage(QtDebugMsg, "Hello world!");
5211 object->basicSignal();
5215 transientErrorsMsgCount = 0;
5216 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5218 object->basicSignal();
5220 qInstallMsgHandler(old);
5222 QCOMPARE(transientErrorsMsgCount, 0);
5227 // Verifies that QQmlGuard<>s used in the vmemetaobject are cleaned correctly
5228 void tst_qqmlecmascript::qtcreatorbug_1289()
5230 QQmlComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml"));
5232 QObject *o = component.create();
5235 QObject *nested = qvariant_cast<QObject *>(o->property("object"));
5236 QVERIFY(nested != 0);
5238 QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
5241 nested = qvariant_cast<QObject *>(o->property("object"));
5242 QVERIFY(nested == 0);
5244 // If the bug is present, the next line will crash
5248 // Test that we shut down without stupid warnings
5249 void tst_qqmlecmascript::noSpuriousWarningsAtShutdown()
5252 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml"));
5254 QObject *o = component.create();
5256 transientErrorsMsgCount = 0;
5257 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5261 qInstallMsgHandler(old);
5263 QCOMPARE(transientErrorsMsgCount, 0);
5268 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml"));
5270 QObject *o = component.create();
5272 transientErrorsMsgCount = 0;
5273 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5277 qInstallMsgHandler(old);
5279 QCOMPARE(transientErrorsMsgCount, 0);
5283 void tst_qqmlecmascript::canAssignNullToQObject()
5286 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml"));
5288 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5291 QVERIFY(o->objectProperty() != 0);
5293 o->setProperty("runTest", true);
5295 QVERIFY(o->objectProperty() == 0);
5301 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml"));
5303 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5306 QVERIFY(o->objectProperty() == 0);
5312 void tst_qqmlecmascript::functionAssignment_fromBinding()
5314 QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml"));
5316 QString url = component.url().toString();
5317 QString warning = url + ":4:25: Unable to assign a function to a property.";
5318 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5320 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5323 QVERIFY(!o->property("a").isValid());
5328 void tst_qqmlecmascript::functionAssignment_fromJS()
5330 QFETCH(QString, triggerProperty);
5332 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5333 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5335 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5337 QVERIFY(!o->property("a").isValid());
5339 o->setProperty("aNumber", QVariant(5));
5340 o->setProperty(triggerProperty.toUtf8().constData(), true);
5341 QCOMPARE(o->property("a"), QVariant(50));
5343 o->setProperty("aNumber", QVariant(10));
5344 QCOMPARE(o->property("a"), QVariant(100));
5349 void tst_qqmlecmascript::functionAssignment_fromJS_data()
5351 QTest::addColumn<QString>("triggerProperty");
5353 QTest::newRow("assign to property") << "assignToProperty";
5354 QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
5356 QTest::newRow("assign to value type") << "assignToValueType";
5358 QTest::newRow("use 'this'") << "assignWithThis";
5359 QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
5362 void tst_qqmlecmascript::functionAssignmentfromJS_invalid()
5364 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5365 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5367 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5369 QVERIFY(!o->property("a").isValid());
5371 o->setProperty("assignFuncWithoutReturn", true);
5372 QVERIFY(!o->property("a").isValid());
5374 QString url = component.url().toString();
5375 QString warning = url + ":67:17: Unable to assign QString to int";
5376 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5377 o->setProperty("assignWrongType", true);
5379 warning = url + ":71:29: Unable to assign QString to int";
5380 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5381 o->setProperty("assignWrongTypeToValueType", true);
5386 void tst_qqmlecmascript::eval()
5388 QQmlComponent component(&engine, testFileUrl("eval.qml"));
5390 QObject *o = component.create();
5393 QCOMPARE(o->property("test1").toBool(), true);
5394 QCOMPARE(o->property("test2").toBool(), true);
5395 QCOMPARE(o->property("test3").toBool(), true);
5396 QCOMPARE(o->property("test4").toBool(), true);
5397 QCOMPARE(o->property("test5").toBool(), true);
5402 void tst_qqmlecmascript::function()
5404 QQmlComponent component(&engine, testFileUrl("function.qml"));
5406 QObject *o = component.create();
5409 QCOMPARE(o->property("test1").toBool(), true);
5410 QCOMPARE(o->property("test2").toBool(), true);
5411 QCOMPARE(o->property("test3").toBool(), true);
5416 void tst_qqmlecmascript::functionException()
5418 // QTBUG-24037 - shouldn't crash.
5419 QString errstr = testFileUrl("v8functionException.qml").toString() + QLatin1String(":13: SyntaxError: Unexpected token ILLEGAL");
5420 QTest::ignoreMessage(QtWarningMsg, qPrintable(errstr));
5421 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Exception occurred during compilation of function: dynamicSlot()");
5422 QQmlComponent component(&engine, testFileUrl("v8functionException.qml"));
5423 QObject *o = component.create();
5425 QMetaObject::invokeMethod(o, "dynamicSlot");
5429 // Test the "Qt.include" method
5430 void tst_qqmlecmascript::include()
5432 // Non-library relative include
5434 QQmlComponent component(&engine, testFileUrl("include.qml"));
5435 QObject *o = component.create();
5438 QCOMPARE(o->property("test0").toInt(), 99);
5439 QCOMPARE(o->property("test1").toBool(), true);
5440 QCOMPARE(o->property("test2").toBool(), true);
5441 QCOMPARE(o->property("test2_1").toBool(), true);
5442 QCOMPARE(o->property("test3").toBool(), true);
5443 QCOMPARE(o->property("test3_1").toBool(), true);
5448 // Library relative include
5450 QQmlComponent component(&engine, testFileUrl("include_shared.qml"));
5451 QObject *o = component.create();
5454 QCOMPARE(o->property("test0").toInt(), 99);
5455 QCOMPARE(o->property("test1").toBool(), true);
5456 QCOMPARE(o->property("test2").toBool(), true);
5457 QCOMPARE(o->property("test2_1").toBool(), true);
5458 QCOMPARE(o->property("test3").toBool(), true);
5459 QCOMPARE(o->property("test3_1").toBool(), true);
5466 QQmlComponent component(&engine, testFileUrl("include_callback.qml"));
5467 QObject *o = component.create();
5470 QCOMPARE(o->property("test1").toBool(), true);
5471 QCOMPARE(o->property("test2").toBool(), true);
5472 QCOMPARE(o->property("test3").toBool(), true);
5473 QCOMPARE(o->property("test4").toBool(), true);
5474 QCOMPARE(o->property("test5").toBool(), true);
5475 QCOMPARE(o->property("test6").toBool(), true);
5480 // Including file with ".pragma library"
5482 QQmlComponent component(&engine, testFileUrl("include_pragma.qml"));
5483 QObject *o = component.create();
5485 QCOMPARE(o->property("test1").toInt(), 100);
5492 TestHTTPServer server(8111);
5493 QVERIFY(server.isValid());
5494 server.serveDirectory(dataDirectory());
5496 QQmlComponent component(&engine, testFileUrl("include_remote.qml"));
5497 QObject *o = component.create();
5500 QTRY_VERIFY(o->property("done").toBool() == true);
5501 QTRY_VERIFY(o->property("done2").toBool() == true);
5503 QCOMPARE(o->property("test1").toBool(), true);
5504 QCOMPARE(o->property("test2").toBool(), true);
5505 QCOMPARE(o->property("test3").toBool(), true);
5506 QCOMPARE(o->property("test4").toBool(), true);
5507 QCOMPARE(o->property("test5").toBool(), true);
5509 QCOMPARE(o->property("test6").toBool(), true);
5510 QCOMPARE(o->property("test7").toBool(), true);
5511 QCOMPARE(o->property("test8").toBool(), true);
5512 QCOMPARE(o->property("test9").toBool(), true);
5513 QCOMPARE(o->property("test10").toBool(), true);
5520 TestHTTPServer server(8111);
5521 QVERIFY(server.isValid());
5522 server.serveDirectory(dataDirectory());
5524 QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml"));
5525 QObject *o = component.create();
5528 QTRY_VERIFY(o->property("done").toBool() == true);
5530 QCOMPARE(o->property("test1").toBool(), true);
5531 QCOMPARE(o->property("test2").toBool(), true);
5532 QCOMPARE(o->property("test3").toBool(), true);
5538 void tst_qqmlecmascript::signalHandlers()
5540 QQmlComponent component(&engine, testFileUrl("signalHandlers.qml"));
5541 QObject *o = component.create();
5544 QVERIFY(o->property("count").toInt() == 0);
5545 QMetaObject::invokeMethod(o, "testSignalCall");
5546 QCOMPARE(o->property("count").toInt(), 1);
5548 QMetaObject::invokeMethod(o, "testSignalHandlerCall");
5549 QCOMPARE(o->property("count").toInt(), 1);
5550 QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
5552 QVERIFY(o->property("funcCount").toInt() == 0);
5553 QMetaObject::invokeMethod(o, "testSignalConnection");
5554 QCOMPARE(o->property("funcCount").toInt(), 1);
5556 QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
5557 QCOMPARE(o->property("funcCount").toInt(), 2);
5559 QMetaObject::invokeMethod(o, "testSignalDefined");
5560 QCOMPARE(o->property("definedResult").toBool(), true);
5562 QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
5563 QCOMPARE(o->property("definedHandlerResult").toBool(), true);
5568 void tst_qqmlecmascript::qtbug_10696()
5570 QQmlComponent component(&engine, testFileUrl("qtbug_10696.qml"));
5571 QObject *o = component.create();
5576 void tst_qqmlecmascript::qtbug_11606()
5578 QQmlComponent component(&engine, testFileUrl("qtbug_11606.qml"));
5579 QObject *o = component.create();
5581 QCOMPARE(o->property("test").toBool(), true);
5585 void tst_qqmlecmascript::qtbug_11600()
5587 QQmlComponent component(&engine, testFileUrl("qtbug_11600.qml"));
5588 QObject *o = component.create();
5590 QCOMPARE(o->property("test").toBool(), true);
5594 void tst_qqmlecmascript::qtbug_21864()
5596 QQmlComponent component(&engine, testFileUrl("qtbug_21864.qml"));
5597 QObject *o = component.create();
5599 QCOMPARE(o->property("test").toBool(), true);
5603 void tst_qqmlecmascript::rewriteMultiLineStrings()
5607 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml"));
5608 QObject *o = component.create();
5610 QTRY_COMPARE(o->property("test").toBool(), true);
5615 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings_crlf.1.qml"));
5616 QObject *o = component.create();
5622 void tst_qqmlecmascript::qobjectConnectionListExceptionHandling()
5625 QQmlComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml"));
5626 QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined");
5627 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5628 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5629 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5630 QObject *o = component.create();
5632 QCOMPARE(o->property("test").toBool(), true);
5636 // Reading and writing non-scriptable properties should fail
5637 void tst_qqmlecmascript::nonscriptable()
5639 QQmlComponent component(&engine, testFileUrl("nonscriptable.qml"));
5640 QObject *o = component.create();
5642 QCOMPARE(o->property("readOk").toBool(), true);
5643 QCOMPARE(o->property("writeOk").toBool(), true);
5647 // deleteLater() should not be callable from QML
5648 void tst_qqmlecmascript::deleteLater()
5650 QQmlComponent component(&engine, testFileUrl("deleteLater.qml"));
5651 QObject *o = component.create();
5653 QCOMPARE(o->property("test").toBool(), true);
5657 void tst_qqmlecmascript::in()
5659 QQmlComponent component(&engine, testFileUrl("in.qml"));
5660 QObject *o = component.create();
5662 QCOMPARE(o->property("test1").toBool(), true);
5663 QCOMPARE(o->property("test2").toBool(), true);
5667 void tst_qqmlecmascript::typeOf()
5669 QQmlComponent component(&engine, testFileUrl("typeOf.qml"));
5671 QObject *o = component.create();
5674 QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
5675 QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
5676 QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
5677 QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
5678 QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
5679 QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
5680 QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
5681 QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
5682 QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
5687 void tst_qqmlecmascript::qtbug_24448()
5689 QQmlComponent component(&engine, testFileUrl("qtbug_24448.qml"));
5690 QScopedPointer<QObject> o(component.create());
5692 QVERIFY(o->property("test").toBool());
5695 void tst_qqmlecmascript::sharedAttachedObject()
5697 QQmlComponent component(&engine, testFileUrl("sharedAttachedObject.qml"));
5698 QObject *o = component.create();
5700 QCOMPARE(o->property("test1").toBool(), true);
5701 QCOMPARE(o->property("test2").toBool(), true);
5706 void tst_qqmlecmascript::objectName()
5708 QQmlComponent component(&engine, testFileUrl("objectName.qml"));
5709 QObject *o = component.create();
5712 QCOMPARE(o->property("test1").toString(), QString("hello"));
5713 QCOMPARE(o->property("test2").toString(), QString("ell"));
5715 o->setObjectName("world");
5717 QCOMPARE(o->property("test1").toString(), QString("world"));
5718 QCOMPARE(o->property("test2").toString(), QString("orl"));
5723 void tst_qqmlecmascript::writeRemovesBinding()
5725 QQmlComponent component(&engine, testFileUrl("writeRemovesBinding.qml"));
5726 QObject *o = component.create();
5729 QCOMPARE(o->property("test").toBool(), true);
5734 // Test bindings assigned to alias properties actually assign to the alias' target
5735 void tst_qqmlecmascript::aliasBindingsAssignCorrectly()
5737 QQmlComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml"));
5738 QObject *o = component.create();
5741 QCOMPARE(o->property("test").toBool(), true);
5746 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
5747 void tst_qqmlecmascript::aliasBindingsOverrideTarget()
5750 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml"));
5751 QObject *o = component.create();
5754 QCOMPARE(o->property("test").toBool(), true);
5760 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml"));
5761 QObject *o = component.create();
5764 QCOMPARE(o->property("test").toBool(), true);
5770 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml"));
5771 QObject *o = component.create();
5774 QCOMPARE(o->property("test").toBool(), true);
5780 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
5781 void tst_qqmlecmascript::aliasWritesOverrideBindings()
5784 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml"));
5785 QObject *o = component.create();
5788 QCOMPARE(o->property("test").toBool(), true);
5794 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml"));
5795 QObject *o = component.create();
5798 QCOMPARE(o->property("test").toBool(), true);
5804 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml"));
5805 QObject *o = component.create();
5808 QCOMPARE(o->property("test").toBool(), true);
5814 // Allow an alais to a composite element
5816 void tst_qqmlecmascript::aliasToCompositeElement()
5818 QQmlComponent component(&engine, testFileUrl("aliasToCompositeElement.qml"));
5820 QObject *object = component.create();
5821 QVERIFY(object != 0);
5826 void tst_qqmlecmascript::qtbug_20344()
5828 QQmlComponent component(&engine, testFileUrl("qtbug_20344.qml"));
5830 QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
5831 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5833 QObject *object = component.create();
5834 QVERIFY(object != 0);
5839 void tst_qqmlecmascript::revisionErrors()
5842 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml"));
5843 QString url = component.url().toString();
5845 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
5846 QString warning2 = url + ":11: ReferenceError: prop2 is not defined";
5847 QString warning3 = url + ":13: ReferenceError: method2 is not defined";
5849 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
5850 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
5851 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
5852 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5853 QVERIFY(object != 0);
5857 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml"));
5858 QString url = component.url().toString();
5860 // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
5861 // method2, prop2 from MyRevisionedClass not available
5862 // method4, prop4 from MyRevisionedSubclass not available
5863 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
5864 QString warning2 = url + ":14: ReferenceError: prop2 is not defined";
5865 QString warning3 = url + ":10: ReferenceError: prop4 is not defined";
5866 QString warning4 = url + ":16: ReferenceError: prop4 is not defined";
5867 QString warning5 = url + ":20: ReferenceError: method2 is not defined";
5869 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
5870 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
5871 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
5872 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
5873 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
5874 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5875 QVERIFY(object != 0);
5879 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml"));
5880 QString url = component.url().toString();
5882 // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
5883 // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
5884 QString warning1 = url + ":30: ReferenceError: methodD is not defined";
5885 QString warning2 = url + ":10: ReferenceError: propD is not defined";
5886 QString warning3 = url + ":20: ReferenceError: propD is not defined";
5887 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
5888 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
5889 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
5890 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5891 QVERIFY(object != 0);
5896 void tst_qqmlecmascript::revision()
5899 QQmlComponent component(&engine, testFileUrl("metaobjectRevision.qml"));
5900 QString url = component.url().toString();
5902 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5903 QVERIFY(object != 0);
5907 QQmlComponent component(&engine, testFileUrl("metaobjectRevision2.qml"));
5908 QString url = component.url().toString();
5910 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5911 QVERIFY(object != 0);
5915 QQmlComponent component(&engine, testFileUrl("metaobjectRevision3.qml"));
5916 QString url = component.url().toString();
5918 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5919 QVERIFY(object != 0);
5922 // Test that non-root classes can resolve revisioned methods
5924 QQmlComponent component(&engine, testFileUrl("metaobjectRevision4.qml"));
5926 QObject *object = component.create();
5927 QVERIFY(object != 0);
5928 QCOMPARE(object->property("test").toReal(), 11.);
5933 void tst_qqmlecmascript::realToInt()
5935 QQmlComponent component(&engine, testFileUrl("realToInt.qml"));
5936 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
5937 QVERIFY(object != 0);
5939 QMetaObject::invokeMethod(object, "test1");
5940 QCOMPARE(object->value(), int(4));
5941 QMetaObject::invokeMethod(object, "test2");
5942 QCOMPARE(object->value(), int(8));
5945 void tst_qqmlecmascript::urlProperty()
5948 QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml"));
5949 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
5950 QVERIFY(object != 0);
5951 object->setStringProperty("http://qt-project.org");
5952 QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
5953 QCOMPARE(object->intProperty(), 123);
5954 QCOMPARE(object->value(), 1);
5955 QCOMPARE(object->property("result").toBool(), true);
5959 void tst_qqmlecmascript::urlPropertyWithEncoding()
5962 QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml"));
5963 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
5964 QVERIFY(object != 0);
5965 object->setStringProperty("http://qt-project.org");
5967 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
5968 QCOMPARE(object->urlProperty(), encoded);
5969 QCOMPARE(object->value(), 0); // Interpreting URL as string yields canonicalised version
5970 QCOMPARE(object->property("result").toBool(), true);
5974 void tst_qqmlecmascript::urlListPropertyWithEncoding()
5977 QQmlComponent component(&engine, testFileUrl("urlListProperty.qml"));
5978 QObject *object = component.create();
5979 QVERIFY(object != 0);
5980 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
5981 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
5982 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
5983 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
5984 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0);
5986 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
5987 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded));
5988 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded));
5989 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << encoded << encoded));
5990 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << encoded << encoded));
5995 void tst_qqmlecmascript::dynamicString()
5997 QQmlComponent component(&engine, testFileUrl("dynamicString.qml"));
5998 QObject *object = component.create();
5999 QVERIFY(object != 0);
6000 QCOMPARE(object->property("stringProperty").toString(),
6001 QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
6004 void tst_qqmlecmascript::automaticSemicolon()
6006 QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml"));
6007 QObject *object = component.create();
6008 QVERIFY(object != 0);
6011 void tst_qqmlecmascript::unaryExpression()
6013 QQmlComponent component(&engine, testFileUrl("unaryExpression.qml"));
6014 QObject *object = component.create();
6015 QVERIFY(object != 0);
6018 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
6019 void tst_qqmlecmascript::doubleEvaluate()
6021 QQmlComponent component(&engine, testFileUrl("doubleEvaluate.qml"));
6022 QObject *object = component.create();
6023 QVERIFY(object != 0);
6024 WriteCounter *wc = qobject_cast<WriteCounter *>(object);
6026 QCOMPARE(wc->count(), 1);
6028 wc->setProperty("x", 9);
6030 QCOMPARE(wc->count(), 2);
6035 static QStringList messages;
6036 static void captureMsgHandler(QtMsgType, const char *msg)
6038 messages.append(QLatin1String(msg));
6041 void tst_qqmlecmascript::nonNotifyable()
6043 QV4Compiler::enableV4(false);
6044 QQmlComponent component(&engine, testFileUrl("nonNotifyable.qml"));
6045 QV4Compiler::enableV4(true);
6047 QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
6049 QObject *object = component.create();
6050 qInstallMsgHandler(old);
6052 QVERIFY(object != 0);
6054 QString expected1 = QLatin1String("QQmlExpression: Expression ") +
6055 component.url().toString() +
6056 QLatin1String(":5 depends on non-NOTIFYable properties:");
6057 QString expected2 = QLatin1String(" ") +
6058 QLatin1String(object->metaObject()->className()) +
6059 QLatin1String("::value");
6061 QCOMPARE(messages.length(), 2);
6062 QCOMPARE(messages.at(0), expected1);
6063 QCOMPARE(messages.at(1), expected2);
6068 void tst_qqmlecmascript::forInLoop()
6070 QQmlComponent component(&engine, testFileUrl("forInLoop.qml"));
6071 QObject *object = component.create();
6072 QVERIFY(object != 0);
6074 QMetaObject::invokeMethod(object, "listProperty");
6076 QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
6077 QCOMPARE(r.size(), 3);
6078 QCOMPARE(r[0],QLatin1String("0=obj1"));
6079 QCOMPARE(r[1],QLatin1String("1=obj2"));
6080 QCOMPARE(r[2],QLatin1String("2=obj3"));
6082 //TODO: should test for in loop for other objects (such as QObjects) as well.
6087 // An object the binding depends on is deleted while the binding is still running
6088 void tst_qqmlecmascript::deleteWhileBindingRunning()
6090 QQmlComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml"));
6091 QObject *object = component.create();
6092 QVERIFY(object != 0);
6096 void tst_qqmlecmascript::qtbug_22679()
6099 object.setStringProperty(QLatin1String("Please work correctly"));
6100 engine.rootContext()->setContextProperty("contextProp", &object);
6102 QQmlComponent component(&engine, testFileUrl("qtbug_22679.qml"));
6103 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6104 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6106 QObject *o = component.create();
6108 QCOMPARE(warningsSpy.count(), 0);
6112 void tst_qqmlecmascript::qtbug_22843_data()
6114 QTest::addColumn<bool>("library");
6116 QTest::newRow("without .pragma library") << false;
6117 QTest::newRow("with .pragma library") << true;
6120 void tst_qqmlecmascript::qtbug_22843()
6122 QFETCH(bool, library);
6124 QString fileName("qtbug_22843");
6126 fileName += QLatin1String(".library");
6127 fileName += QLatin1String(".qml");
6129 QQmlComponent component(&engine, testFileUrl(fileName));
6130 QString url = component.url().toString();
6131 QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )");
6132 QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'");
6134 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6135 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6136 for (int x = 0; x < 3; ++x) {
6137 warningsSpy.clear();
6138 // For libraries, only the first import attempt should produce a
6139 // SyntaxError warning; subsequent component creation should not
6140 // attempt to reload the script.
6141 bool expectSyntaxError = !library || (x == 0);
6142 if (expectSyntaxError)
6143 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
6144 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
6145 QObject *object = component.create();
6146 QVERIFY(object != 0);
6147 QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
6153 void tst_qqmlecmascript::switchStatement()
6156 QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml"));
6157 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6158 QVERIFY(object != 0);
6160 // `object->value()' is the number of executed statements
6162 object->setStringProperty("A");
6163 QCOMPARE(object->value(), 5);
6165 object->setStringProperty("S");
6166 QCOMPARE(object->value(), 3);
6168 object->setStringProperty("D");
6169 QCOMPARE(object->value(), 3);
6171 object->setStringProperty("F");
6172 QCOMPARE(object->value(), 4);
6174 object->setStringProperty("something else");
6175 QCOMPARE(object->value(), 1);
6179 QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml"));
6180 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6181 QVERIFY(object != 0);
6183 // `object->value()' is the number of executed statements
6185 object->setStringProperty("A");
6186 QCOMPARE(object->value(), 5);
6188 object->setStringProperty("S");
6189 QCOMPARE(object->value(), 3);
6191 object->setStringProperty("D");
6192 QCOMPARE(object->value(), 3);
6194 object->setStringProperty("F");
6195 QCOMPARE(object->value(), 3);
6197 object->setStringProperty("something else");
6198 QCOMPARE(object->value(), 4);
6202 QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml"));
6203 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6204 QVERIFY(object != 0);
6206 // `object->value()' is the number of executed statements
6208 object->setStringProperty("A");
6209 QCOMPARE(object->value(), 5);
6211 object->setStringProperty("S");
6212 QCOMPARE(object->value(), 3);
6214 object->setStringProperty("D");
6215 QCOMPARE(object->value(), 3);
6217 object->setStringProperty("F");
6218 QCOMPARE(object->value(), 3);
6220 object->setStringProperty("something else");
6221 QCOMPARE(object->value(), 6);
6225 QQmlComponent component(&engine, testFileUrl("switchStatement.4.qml"));
6227 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
6228 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6230 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6231 QVERIFY(object != 0);
6233 // `object->value()' is the number of executed statements
6235 object->setStringProperty("A");
6236 QCOMPARE(object->value(), 5);
6238 object->setStringProperty("S");
6239 QCOMPARE(object->value(), 3);
6241 object->setStringProperty("D");
6242 QCOMPARE(object->value(), 3);
6244 object->setStringProperty("F");
6245 QCOMPARE(object->value(), 3);
6247 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6249 object->setStringProperty("something else");
6253 QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml"));
6254 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6255 QVERIFY(object != 0);
6257 // `object->value()' is the number of executed statements
6259 object->setStringProperty("A");
6260 QCOMPARE(object->value(), 1);
6262 object->setStringProperty("S");
6263 QCOMPARE(object->value(), 1);
6265 object->setStringProperty("D");
6266 QCOMPARE(object->value(), 1);
6268 object->setStringProperty("F");
6269 QCOMPARE(object->value(), 1);
6271 object->setStringProperty("something else");
6272 QCOMPARE(object->value(), 1);
6276 QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml"));
6277 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6278 QVERIFY(object != 0);
6280 // `object->value()' is the number of executed statements
6282 object->setStringProperty("A");
6283 QCOMPARE(object->value(), 123);
6285 object->setStringProperty("S");
6286 QCOMPARE(object->value(), 123);
6288 object->setStringProperty("D");
6289 QCOMPARE(object->value(), 321);
6291 object->setStringProperty("F");
6292 QCOMPARE(object->value(), 321);
6294 object->setStringProperty("something else");
6295 QCOMPARE(object->value(), 0);
6299 void tst_qqmlecmascript::withStatement()
6302 QQmlComponent component(&engine, testFileUrl("withStatement.1.qml"));
6303 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6304 QVERIFY(object != 0);
6306 QCOMPARE(object->value(), 123);
6310 void tst_qqmlecmascript::tryStatement()
6313 QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml"));
6314 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6315 QVERIFY(object != 0);
6317 QCOMPARE(object->value(), 123);
6321 QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml"));
6322 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6323 QVERIFY(object != 0);
6325 QCOMPARE(object->value(), 321);
6329 QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml"));
6330 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6331 QVERIFY(object != 0);
6333 QCOMPARE(object->value(), 1);
6337 QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml"));
6338 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6339 QVERIFY(object != 0);
6341 QCOMPARE(object->value(), 1);
6345 class CppInvokableWithQObjectDerived : public QObject
6349 CppInvokableWithQObjectDerived() {}
6350 ~CppInvokableWithQObjectDerived() {}
6352 Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data)
6354 MyQmlObject *obj = new MyQmlObject();
6355 obj->setStringProperty(data);
6359 Q_INVOKABLE QString getStringProperty(MyQmlObject *obj)
6361 return obj->stringProperty();
6365 void tst_qqmlecmascript::invokableWithQObjectDerived()
6367 CppInvokableWithQObjectDerived invokable;
6371 engine.rootContext()->setContextProperty("invokable", &invokable);
6373 QQmlComponent component(&engine, testFileUrl("qobjectDerivedArgument.qml"));
6375 QObject *object = component.create();
6377 QVERIFY(object != 0);
6378 QVERIFY(object->property("result").value<bool>() == true);
6384 void tst_qqmlecmascript::realTypePrecision()
6386 // Properties and signal parameters of type real should have double precision.
6387 QQmlComponent component(&engine, testFileUrl("realTypePrecision.qml"));
6388 QScopedPointer<QObject> object(component.create());
6389 QVERIFY(object != 0);
6390 QCOMPARE(object->property("test").toDouble(), 1234567890.);
6391 QCOMPARE(object->property("test2").toDouble(), 1234567890.);
6392 QCOMPARE(object->property("test3").toDouble(), 1234567890.);
6393 QCOMPARE(object->property("test4").toDouble(), 1234567890.);
6394 QCOMPARE(object->property("test5").toDouble(), 1234567890.);
6395 QCOMPARE(object->property("test6").toDouble(), 1234567890.*2);
6398 void tst_qqmlecmascript::registeredFlagMethod()
6401 QQmlComponent component(&engine, testFileUrl("registeredFlagMethod.qml"));
6402 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6403 QVERIFY(object != 0);
6405 QCOMPARE(object->buttons(), 0);
6406 emit object->basicSignal();
6407 QCOMPARE(object->buttons(), Qt::RightButton);
6412 QTEST_MAIN(tst_qqmlecmascript)
6414 #include "tst_qqmlecmascript.moc"