1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the test suite of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
41 #include <QtTest/QtTest>
42 #include <QtDeclarative/qdeclarativecomponent.h>
43 #include <QtDeclarative/qdeclarativeengine.h>
44 #include <QtDeclarative/qdeclarativeexpression.h>
45 #include <QtDeclarative/qdeclarativecontext.h>
46 #include <QtCore/qfileinfo.h>
47 #include <QtCore/qdebug.h>
48 #include <QtDeclarative/private/qdeclarativeguard_p.h>
49 #include <QtCore/qdir.h>
50 #include <QtCore/qnumeric.h>
51 #include <private/qdeclarativeengine_p.h>
52 #include <private/qv8gccallback_p.h>
53 #include <private/qdeclarativevmemetaobject_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
64 inline QUrl TEST_FILE(const QString &filename)
66 return QUrl::fromLocalFile(TESTDATA(filename));
69 inline QUrl TEST_FILE(const char *filename)
71 return TEST_FILE(QLatin1String(filename));
74 class tst_qdeclarativeecmascript : public QObject
78 tst_qdeclarativeecmascript() {}
82 void assignBasicTypes();
83 void idShortcutInvalidates();
84 void boolPropertiesEvaluateAsBool();
86 void signalAssignment();
88 void basicExpressions();
89 void basicExpressions_data();
90 void arrayExpressions();
91 void contextPropertiesTriggerReeval();
92 void objectPropertiesTriggerReeval();
93 void deferredProperties();
94 void deferredPropertiesErrors();
95 void extensionObjects();
96 void overrideExtensionProperties();
97 void attachedProperties();
99 void valueTypeFunctions();
100 void constantsOverrideBindings();
101 void outerBindingOverridesInnerBinding();
102 void aliasPropertyAndBinding();
103 void aliasPropertyReset();
104 void nonExistentAttachedObject();
107 void signalParameterTypes();
108 void objectsCompareAsEqual();
109 void dynamicCreation_data();
110 void dynamicCreation();
111 void dynamicDestruction();
112 void objectToString();
113 void objectHasOwnProperty();
114 void selfDeletingBinding();
115 void extendedObjectPropertyLookup();
117 void functionErrors();
118 void propertyAssignmentErrors();
119 void signalTriggeredBindings();
120 void listProperties();
121 void exceptionClearsOnReeval();
122 void exceptionSlotProducesWarning();
123 void exceptionBindingProducesWarning();
124 void transientErrors();
125 void shutdownErrors();
126 void compositePropertyType();
128 void undefinedResetsProperty();
129 void listToVariant();
130 void listAssignment();
131 void multiEngineObject();
132 void deletedObject();
133 void attachedPropertyScope();
134 void scriptConnect();
135 void scriptDisconnect();
137 void cppOwnershipReturnValue();
138 void ownershipCustomReturnValue();
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 moduleApi_data();
151 void importScripts_data();
152 void importScripts();
153 void scarceResources();
154 void propertyChangeSlots();
155 void propertyVar_data();
157 void propertyVarCpp();
158 void propertyVarOwnership();
159 void propertyVarImplicitOwnership();
160 void propertyVarReparent();
161 void propertyVarReparentNullContext();
162 void propertyVarCircular();
163 void propertyVarCircular2();
164 void propertyVarInheritance();
165 void propertyVarInheritance2();
166 void elementAssign();
167 void objectPassThroughSignals();
168 void objectConversion();
169 void booleanConversion();
170 void handleReferenceManagement();
175 void dynamicCreationCrash();
176 void dynamicCreationOwnership();
178 void nullObjectBinding();
179 void deletedEngine();
180 void libraryScriptAssert();
181 void variantsAssignedUndefined();
183 void qtcreatorbug_1289();
184 void noSpuriousWarningsAtShutdown();
185 void canAssignNullToQObject();
186 void functionAssignment_fromBinding();
187 void functionAssignment_fromJS();
188 void functionAssignment_fromJS_data();
189 void functionAssignmentfromJS_invalid();
195 void nonscriptable();
198 void sharedAttachedObject();
200 void writeRemovesBinding();
201 void aliasBindingsAssignCorrectly();
202 void aliasBindingsOverrideTarget();
203 void aliasWritesOverrideBindings();
204 void aliasToCompositeElement();
206 void dynamicString();
208 void signalHandlers();
209 void doubleEvaluate();
211 void callQtInvokables();
212 void invokableObjectArg();
213 void invokableObjectRet();
215 void revisionErrors();
218 void automaticSemicolon();
221 static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
222 QDeclarativeEngine engine;
225 void tst_qdeclarativeecmascript::initTestCase() { registerTypes(); }
227 void tst_qdeclarativeecmascript::assignBasicTypes()
230 QDeclarativeComponent component(&engine, TEST_FILE("assignBasicTypes.qml"));
231 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
232 QVERIFY(object != 0);
233 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
234 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
235 QCOMPARE(object->stringProperty(), QString("Hello World!"));
236 QCOMPARE(object->uintProperty(), uint(10));
237 QCOMPARE(object->intProperty(), -19);
238 QCOMPARE((float)object->realProperty(), float(23.2));
239 QCOMPARE((float)object->doubleProperty(), float(-19.75));
240 QCOMPARE((float)object->floatProperty(), float(8.5));
241 QCOMPARE(object->colorProperty(), QColor("red"));
242 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
243 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
244 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1)));
245 QCOMPARE(object->pointProperty(), QPoint(99,13));
246 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
247 QCOMPARE(object->sizeProperty(), QSize(99, 13));
248 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
249 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
250 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
251 QCOMPARE(object->boolProperty(), true);
252 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
253 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
254 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
258 QDeclarativeComponent component(&engine, TEST_FILE("assignBasicTypes.2.qml"));
259 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
260 QVERIFY(object != 0);
261 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
262 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
263 QCOMPARE(object->stringProperty(), QString("Hello World!"));
264 QCOMPARE(object->uintProperty(), uint(10));
265 QCOMPARE(object->intProperty(), -19);
266 QCOMPARE((float)object->realProperty(), float(23.2));
267 QCOMPARE((float)object->doubleProperty(), float(-19.75));
268 QCOMPARE((float)object->floatProperty(), float(8.5));
269 QCOMPARE(object->colorProperty(), QColor("red"));
270 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
271 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
272 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1)));
273 QCOMPARE(object->pointProperty(), QPoint(99,13));
274 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
275 QCOMPARE(object->sizeProperty(), QSize(99, 13));
276 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
277 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
278 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
279 QCOMPARE(object->boolProperty(), true);
280 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
281 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
282 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
287 void tst_qdeclarativeecmascript::idShortcutInvalidates()
290 QDeclarativeComponent component(&engine, TEST_FILE("idShortcutInvalidates.qml"));
291 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
292 QVERIFY(object != 0);
293 QVERIFY(object->objectProperty() != 0);
294 delete object->objectProperty();
295 QVERIFY(object->objectProperty() == 0);
300 QDeclarativeComponent component(&engine, TEST_FILE("idShortcutInvalidates.1.qml"));
301 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
302 QVERIFY(object != 0);
303 QVERIFY(object->objectProperty() != 0);
304 delete object->objectProperty();
305 QVERIFY(object->objectProperty() == 0);
310 void tst_qdeclarativeecmascript::boolPropertiesEvaluateAsBool()
313 QDeclarativeComponent component(&engine, TEST_FILE("boolPropertiesEvaluateAsBool.1.qml"));
314 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
315 QVERIFY(object != 0);
316 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
320 QDeclarativeComponent component(&engine, TEST_FILE("boolPropertiesEvaluateAsBool.2.qml"));
321 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
322 QVERIFY(object != 0);
323 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
328 void tst_qdeclarativeecmascript::signalAssignment()
331 QDeclarativeComponent component(&engine, TEST_FILE("signalAssignment.1.qml"));
332 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
333 QVERIFY(object != 0);
334 QCOMPARE(object->string(), QString());
335 emit object->basicSignal();
336 QCOMPARE(object->string(), QString("pass"));
341 QDeclarativeComponent component(&engine, TEST_FILE("signalAssignment.2.qml"));
342 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
343 QVERIFY(object != 0);
344 QCOMPARE(object->string(), QString());
345 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
346 QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
351 void tst_qdeclarativeecmascript::methods()
354 QDeclarativeComponent component(&engine, TEST_FILE("methods.1.qml"));
355 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
356 QVERIFY(object != 0);
357 QCOMPARE(object->methodCalled(), false);
358 QCOMPARE(object->methodIntCalled(), false);
359 emit object->basicSignal();
360 QCOMPARE(object->methodCalled(), true);
361 QCOMPARE(object->methodIntCalled(), false);
366 QDeclarativeComponent component(&engine, TEST_FILE("methods.2.qml"));
367 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
368 QVERIFY(object != 0);
369 QCOMPARE(object->methodCalled(), false);
370 QCOMPARE(object->methodIntCalled(), false);
371 emit object->basicSignal();
372 QCOMPARE(object->methodCalled(), false);
373 QCOMPARE(object->methodIntCalled(), true);
378 QDeclarativeComponent component(&engine, TEST_FILE("methods.3.qml"));
379 QObject *object = component.create();
380 QVERIFY(object != 0);
381 QCOMPARE(object->property("test").toInt(), 19);
386 QDeclarativeComponent component(&engine, TEST_FILE("methods.4.qml"));
387 QObject *object = component.create();
388 QVERIFY(object != 0);
389 QCOMPARE(object->property("test").toInt(), 19);
390 QCOMPARE(object->property("test2").toInt(), 17);
391 QCOMPARE(object->property("test3").toInt(), 16);
396 QDeclarativeComponent component(&engine, TEST_FILE("methods.5.qml"));
397 QObject *object = component.create();
398 QVERIFY(object != 0);
399 QCOMPARE(object->property("test").toInt(), 9);
404 void tst_qdeclarativeecmascript::bindingLoop()
406 QDeclarativeComponent component(&engine, TEST_FILE("bindingLoop.qml"));
407 QString warning = component.url().toString() + ":5:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\"";
408 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
409 QObject *object = component.create();
410 QVERIFY(object != 0);
414 void tst_qdeclarativeecmascript::basicExpressions_data()
416 QTest::addColumn<QString>("expression");
417 QTest::addColumn<QVariant>("result");
418 QTest::addColumn<bool>("nest");
420 QTest::newRow("Syntax error (self test)") << "{console.log({'a':1'}.a)}" << QVariant() << false;
421 QTest::newRow("Context property") << "a" << QVariant(1944) << false;
422 QTest::newRow("Context property") << "a" << QVariant(1944) << true;
423 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << false;
424 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << true;
425 QTest::newRow("Overridden context property") << "b" << QVariant("Milk") << false;
426 QTest::newRow("Overridden context property") << "b" << QVariant("Cow") << true;
427 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << false;
428 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << true;
429 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object2") << false;
430 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object3") << true;
431 QTest::newRow("Default object property") << "horseLegs" << QVariant(4) << false;
432 QTest::newRow("Default object property") << "antLegs" << QVariant(6) << false;
433 QTest::newRow("Default object property") << "emuLegs" << QVariant(2) << false;
434 QTest::newRow("Nested default object property") << "horseLegs" << QVariant(4) << true;
435 QTest::newRow("Nested default object property") << "antLegs" << QVariant(7) << true;
436 QTest::newRow("Nested default object property") << "emuLegs" << QVariant(2) << true;
437 QTest::newRow("Nested default object property") << "humanLegs" << QVariant(2) << true;
438 QTest::newRow("Context property override default object property") << "millipedeLegs" << QVariant(100) << true;
441 void tst_qdeclarativeecmascript::basicExpressions()
443 QFETCH(QString, expression);
444 QFETCH(QVariant, result);
450 MyDefaultObject1 default1;
451 MyDefaultObject3 default3;
452 object1.setStringProperty("Object1");
453 object2.setStringProperty("Object2");
454 object3.setStringProperty("Object3");
456 QDeclarativeContext context(engine.rootContext());
457 QDeclarativeContext nestedContext(&context);
459 context.setContextObject(&default1);
460 context.setContextProperty("a", QVariant(1944));
461 context.setContextProperty("b", QVariant("Milk"));
462 context.setContextProperty("object", &object1);
463 context.setContextProperty("objectOverride", &object2);
464 nestedContext.setContextObject(&default3);
465 nestedContext.setContextProperty("b", QVariant("Cow"));
466 nestedContext.setContextProperty("objectOverride", &object3);
467 nestedContext.setContextProperty("millipedeLegs", QVariant(100));
469 MyExpression expr(nest?&nestedContext:&context, expression);
470 QCOMPARE(expr.evaluate(), result);
473 void tst_qdeclarativeecmascript::arrayExpressions()
479 QDeclarativeContext context(engine.rootContext());
480 context.setContextProperty("a", &obj1);
481 context.setContextProperty("b", &obj2);
482 context.setContextProperty("c", &obj3);
484 MyExpression expr(&context, "[a, b, c, 10]");
485 QVariant result = expr.evaluate();
486 QCOMPARE(result.userType(), qMetaTypeId<QList<QObject *> >());
487 QList<QObject *> list = qvariant_cast<QList<QObject *> >(result);
488 QCOMPARE(list.count(), 4);
489 QCOMPARE(list.at(0), &obj1);
490 QCOMPARE(list.at(1), &obj2);
491 QCOMPARE(list.at(2), &obj3);
492 QCOMPARE(list.at(3), (QObject *)0);
495 // Tests that modifying a context property will reevaluate expressions
496 void tst_qdeclarativeecmascript::contextPropertiesTriggerReeval()
498 QDeclarativeContext context(engine.rootContext());
501 MyQmlObject *object3 = new MyQmlObject;
503 object1.setStringProperty("Hello");
504 object2.setStringProperty("World");
506 context.setContextProperty("testProp", QVariant(1));
507 context.setContextProperty("testObj", &object1);
508 context.setContextProperty("testObj2", object3);
511 MyExpression expr(&context, "testProp + 1");
512 QCOMPARE(expr.changed, false);
513 QCOMPARE(expr.evaluate(), QVariant(2));
515 context.setContextProperty("testProp", QVariant(2));
516 QCOMPARE(expr.changed, true);
517 QCOMPARE(expr.evaluate(), QVariant(3));
521 MyExpression expr(&context, "testProp + testProp + testProp");
522 QCOMPARE(expr.changed, false);
523 QCOMPARE(expr.evaluate(), QVariant(6));
525 context.setContextProperty("testProp", QVariant(4));
526 QCOMPARE(expr.changed, true);
527 QCOMPARE(expr.evaluate(), QVariant(12));
531 MyExpression expr(&context, "testObj.stringProperty");
532 QCOMPARE(expr.changed, false);
533 QCOMPARE(expr.evaluate(), QVariant("Hello"));
535 context.setContextProperty("testObj", &object2);
536 QCOMPARE(expr.changed, true);
537 QCOMPARE(expr.evaluate(), QVariant("World"));
541 MyExpression expr(&context, "testObj.stringProperty /**/");
542 QCOMPARE(expr.changed, false);
543 QCOMPARE(expr.evaluate(), QVariant("World"));
545 context.setContextProperty("testObj", &object1);
546 QCOMPARE(expr.changed, true);
547 QCOMPARE(expr.evaluate(), QVariant("Hello"));
551 MyExpression expr(&context, "testObj2");
552 QCOMPARE(expr.changed, false);
553 QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3));
559 void tst_qdeclarativeecmascript::objectPropertiesTriggerReeval()
561 QDeclarativeContext context(engine.rootContext());
565 context.setContextProperty("testObj", &object1);
567 object1.setStringProperty(QLatin1String("Hello"));
568 object2.setStringProperty(QLatin1String("Dog"));
569 object3.setStringProperty(QLatin1String("Cat"));
572 MyExpression expr(&context, "testObj.stringProperty");
573 QCOMPARE(expr.changed, false);
574 QCOMPARE(expr.evaluate(), QVariant("Hello"));
576 object1.setStringProperty(QLatin1String("World"));
577 QCOMPARE(expr.changed, true);
578 QCOMPARE(expr.evaluate(), QVariant("World"));
582 MyExpression expr(&context, "testObj.objectProperty.stringProperty");
583 QCOMPARE(expr.changed, false);
584 QCOMPARE(expr.evaluate(), QVariant());
586 object1.setObjectProperty(&object2);
587 QCOMPARE(expr.changed, true);
588 expr.changed = false;
589 QCOMPARE(expr.evaluate(), QVariant("Dog"));
591 object1.setObjectProperty(&object3);
592 QCOMPARE(expr.changed, true);
593 expr.changed = false;
594 QCOMPARE(expr.evaluate(), QVariant("Cat"));
596 object1.setObjectProperty(0);
597 QCOMPARE(expr.changed, true);
598 expr.changed = false;
599 QCOMPARE(expr.evaluate(), QVariant());
601 object1.setObjectProperty(&object3);
602 QCOMPARE(expr.changed, true);
603 expr.changed = false;
604 QCOMPARE(expr.evaluate(), QVariant("Cat"));
606 object3.setStringProperty("Donkey");
607 QCOMPARE(expr.changed, true);
608 expr.changed = false;
609 QCOMPARE(expr.evaluate(), QVariant("Donkey"));
613 void tst_qdeclarativeecmascript::deferredProperties()
615 QDeclarativeComponent component(&engine, TEST_FILE("deferredProperties.qml"));
616 MyDeferredObject *object =
617 qobject_cast<MyDeferredObject *>(component.create());
618 QVERIFY(object != 0);
619 QCOMPARE(object->value(), 0);
620 QVERIFY(object->objectProperty() == 0);
621 QVERIFY(object->objectProperty2() != 0);
622 qmlExecuteDeferred(object);
623 QCOMPARE(object->value(), 10);
624 QVERIFY(object->objectProperty() != 0);
625 MyQmlObject *qmlObject =
626 qobject_cast<MyQmlObject *>(object->objectProperty());
627 QVERIFY(qmlObject != 0);
628 QCOMPARE(qmlObject->value(), 10);
629 object->setValue(19);
630 QCOMPARE(qmlObject->value(), 19);
635 // Check errors on deferred properties are correctly emitted
636 void tst_qdeclarativeecmascript::deferredPropertiesErrors()
638 QDeclarativeComponent component(&engine, TEST_FILE("deferredPropertiesErrors.qml"));
639 MyDeferredObject *object =
640 qobject_cast<MyDeferredObject *>(component.create());
641 QVERIFY(object != 0);
642 QCOMPARE(object->value(), 0);
643 QVERIFY(object->objectProperty() == 0);
644 QVERIFY(object->objectProperty2() == 0);
646 QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject*";
647 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
649 qmlExecuteDeferred(object);
654 void tst_qdeclarativeecmascript::extensionObjects()
656 QDeclarativeComponent component(&engine, TEST_FILE("extensionObjects.qml"));
657 MyExtendedObject *object =
658 qobject_cast<MyExtendedObject *>(component.create());
659 QVERIFY(object != 0);
660 QCOMPARE(object->baseProperty(), 13);
661 QCOMPARE(object->coreProperty(), 9);
662 object->setProperty("extendedProperty", QVariant(11));
663 object->setProperty("baseExtendedProperty", QVariant(92));
664 QCOMPARE(object->coreProperty(), 11);
665 QCOMPARE(object->baseProperty(), 92);
667 MyExtendedObject *nested = qobject_cast<MyExtendedObject*>(qvariant_cast<QObject *>(object->property("nested")));
669 QCOMPARE(nested->baseProperty(), 13);
670 QCOMPARE(nested->coreProperty(), 9);
671 nested->setProperty("extendedProperty", QVariant(11));
672 nested->setProperty("baseExtendedProperty", QVariant(92));
673 QCOMPARE(nested->coreProperty(), 11);
674 QCOMPARE(nested->baseProperty(), 92);
679 void tst_qdeclarativeecmascript::overrideExtensionProperties()
681 QDeclarativeComponent component(&engine, TEST_FILE("extensionObjectsPropertyOverride.qml"));
682 OverrideDefaultPropertyObject *object =
683 qobject_cast<OverrideDefaultPropertyObject *>(component.create());
684 QVERIFY(object != 0);
685 QVERIFY(object->secondProperty() != 0);
686 QVERIFY(object->firstProperty() == 0);
691 void tst_qdeclarativeecmascript::attachedProperties()
694 QDeclarativeComponent component(&engine, TEST_FILE("attachedProperty.qml"));
695 QObject *object = component.create();
696 QVERIFY(object != 0);
697 QCOMPARE(object->property("a").toInt(), 19);
698 QCOMPARE(object->property("b").toInt(), 19);
699 QCOMPARE(object->property("c").toInt(), 19);
700 QCOMPARE(object->property("d").toInt(), 19);
705 QDeclarativeComponent component(&engine, TEST_FILE("attachedProperty.2.qml"));
706 QObject *object = component.create();
707 QVERIFY(object != 0);
708 QCOMPARE(object->property("a").toInt(), 26);
709 QCOMPARE(object->property("b").toInt(), 26);
710 QCOMPARE(object->property("c").toInt(), 26);
711 QCOMPARE(object->property("d").toInt(), 26);
717 QDeclarativeComponent component(&engine, TEST_FILE("writeAttachedProperty.qml"));
718 QObject *object = component.create();
719 QVERIFY(object != 0);
721 QMetaObject::invokeMethod(object, "writeValue2");
723 MyQmlAttachedObject *attached =
724 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
725 QVERIFY(attached != 0);
727 QCOMPARE(attached->value2(), 9);
732 void tst_qdeclarativeecmascript::enums()
736 QDeclarativeComponent component(&engine, TEST_FILE("enums.1.qml"));
737 QObject *object = component.create();
738 QVERIFY(object != 0);
740 QCOMPARE(object->property("a").toInt(), 0);
741 QCOMPARE(object->property("b").toInt(), 1);
742 QCOMPARE(object->property("c").toInt(), 2);
743 QCOMPARE(object->property("d").toInt(), 3);
744 QCOMPARE(object->property("e").toInt(), 0);
745 QCOMPARE(object->property("f").toInt(), 1);
746 QCOMPARE(object->property("g").toInt(), 2);
747 QCOMPARE(object->property("h").toInt(), 3);
748 QCOMPARE(object->property("i").toInt(), 19);
749 QCOMPARE(object->property("j").toInt(), 19);
753 // Non-existent enums
755 QDeclarativeComponent component(&engine, TEST_FILE("enums.2.qml"));
757 QString warning1 = component.url().toString() + ":5: Unable to assign [undefined] to int";
758 QString warning2 = component.url().toString() + ":6: Unable to assign [undefined] to int";
759 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
760 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
762 QObject *object = component.create();
763 QVERIFY(object != 0);
764 QCOMPARE(object->property("a").toInt(), 0);
765 QCOMPARE(object->property("b").toInt(), 0);
771 void tst_qdeclarativeecmascript::valueTypeFunctions()
773 QDeclarativeComponent component(&engine, TEST_FILE("valueTypeFunctions.qml"));
774 MyTypeObject *obj = qobject_cast<MyTypeObject*>(component.create());
776 QCOMPARE(obj->rectProperty(), QRect(0,0,100,100));
777 QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5));
783 Tests that writing a constant to a property with a binding on it disables the
786 void tst_qdeclarativeecmascript::constantsOverrideBindings()
790 QDeclarativeComponent component(&engine, TEST_FILE("constantsOverrideBindings.1.qml"));
791 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
792 QVERIFY(object != 0);
794 QCOMPARE(object->property("c2").toInt(), 0);
795 object->setProperty("c1", QVariant(9));
796 QCOMPARE(object->property("c2").toInt(), 9);
798 emit object->basicSignal();
800 QCOMPARE(object->property("c2").toInt(), 13);
801 object->setProperty("c1", QVariant(8));
802 QCOMPARE(object->property("c2").toInt(), 13);
807 // During construction
809 QDeclarativeComponent component(&engine, TEST_FILE("constantsOverrideBindings.2.qml"));
810 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
811 QVERIFY(object != 0);
813 QCOMPARE(object->property("c1").toInt(), 0);
814 QCOMPARE(object->property("c2").toInt(), 10);
815 object->setProperty("c1", QVariant(9));
816 QCOMPARE(object->property("c1").toInt(), 9);
817 QCOMPARE(object->property("c2").toInt(), 10);
825 QDeclarativeComponent component(&engine, TEST_FILE("constantsOverrideBindings.3.qml"));
826 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
827 QVERIFY(object != 0);
829 QCOMPARE(object->property("c2").toInt(), 0);
830 object->setProperty("c1", QVariant(9));
831 QCOMPARE(object->property("c2").toInt(), 9);
833 object->setProperty("c2", QVariant(13));
834 QCOMPARE(object->property("c2").toInt(), 13);
835 object->setProperty("c1", QVariant(7));
836 QCOMPARE(object->property("c1").toInt(), 7);
837 QCOMPARE(object->property("c2").toInt(), 13);
845 QDeclarativeComponent component(&engine, TEST_FILE("constantsOverrideBindings.4.qml"));
846 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
847 QVERIFY(object != 0);
849 QCOMPARE(object->property("c1").toInt(), 0);
850 QCOMPARE(object->property("c3").toInt(), 10);
851 object->setProperty("c1", QVariant(9));
852 QCOMPARE(object->property("c1").toInt(), 9);
853 QCOMPARE(object->property("c3").toInt(), 10);
860 Tests that assigning a binding to a property that already has a binding causes
861 the original binding to be disabled.
863 void tst_qdeclarativeecmascript::outerBindingOverridesInnerBinding()
865 QDeclarativeComponent component(&engine,
866 TEST_FILE("outerBindingOverridesInnerBinding.qml"));
867 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
868 QVERIFY(object != 0);
870 QCOMPARE(object->property("c1").toInt(), 0);
871 QCOMPARE(object->property("c2").toInt(), 0);
872 QCOMPARE(object->property("c3").toInt(), 0);
874 object->setProperty("c1", QVariant(9));
875 QCOMPARE(object->property("c1").toInt(), 9);
876 QCOMPARE(object->property("c2").toInt(), 0);
877 QCOMPARE(object->property("c3").toInt(), 0);
879 object->setProperty("c3", QVariant(8));
880 QCOMPARE(object->property("c1").toInt(), 9);
881 QCOMPARE(object->property("c2").toInt(), 8);
882 QCOMPARE(object->property("c3").toInt(), 8);
888 Access a non-existent attached object.
890 Tests for a regression where this used to crash.
892 void tst_qdeclarativeecmascript::nonExistentAttachedObject()
894 QDeclarativeComponent component(&engine, TEST_FILE("nonExistentAttachedObject.qml"));
896 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString";
897 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
899 QObject *object = component.create();
900 QVERIFY(object != 0);
905 void tst_qdeclarativeecmascript::scope()
908 QDeclarativeComponent component(&engine, TEST_FILE("scope.qml"));
909 QObject *object = component.create();
910 QVERIFY(object != 0);
912 QCOMPARE(object->property("test1").toInt(), 1);
913 QCOMPARE(object->property("test2").toInt(), 2);
914 QCOMPARE(object->property("test3").toString(), QString("1Test"));
915 QCOMPARE(object->property("test4").toString(), QString("2Test"));
916 QCOMPARE(object->property("test5").toInt(), 1);
917 QCOMPARE(object->property("test6").toInt(), 1);
918 QCOMPARE(object->property("test7").toInt(), 2);
919 QCOMPARE(object->property("test8").toInt(), 2);
920 QCOMPARE(object->property("test9").toInt(), 1);
921 QCOMPARE(object->property("test10").toInt(), 3);
927 QDeclarativeComponent component(&engine, TEST_FILE("scope.2.qml"));
928 QObject *object = component.create();
929 QVERIFY(object != 0);
931 QCOMPARE(object->property("test1").toInt(), 19);
932 QCOMPARE(object->property("test2").toInt(), 19);
933 QCOMPARE(object->property("test3").toInt(), 14);
934 QCOMPARE(object->property("test4").toInt(), 14);
935 QCOMPARE(object->property("test5").toInt(), 24);
936 QCOMPARE(object->property("test6").toInt(), 24);
942 QDeclarativeComponent component(&engine, TEST_FILE("scope.3.qml"));
943 QObject *object = component.create();
944 QVERIFY(object != 0);
946 QCOMPARE(object->property("test1").toBool(), true);
947 QCOMPARE(object->property("test2").toBool(), true);
948 QCOMPARE(object->property("test3").toBool(), true);
953 // Signal argument scope
955 QDeclarativeComponent component(&engine, TEST_FILE("scope.4.qml"));
956 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
957 QVERIFY(object != 0);
959 QCOMPARE(object->property("test").toInt(), 0);
960 QCOMPARE(object->property("test2").toString(), QString());
962 emit object->argumentSignal(13, "Argument Scope", 9, MyQmlObject::EnumValue4, Qt::RightButton);
964 QCOMPARE(object->property("test").toInt(), 13);
965 QCOMPARE(object->property("test2").toString(), QString("Argument Scope"));
971 QDeclarativeComponent component(&engine, TEST_FILE("scope.5.qml"));
972 QObject *object = component.create();
973 QVERIFY(object != 0);
975 QCOMPARE(object->property("test1").toBool(), true);
976 QCOMPARE(object->property("test2").toBool(), true);
982 QDeclarativeComponent component(&engine, TEST_FILE("scope.6.qml"));
983 QObject *object = component.create();
984 QVERIFY(object != 0);
986 QCOMPARE(object->property("test").toBool(), true);
992 // In 4.7, non-library javascript files that had no imports shared the imports of their
994 void tst_qdeclarativeecmascript::importScope()
996 QDeclarativeComponent component(&engine, TEST_FILE("importScope.qml"));
997 QObject *o = component.create();
1000 QCOMPARE(o->property("test").toInt(), 240);
1006 Tests that "any" type passes through a synthesized signal parameter. This
1007 is essentially a test of QDeclarativeMetaType::copy()
1009 void tst_qdeclarativeecmascript::signalParameterTypes()
1011 QDeclarativeComponent component(&engine, TEST_FILE("signalParameterTypes.qml"));
1012 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1013 QVERIFY(object != 0);
1015 emit object->basicSignal();
1017 QCOMPARE(object->property("intProperty").toInt(), 10);
1018 QCOMPARE(object->property("realProperty").toReal(), 19.2);
1019 QVERIFY(object->property("colorProperty").value<QColor>() == QColor(255, 255, 0, 255));
1020 QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255)));
1021 QVERIFY(object->property("enumProperty") == MyQmlObject::EnumValue3);
1022 QVERIFY(object->property("qtEnumProperty") == Qt::LeftButton);
1028 Test that two JS objects for the same QObject compare as equal.
1030 void tst_qdeclarativeecmascript::objectsCompareAsEqual()
1032 QDeclarativeComponent component(&engine, TEST_FILE("objectsCompareAsEqual.qml"));
1033 QObject *object = component.create();
1034 QVERIFY(object != 0);
1036 QCOMPARE(object->property("test1").toBool(), true);
1037 QCOMPARE(object->property("test2").toBool(), true);
1038 QCOMPARE(object->property("test3").toBool(), true);
1039 QCOMPARE(object->property("test4").toBool(), true);
1040 QCOMPARE(object->property("test5").toBool(), true);
1046 Confirm bindings and alias properties can coexist.
1048 Tests for a regression where the binding would not reevaluate.
1050 void tst_qdeclarativeecmascript::aliasPropertyAndBinding()
1052 QDeclarativeComponent component(&engine, TEST_FILE("aliasPropertyAndBinding.qml"));
1053 QObject *object = component.create();
1054 QVERIFY(object != 0);
1056 QCOMPARE(object->property("c2").toInt(), 3);
1057 QCOMPARE(object->property("c3").toInt(), 3);
1059 object->setProperty("c2", QVariant(19));
1061 QCOMPARE(object->property("c2").toInt(), 19);
1062 QCOMPARE(object->property("c3").toInt(), 19);
1068 Ensure that we can write undefined value to an alias property,
1069 and that the aliased property is reset correctly if possible.
1071 void tst_qdeclarativeecmascript::aliasPropertyReset()
1073 QObject *object = 0;
1075 // test that a manual write (of undefined) to a resettable aliased property succeeds
1076 QDeclarativeComponent c1(&engine, TEST_FILE("aliasreset/aliasPropertyReset.1.qml"));
1077 object = c1.create();
1078 QVERIFY(object != 0);
1079 QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() != 0);
1080 QCOMPARE(object->property("aliasIsUndefined"), QVariant(false));
1081 QMetaObject::invokeMethod(object, "resetAliased");
1082 QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0);
1083 QCOMPARE(object->property("aliasIsUndefined"), QVariant(true));
1086 // test that a manual write (of undefined) to a resettable alias property succeeds
1087 QDeclarativeComponent c2(&engine, TEST_FILE("aliasreset/aliasPropertyReset.2.qml"));
1088 object = c2.create();
1089 QVERIFY(object != 0);
1090 QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() != 0);
1091 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false));
1092 QMetaObject::invokeMethod(object, "resetAlias");
1093 QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0);
1094 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(true));
1097 // test that an alias to a bound property works correctly
1098 QDeclarativeComponent c3(&engine, TEST_FILE("aliasreset/aliasPropertyReset.3.qml"));
1099 object = c3.create();
1100 QVERIFY(object != 0);
1101 QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() != 0);
1102 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false));
1103 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1104 QMetaObject::invokeMethod(object, "resetAlias");
1105 QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0);
1106 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(true));
1107 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1110 // test that a manual write (of undefined) to a resettable alias property
1111 // whose aliased property's object has been deleted, does not crash.
1112 QDeclarativeComponent c4(&engine, TEST_FILE("aliasreset/aliasPropertyReset.4.qml"));
1113 object = c4.create();
1114 QVERIFY(object != 0);
1115 QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() != 0);
1116 QObject *loader = object->findChild<QObject*>("loader");
1117 QVERIFY(loader != 0);
1119 QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0); // deletion should have caused value unset.
1120 QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash.
1121 QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0);
1122 QMetaObject::invokeMethod(object, "setAlias"); // shouldn't crash, and shouldn't change value (since it's no longer referencing anything).
1123 QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0);
1126 // test that binding an alias property to an undefined value works correctly
1127 QDeclarativeComponent c5(&engine, TEST_FILE("aliasreset/aliasPropertyReset.5.qml"));
1128 object = c5.create();
1129 QVERIFY(object != 0);
1130 QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0); // bound to undefined value.
1133 // test that a manual write (of undefined) to a non-resettable property fails properly
1134 QUrl url = TEST_FILE("aliasreset/aliasPropertyReset.error.1.qml");
1135 QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int");
1136 QDeclarativeComponent e1(&engine, url);
1137 object = e1.create();
1138 QVERIFY(object != 0);
1139 QCOMPARE(object->property("intAlias").value<int>(), 12);
1140 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1141 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1142 QMetaObject::invokeMethod(object, "resetAlias");
1143 QCOMPARE(object->property("intAlias").value<int>(), 12);
1144 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1148 void tst_qdeclarativeecmascript::dynamicCreation_data()
1150 QTest::addColumn<QString>("method");
1151 QTest::addColumn<QString>("createdName");
1153 QTest::newRow("One") << "createOne" << "objectOne";
1154 QTest::newRow("Two") << "createTwo" << "objectTwo";
1155 QTest::newRow("Three") << "createThree" << "objectThree";
1159 Test using createQmlObject to dynamically generate an item
1160 Also using createComponent is tested.
1162 void tst_qdeclarativeecmascript::dynamicCreation()
1164 QFETCH(QString, method);
1165 QFETCH(QString, createdName);
1167 QDeclarativeComponent component(&engine, TEST_FILE("dynamicCreation.qml"));
1168 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1169 QVERIFY(object != 0);
1171 QMetaObject::invokeMethod(object, method.toUtf8());
1172 QObject *created = object->objectProperty();
1174 QCOMPARE(created->objectName(), createdName);
1180 Tests the destroy function
1182 void tst_qdeclarativeecmascript::dynamicDestruction()
1185 QDeclarativeComponent component(&engine, TEST_FILE("dynamicDeletion.qml"));
1186 QDeclarativeGuard<MyQmlObject> object = qobject_cast<MyQmlObject*>(component.create());
1187 QVERIFY(object != 0);
1188 QDeclarativeGuard<QObject> createdQmlObject = 0;
1190 QMetaObject::invokeMethod(object, "create");
1191 createdQmlObject = object->objectProperty();
1192 QVERIFY(createdQmlObject);
1193 QCOMPARE(createdQmlObject->objectName(), QString("emptyObject"));
1195 QMetaObject::invokeMethod(object, "killOther");
1196 QVERIFY(createdQmlObject);
1197 QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion);
1198 QVERIFY(createdQmlObject);
1199 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1200 if (createdQmlObject) {
1202 QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion);
1205 QVERIFY(!createdQmlObject);
1207 QDeclarativeEngine::setObjectOwnership(object, QDeclarativeEngine::JavaScriptOwnership);
1208 QMetaObject::invokeMethod(object, "killMe");
1211 QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion);
1216 QDeclarativeComponent component(&engine, TEST_FILE("dynamicDeletion.2.qml"));
1217 QObject *o = component.create();
1220 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1222 QMetaObject::invokeMethod(o, "create");
1224 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) != 0);
1226 QMetaObject::invokeMethod(o, "destroy");
1228 QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion);
1230 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1237 tests that id.toString() works
1239 void tst_qdeclarativeecmascript::objectToString()
1241 QDeclarativeComponent component(&engine, TEST_FILE("declarativeToString.qml"));
1242 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1243 QVERIFY(object != 0);
1244 QMetaObject::invokeMethod(object, "testToString");
1245 QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_"));
1246 QVERIFY(object->stringProperty().endsWith(", \"objName\")"));
1252 tests that id.hasOwnProperty() works
1254 void tst_qdeclarativeecmascript::objectHasOwnProperty()
1256 QUrl url = TEST_FILE("declarativeHasOwnProperty.qml");
1257 QString warning1 = url.toString() + ":59: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1258 QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1259 QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1261 QDeclarativeComponent component(&engine, url);
1262 QObject *object = component.create();
1263 QVERIFY(object != 0);
1265 // test QObjects in QML
1266 QMetaObject::invokeMethod(object, "testHasOwnPropertySuccess");
1267 QVERIFY(object->property("result").value<bool>() == true);
1268 QMetaObject::invokeMethod(object, "testHasOwnPropertyFailure");
1269 QVERIFY(object->property("result").value<bool>() == false);
1271 // now test other types in QML
1272 QObject *child = object->findChild<QObject*>("typeObj");
1273 QVERIFY(child != 0);
1274 QMetaObject::invokeMethod(child, "testHasOwnPropertySuccess");
1275 QCOMPARE(child->property("valueTypeHasOwnProperty").toBool(), true);
1276 QCOMPARE(child->property("valueTypeHasOwnProperty2").toBool(), true);
1277 QCOMPARE(child->property("variantTypeHasOwnProperty").toBool(), true);
1278 QCOMPARE(child->property("stringTypeHasOwnProperty").toBool(), true);
1279 QCOMPARE(child->property("listTypeHasOwnProperty").toBool(), true);
1280 QCOMPARE(child->property("emptyListTypeHasOwnProperty").toBool(), true);
1281 QCOMPARE(child->property("enumTypeHasOwnProperty").toBool(), true);
1282 QCOMPARE(child->property("typenameHasOwnProperty").toBool(), true);
1283 QCOMPARE(child->property("typenameHasOwnProperty2").toBool(), true);
1284 QCOMPARE(child->property("moduleApiTypeHasOwnProperty").toBool(), true);
1285 QCOMPARE(child->property("moduleApiPropertyTypeHasOwnProperty").toBool(), true);
1287 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1288 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureOne");
1289 QCOMPARE(child->property("enumNonValueHasOwnProperty").toBool(), false);
1290 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1291 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureTwo");
1292 QCOMPARE(child->property("moduleApiNonPropertyHasOwnProperty").toBool(), false);
1293 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1294 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureThree");
1295 QCOMPARE(child->property("listAtInvalidHasOwnProperty").toBool(), false);
1301 Tests bindings that indirectly cause their own deletion work.
1303 This test is best run under valgrind to ensure no invalid memory access occur.
1305 void tst_qdeclarativeecmascript::selfDeletingBinding()
1308 QDeclarativeComponent component(&engine, TEST_FILE("selfDeletingBinding.qml"));
1309 QObject *object = component.create();
1310 QVERIFY(object != 0);
1311 object->setProperty("triggerDelete", true);
1316 QDeclarativeComponent component(&engine, TEST_FILE("selfDeletingBinding.2.qml"));
1317 QObject *object = component.create();
1318 QVERIFY(object != 0);
1319 object->setProperty("triggerDelete", true);
1325 Test that extended object properties can be accessed.
1327 This test a regression where this used to crash. The issue was specificially
1328 for extended objects that did not include a synthesized meta object (so non-root
1329 and no synthesiszed properties).
1331 void tst_qdeclarativeecmascript::extendedObjectPropertyLookup()
1333 QDeclarativeComponent component(&engine, TEST_FILE("extendedObjectPropertyLookup.qml"));
1334 QObject *object = component.create();
1335 QVERIFY(object != 0);
1340 Test file/lineNumbers for binding/Script errors.
1342 void tst_qdeclarativeecmascript::scriptErrors()
1344 QDeclarativeComponent component(&engine, TEST_FILE("scriptErrors.qml"));
1345 QString url = component.url().toString();
1347 QString warning1 = url.left(url.length() - 3) + "js:2: Error: Invalid write to global property \"a\"";
1348 QString warning2 = url + ":5: ReferenceError: Can't find variable: a";
1349 QString warning3 = url.left(url.length() - 3) + "js:4: Error: Invalid write to global property \"a\"";
1350 QString warning4 = url + ":10: ReferenceError: Can't find variable: a";
1351 QString warning5 = url + ":8: ReferenceError: Can't find variable: a";
1352 QString warning6 = url + ":7: Unable to assign [undefined] to int";
1353 QString warning7 = url + ":12: Error: Cannot assign to read-only property \"trueProperty\"";
1354 QString warning8 = url + ":13: Error: Cannot assign to non-existent property \"fakeProperty\"";
1356 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1357 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1358 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1359 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
1360 QTest::ignoreMessage(QtWarningMsg, warning6.toLatin1().constData());
1361 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1362 QVERIFY(object != 0);
1364 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
1365 emit object->basicSignal();
1367 QTest::ignoreMessage(QtWarningMsg, warning7.toLatin1().constData());
1368 emit object->anotherBasicSignal();
1370 QTest::ignoreMessage(QtWarningMsg, warning8.toLatin1().constData());
1371 emit object->thirdBasicSignal();
1377 Test file/lineNumbers for inline functions.
1379 void tst_qdeclarativeecmascript::functionErrors()
1381 QDeclarativeComponent component(&engine, TEST_FILE("functionErrors.qml"));
1382 QString url = component.url().toString();
1384 QString warning = url + ":5: Error: Invalid write to global property \"a\"";
1386 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1388 QObject *object = component.create();
1389 QVERIFY(object != 0);
1392 // test that if an exception occurs while invoking js function from cpp, it is reported as expected.
1393 QDeclarativeComponent componentTwo(&engine, TEST_FILE("scarceresources/scarceResourceFunctionFail.qml"));
1394 url = componentTwo.url().toString();
1395 object = componentTwo.create();
1396 QVERIFY(object != 0);
1398 QString srpname = object->property("srp_name").toString();
1400 warning = url + QLatin1String(":17: TypeError: Property 'scarceResource' of object ") + srpname +
1401 QLatin1String(" is not a function");
1402 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed.
1403 QMetaObject::invokeMethod(object, "retrieveScarceResource");
1408 Test various errors that can occur when assigning a property from script
1410 void tst_qdeclarativeecmascript::propertyAssignmentErrors()
1412 QDeclarativeComponent component(&engine, TEST_FILE("propertyAssignmentErrors.qml"));
1414 QString url = component.url().toString();
1416 QObject *object = component.create();
1417 QVERIFY(object != 0);
1419 QCOMPARE(object->property("test1").toBool(), true);
1420 QCOMPARE(object->property("test2").toBool(), true);
1426 Test bindings still work when the reeval is triggered from within
1429 void tst_qdeclarativeecmascript::signalTriggeredBindings()
1431 QDeclarativeComponent component(&engine, TEST_FILE("signalTriggeredBindings.qml"));
1432 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1433 QVERIFY(object != 0);
1435 QCOMPARE(object->property("base").toReal(), 50.);
1436 QCOMPARE(object->property("test1").toReal(), 50.);
1437 QCOMPARE(object->property("test2").toReal(), 50.);
1439 object->basicSignal();
1441 QCOMPARE(object->property("base").toReal(), 200.);
1442 QCOMPARE(object->property("test1").toReal(), 200.);
1443 QCOMPARE(object->property("test2").toReal(), 200.);
1445 object->argumentSignal(10, QString(), 10, MyQmlObject::EnumValue4, Qt::RightButton);
1447 QCOMPARE(object->property("base").toReal(), 400.);
1448 QCOMPARE(object->property("test1").toReal(), 400.);
1449 QCOMPARE(object->property("test2").toReal(), 400.);
1455 Test that list properties can be iterated from ECMAScript
1457 void tst_qdeclarativeecmascript::listProperties()
1459 QDeclarativeComponent component(&engine, TEST_FILE("listProperties.qml"));
1460 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1461 QVERIFY(object != 0);
1463 QCOMPARE(object->property("test1").toInt(), 21);
1464 QCOMPARE(object->property("test2").toInt(), 2);
1465 QCOMPARE(object->property("test3").toBool(), true);
1466 QCOMPARE(object->property("test4").toBool(), true);
1471 void tst_qdeclarativeecmascript::exceptionClearsOnReeval()
1473 QDeclarativeComponent component(&engine, TEST_FILE("exceptionClearsOnReeval.qml"));
1474 QString url = component.url().toString();
1476 QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null";
1478 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1479 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1480 QVERIFY(object != 0);
1482 QCOMPARE(object->property("test").toBool(), false);
1484 MyQmlObject object2;
1485 MyQmlObject object3;
1486 object2.setObjectProperty(&object3);
1487 object->setObjectProperty(&object2);
1489 QCOMPARE(object->property("test").toBool(), true);
1494 void tst_qdeclarativeecmascript::exceptionSlotProducesWarning()
1496 QDeclarativeComponent component(&engine, TEST_FILE("exceptionProducesWarning.qml"));
1497 QString url = component.url().toString();
1499 QString warning = component.url().toString() + ":6: Error: JS exception";
1501 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1502 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1503 QVERIFY(object != 0);
1507 void tst_qdeclarativeecmascript::exceptionBindingProducesWarning()
1509 QDeclarativeComponent component(&engine, TEST_FILE("exceptionProducesWarning2.qml"));
1510 QString url = component.url().toString();
1512 QString warning = component.url().toString() + ":5: Error: JS exception";
1514 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1515 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1516 QVERIFY(object != 0);
1520 static int transientErrorsMsgCount = 0;
1521 static void transientErrorsMsgHandler(QtMsgType, const char *)
1523 ++transientErrorsMsgCount;
1526 // Check that transient binding errors are not displayed
1527 void tst_qdeclarativeecmascript::transientErrors()
1530 QDeclarativeComponent component(&engine, TEST_FILE("transientErrors.qml"));
1532 transientErrorsMsgCount = 0;
1533 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1535 QObject *object = component.create();
1536 QVERIFY(object != 0);
1538 qInstallMsgHandler(old);
1540 QCOMPARE(transientErrorsMsgCount, 0);
1545 // One binding erroring multiple times, but then resolving
1547 QDeclarativeComponent component(&engine, TEST_FILE("transientErrors.2.qml"));
1549 transientErrorsMsgCount = 0;
1550 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1552 QObject *object = component.create();
1553 QVERIFY(object != 0);
1555 qInstallMsgHandler(old);
1557 QCOMPARE(transientErrorsMsgCount, 0);
1563 // Check that errors during shutdown are minimized
1564 void tst_qdeclarativeecmascript::shutdownErrors()
1566 QDeclarativeComponent component(&engine, TEST_FILE("shutdownErrors.qml"));
1567 QObject *object = component.create();
1568 QVERIFY(object != 0);
1570 transientErrorsMsgCount = 0;
1571 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1575 qInstallMsgHandler(old);
1576 QCOMPARE(transientErrorsMsgCount, 0);
1579 void tst_qdeclarativeecmascript::compositePropertyType()
1581 QDeclarativeComponent component(&engine, TEST_FILE("compositePropertyType.qml"));
1582 QTest::ignoreMessage(QtDebugMsg, "hello world");
1583 QObject *object = qobject_cast<QObject *>(component.create());
1588 void tst_qdeclarativeecmascript::jsObject()
1590 QDeclarativeComponent component(&engine, TEST_FILE("jsObject.qml"));
1591 QObject *object = component.create();
1592 QVERIFY(object != 0);
1594 QCOMPARE(object->property("test").toInt(), 92);
1599 void tst_qdeclarativeecmascript::undefinedResetsProperty()
1602 QDeclarativeComponent component(&engine, TEST_FILE("undefinedResetsProperty.qml"));
1603 QObject *object = component.create();
1604 QVERIFY(object != 0);
1606 QCOMPARE(object->property("resettableProperty").toInt(), 92);
1608 object->setProperty("setUndefined", true);
1610 QCOMPARE(object->property("resettableProperty").toInt(), 13);
1612 object->setProperty("setUndefined", false);
1614 QCOMPARE(object->property("resettableProperty").toInt(), 92);
1619 QDeclarativeComponent component(&engine, TEST_FILE("undefinedResetsProperty.2.qml"));
1620 QObject *object = component.create();
1621 QVERIFY(object != 0);
1623 QCOMPARE(object->property("resettableProperty").toInt(), 19);
1625 QMetaObject::invokeMethod(object, "doReset");
1627 QCOMPARE(object->property("resettableProperty").toInt(), 13);
1634 void tst_qdeclarativeecmascript::bug1()
1636 QDeclarativeComponent component(&engine, TEST_FILE("bug.1.qml"));
1637 QObject *object = component.create();
1638 QVERIFY(object != 0);
1640 QCOMPARE(object->property("test").toInt(), 14);
1642 object->setProperty("a", 11);
1644 QCOMPARE(object->property("test").toInt(), 3);
1646 object->setProperty("b", true);
1648 QCOMPARE(object->property("test").toInt(), 9);
1653 void tst_qdeclarativeecmascript::bug2()
1655 QDeclarativeComponent component(&engine);
1656 component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl());
1658 QObject *object = component.create();
1659 QVERIFY(object != 0);
1664 // Don't crash in createObject when the component has errors.
1665 void tst_qdeclarativeecmascript::dynamicCreationCrash()
1667 QDeclarativeComponent component(&engine, TEST_FILE("dynamicCreation.qml"));
1668 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1669 QVERIFY(object != 0);
1671 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
1672 QMetaObject::invokeMethod(object, "dontCrash");
1673 QObject *created = object->objectProperty();
1674 QVERIFY(created == 0);
1679 // ownership transferred to JS, ensure that GC runs the dtor
1680 void tst_qdeclarativeecmascript::dynamicCreationOwnership()
1683 int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
1685 // allow the engine to go out of scope too.
1687 QDeclarativeEngine dcoEngine;
1688 QDeclarativeComponent component(&dcoEngine, TEST_FILE("dynamicCreationOwnership.qml"));
1689 QObject *object = component.create();
1690 QVERIFY(object != 0);
1691 MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo");
1692 QVERIFY(mdcdo != 0);
1693 mdcdo->setDtorCount(&dtorCount);
1695 for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
1696 QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
1698 // we do this once manually, but it should be done automatically
1699 // when the engine goes out of scope (since it should gc in dtor)
1700 QMetaObject::invokeMethod(object, "performGc");
1703 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
1709 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
1710 QCOMPARE(dtorCount, expectedDtorCount);
1714 void tst_qdeclarativeecmascript::regExpBug()
1716 QDeclarativeComponent component(&engine, TEST_FILE("regExp.qml"));
1717 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1718 QVERIFY(object != 0);
1719 QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]"));
1723 static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source)
1725 QString functionSource = QLatin1String("(function(object) { return ") +
1726 QLatin1String(source) + QLatin1String(" })");
1728 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
1731 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
1732 if (function.IsEmpty())
1734 v8::Handle<v8::Value> args[] = { o };
1735 function->Call(engine->global(), 1, args);
1736 return tc.HasCaught();
1739 static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o,
1740 const char *source, v8::Handle<v8::Value> result)
1742 QString functionSource = QLatin1String("(function(object) { return ") +
1743 QLatin1String(source) + QLatin1String(" })");
1745 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
1748 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
1749 if (function.IsEmpty())
1751 v8::Handle<v8::Value> args[] = { o };
1753 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
1758 return value->StrictEquals(result);
1761 static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o,
1764 QString functionSource = QLatin1String("(function(object) { return ") +
1765 QLatin1String(source) + QLatin1String(" })");
1767 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
1769 return v8::Handle<v8::Value>();
1770 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
1771 if (function.IsEmpty())
1772 return v8::Handle<v8::Value>();
1773 v8::Handle<v8::Value> args[] = { o };
1775 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
1778 return v8::Handle<v8::Value>();
1782 #define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
1783 #define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result)
1784 #define EVALUATE(source) evaluate(engine, object, source)
1786 void tst_qdeclarativeecmascript::callQtInvokables()
1788 MyInvokableObject o;
1790 QDeclarativeEngine qmlengine;
1791 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&qmlengine);
1793 QV8Engine *engine = ep->v8engine();
1795 v8::HandleScope handle_scope;
1796 v8::Context::Scope scope(engine->context());
1798 v8::Local<v8::Object> object = engine->newQObject(&o)->ToObject();
1800 // Non-existent methods
1802 QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
1803 QCOMPARE(o.error(), false);
1804 QCOMPARE(o.invoked(), -1);
1805 QCOMPARE(o.actuals().count(), 0);
1808 QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)"));
1809 QCOMPARE(o.error(), false);
1810 QCOMPARE(o.invoked(), -1);
1811 QCOMPARE(o.actuals().count(), 0);
1813 // Insufficient arguments
1815 QVERIFY(EVALUATE_ERROR("object.method_int()"));
1816 QCOMPARE(o.error(), false);
1817 QCOMPARE(o.invoked(), -1);
1818 QCOMPARE(o.actuals().count(), 0);
1821 QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
1822 QCOMPARE(o.error(), false);
1823 QCOMPARE(o.invoked(), -1);
1824 QCOMPARE(o.actuals().count(), 0);
1826 // Excessive arguments
1828 QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", v8::Undefined()));
1829 QCOMPARE(o.error(), false);
1830 QCOMPARE(o.invoked(), 8);
1831 QCOMPARE(o.actuals().count(), 1);
1832 QCOMPARE(o.actuals().at(0), QVariant(10));
1835 QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", v8::Undefined()));
1836 QCOMPARE(o.error(), false);
1837 QCOMPARE(o.invoked(), 9);
1838 QCOMPARE(o.actuals().count(), 2);
1839 QCOMPARE(o.actuals().at(0), QVariant(10));
1840 QCOMPARE(o.actuals().at(1), QVariant(11));
1842 // Test return types
1844 QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", v8::Undefined()));
1845 QCOMPARE(o.error(), false);
1846 QCOMPARE(o.invoked(), 0);
1847 QCOMPARE(o.actuals().count(), 0);
1850 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", v8::Integer::New(6)));
1851 QCOMPARE(o.error(), false);
1852 QCOMPARE(o.invoked(), 1);
1853 QCOMPARE(o.actuals().count(), 0);
1856 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", v8::Number::New(19.75)));
1857 QCOMPARE(o.error(), false);
1858 QCOMPARE(o.invoked(), 2);
1859 QCOMPARE(o.actuals().count(), 0);
1863 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QPointF()");
1864 QVERIFY(!ret.IsEmpty());
1865 QCOMPARE(engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5)));
1866 QCOMPARE(o.error(), false);
1867 QCOMPARE(o.invoked(), 3);
1868 QCOMPARE(o.actuals().count(), 0);
1873 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QObject()");
1874 QCOMPARE(engine->toQObject(ret), (QObject *)&o);
1875 QCOMPARE(o.error(), false);
1876 QCOMPARE(o.invoked(), 4);
1877 QCOMPARE(o.actuals().count(), 0);
1881 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_unknown()", v8::Undefined()));
1882 QCOMPARE(o.error(), false);
1883 QCOMPARE(o.invoked(), 5);
1884 QCOMPARE(o.actuals().count(), 0);
1888 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QScriptValue()");
1889 QVERIFY(ret->IsString());
1890 QCOMPARE(engine->toString(ret), QString("Hello world"));
1891 QCOMPARE(o.error(), false);
1892 QCOMPARE(o.invoked(), 6);
1893 QCOMPARE(o.actuals().count(), 0);
1897 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks")));
1898 QCOMPARE(o.error(), false);
1899 QCOMPARE(o.invoked(), 7);
1900 QCOMPARE(o.actuals().count(), 0);
1904 QVERIFY(EVALUATE_VALUE("object.method_int(94)", v8::Undefined()));
1905 QCOMPARE(o.error(), false);
1906 QCOMPARE(o.invoked(), 8);
1907 QCOMPARE(o.actuals().count(), 1);
1908 QCOMPARE(o.actuals().at(0), QVariant(94));
1911 QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", v8::Undefined()));
1912 QCOMPARE(o.error(), false);
1913 QCOMPARE(o.invoked(), 8);
1914 QCOMPARE(o.actuals().count(), 1);
1915 QCOMPARE(o.actuals().at(0), QVariant(94));
1918 QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", v8::Undefined()));
1919 QCOMPARE(o.error(), false);
1920 QCOMPARE(o.invoked(), 8);
1921 QCOMPARE(o.actuals().count(), 1);
1922 QCOMPARE(o.actuals().at(0), QVariant(0));
1925 QVERIFY(EVALUATE_VALUE("object.method_int(null)", v8::Undefined()));
1926 QCOMPARE(o.error(), false);
1927 QCOMPARE(o.invoked(), 8);
1928 QCOMPARE(o.actuals().count(), 1);
1929 QCOMPARE(o.actuals().at(0), QVariant(0));
1932 QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", v8::Undefined()));
1933 QCOMPARE(o.error(), false);
1934 QCOMPARE(o.invoked(), 8);
1935 QCOMPARE(o.actuals().count(), 1);
1936 QCOMPARE(o.actuals().at(0), QVariant(0));
1939 QVERIFY(EVALUATE_VALUE("object.method_int(object)", v8::Undefined()));
1940 QCOMPARE(o.error(), false);
1941 QCOMPARE(o.invoked(), 8);
1942 QCOMPARE(o.actuals().count(), 1);
1943 QCOMPARE(o.actuals().at(0), QVariant(0));
1946 QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", v8::Undefined()));
1947 QCOMPARE(o.error(), false);
1948 QCOMPARE(o.invoked(), 9);
1949 QCOMPARE(o.actuals().count(), 2);
1950 QCOMPARE(o.actuals().at(0), QVariant(122));
1951 QCOMPARE(o.actuals().at(1), QVariant(9));
1954 QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", v8::Undefined()));
1955 QCOMPARE(o.error(), false);
1956 QCOMPARE(o.invoked(), 10);
1957 QCOMPARE(o.actuals().count(), 1);
1958 QCOMPARE(o.actuals().at(0), QVariant(94.3));
1961 QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", v8::Undefined()));
1962 QCOMPARE(o.error(), false);
1963 QCOMPARE(o.invoked(), 10);
1964 QCOMPARE(o.actuals().count(), 1);
1965 QCOMPARE(o.actuals().at(0), QVariant(94.3));
1968 QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", v8::Undefined()));
1969 QCOMPARE(o.error(), false);
1970 QCOMPARE(o.invoked(), 10);
1971 QCOMPARE(o.actuals().count(), 1);
1972 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
1975 QVERIFY(EVALUATE_VALUE("object.method_real(null)", v8::Undefined()));
1976 QCOMPARE(o.error(), false);
1977 QCOMPARE(o.invoked(), 10);
1978 QCOMPARE(o.actuals().count(), 1);
1979 QCOMPARE(o.actuals().at(0), QVariant(0));
1982 QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", v8::Undefined()));
1983 QCOMPARE(o.error(), false);
1984 QCOMPARE(o.invoked(), 10);
1985 QCOMPARE(o.actuals().count(), 1);
1986 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
1989 QVERIFY(EVALUATE_VALUE("object.method_real(object)", v8::Undefined()));
1990 QCOMPARE(o.error(), false);
1991 QCOMPARE(o.invoked(), 10);
1992 QCOMPARE(o.actuals().count(), 1);
1993 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
1996 QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", v8::Undefined()));
1997 QCOMPARE(o.error(), false);
1998 QCOMPARE(o.invoked(), 11);
1999 QCOMPARE(o.actuals().count(), 1);
2000 QCOMPARE(o.actuals().at(0), QVariant("Hello world"));
2003 QVERIFY(EVALUATE_VALUE("object.method_QString(19)", v8::Undefined()));
2004 QCOMPARE(o.error(), false);
2005 QCOMPARE(o.invoked(), 11);
2006 QCOMPARE(o.actuals().count(), 1);
2007 QCOMPARE(o.actuals().at(0), QVariant("19"));
2011 QString expected = "MyInvokableObject(0x" + QString::number((quintptr)&o, 16) + ")";
2012 QVERIFY(EVALUATE_VALUE("object.method_QString(object)", v8::Undefined()));
2013 QCOMPARE(o.error(), false);
2014 QCOMPARE(o.invoked(), 11);
2015 QCOMPARE(o.actuals().count(), 1);
2016 QCOMPARE(o.actuals().at(0), QVariant(expected));
2020 QVERIFY(EVALUATE_VALUE("object.method_QString(null)", v8::Undefined()));
2021 QCOMPARE(o.error(), false);
2022 QCOMPARE(o.invoked(), 11);
2023 QCOMPARE(o.actuals().count(), 1);
2024 QCOMPARE(o.actuals().at(0), QVariant(QString()));
2027 QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", v8::Undefined()));
2028 QCOMPARE(o.error(), false);
2029 QCOMPARE(o.invoked(), 11);
2030 QCOMPARE(o.actuals().count(), 1);
2031 QCOMPARE(o.actuals().at(0), QVariant(QString()));
2034 QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", v8::Undefined()));
2035 QCOMPARE(o.error(), false);
2036 QCOMPARE(o.invoked(), 12);
2037 QCOMPARE(o.actuals().count(), 1);
2038 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2041 QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", v8::Undefined()));
2042 QCOMPARE(o.error(), false);
2043 QCOMPARE(o.invoked(), 12);
2044 QCOMPARE(o.actuals().count(), 1);
2045 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2048 QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", v8::Undefined()));
2049 QCOMPARE(o.error(), false);
2050 QCOMPARE(o.invoked(), 12);
2051 QCOMPARE(o.actuals().count(), 1);
2052 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2055 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", v8::Undefined()));
2056 QCOMPARE(o.error(), false);
2057 QCOMPARE(o.invoked(), 12);
2058 QCOMPARE(o.actuals().count(), 1);
2059 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2062 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", v8::Undefined()));
2063 QCOMPARE(o.error(), false);
2064 QCOMPARE(o.invoked(), 12);
2065 QCOMPARE(o.actuals().count(), 1);
2066 QCOMPARE(o.actuals().at(0), QVariant(QPointF(99.3, -10.2)));
2069 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", v8::Undefined()));
2070 QCOMPARE(o.error(), false);
2071 QCOMPARE(o.invoked(), 12);
2072 QCOMPARE(o.actuals().count(), 1);
2073 QCOMPARE(o.actuals().at(0), QVariant(QPointF(9, 12)));
2076 QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", v8::Undefined()));
2077 QCOMPARE(o.error(), false);
2078 QCOMPARE(o.invoked(), 13);
2079 QCOMPARE(o.actuals().count(), 1);
2080 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2083 QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", v8::Undefined()));
2084 QCOMPARE(o.error(), false);
2085 QCOMPARE(o.invoked(), 13);
2086 QCOMPARE(o.actuals().count(), 1);
2087 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2090 QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", v8::Undefined()));
2091 QCOMPARE(o.error(), false);
2092 QCOMPARE(o.invoked(), 13);
2093 QCOMPARE(o.actuals().count(), 1);
2094 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2097 QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", v8::Undefined()));
2098 QCOMPARE(o.error(), false);
2099 QCOMPARE(o.invoked(), 13);
2100 QCOMPARE(o.actuals().count(), 1);
2101 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2104 QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", v8::Undefined()));
2105 QCOMPARE(o.error(), false);
2106 QCOMPARE(o.invoked(), 13);
2107 QCOMPARE(o.actuals().count(), 1);
2108 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)&o));
2111 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined()));
2112 QCOMPARE(o.error(), false);
2113 QCOMPARE(o.invoked(), 14);
2114 QCOMPARE(o.actuals().count(), 1);
2115 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isNull());
2118 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined()));
2119 QCOMPARE(o.error(), false);
2120 QCOMPARE(o.invoked(), 14);
2121 QCOMPARE(o.actuals().count(), 1);
2122 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isUndefined());
2125 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined()));
2126 QCOMPARE(o.error(), false);
2127 QCOMPARE(o.invoked(), 14);
2128 QCOMPARE(o.actuals().count(), 1);
2129 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).strictlyEquals(QJSValue(19)));
2132 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined()));
2133 QCOMPARE(o.error(), false);
2134 QCOMPARE(o.invoked(), 14);
2135 QCOMPARE(o.actuals().count(), 1);
2136 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isArray());
2139 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined()));
2140 QCOMPARE(o.error(), false);
2141 QCOMPARE(o.invoked(), 15);
2142 QCOMPARE(o.actuals().count(), 2);
2143 QCOMPARE(o.actuals().at(0), QVariant(4));
2144 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isNull());
2147 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined()));
2148 QCOMPARE(o.error(), false);
2149 QCOMPARE(o.invoked(), 15);
2150 QCOMPARE(o.actuals().count(), 2);
2151 QCOMPARE(o.actuals().at(0), QVariant(8));
2152 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isUndefined());
2155 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined()));
2156 QCOMPARE(o.error(), false);
2157 QCOMPARE(o.invoked(), 15);
2158 QCOMPARE(o.actuals().count(), 2);
2159 QCOMPARE(o.actuals().at(0), QVariant(3));
2160 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).strictlyEquals(QJSValue(19)));
2163 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined()));
2164 QCOMPARE(o.error(), false);
2165 QCOMPARE(o.invoked(), 15);
2166 QCOMPARE(o.actuals().count(), 2);
2167 QCOMPARE(o.actuals().at(0), QVariant(44));
2168 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isArray());
2171 QVERIFY(EVALUATE_ERROR("object.method_overload()"));
2172 QCOMPARE(o.error(), false);
2173 QCOMPARE(o.invoked(), -1);
2174 QCOMPARE(o.actuals().count(), 0);
2177 QVERIFY(EVALUATE_VALUE("object.method_overload(10)", v8::Undefined()));
2178 QCOMPARE(o.error(), false);
2179 QCOMPARE(o.invoked(), 16);
2180 QCOMPARE(o.actuals().count(), 1);
2181 QCOMPARE(o.actuals().at(0), QVariant(10));
2184 QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", v8::Undefined()));
2185 QCOMPARE(o.error(), false);
2186 QCOMPARE(o.invoked(), 17);
2187 QCOMPARE(o.actuals().count(), 2);
2188 QCOMPARE(o.actuals().at(0), QVariant(10));
2189 QCOMPARE(o.actuals().at(1), QVariant(11));
2192 QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", v8::Undefined()));
2193 QCOMPARE(o.error(), false);
2194 QCOMPARE(o.invoked(), 18);
2195 QCOMPARE(o.actuals().count(), 1);
2196 QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2199 QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", v8::Undefined()));
2200 QCOMPARE(o.error(), false);
2201 QCOMPARE(o.invoked(), 19);
2202 QCOMPARE(o.actuals().count(), 1);
2203 QCOMPARE(o.actuals().at(0), QVariant(9));
2206 QVERIFY(EVALUATE_VALUE("object.method_default(10)", v8::Integer::New(19)));
2207 QCOMPARE(o.error(), false);
2208 QCOMPARE(o.invoked(), 20);
2209 QCOMPARE(o.actuals().count(), 2);
2210 QCOMPARE(o.actuals().at(0), QVariant(10));
2211 QCOMPARE(o.actuals().at(1), QVariant(19));
2214 QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", v8::Integer::New(13)));
2215 QCOMPARE(o.error(), false);
2216 QCOMPARE(o.invoked(), 20);
2217 QCOMPARE(o.actuals().count(), 2);
2218 QCOMPARE(o.actuals().at(0), QVariant(10));
2219 QCOMPARE(o.actuals().at(1), QVariant(13));
2222 QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", v8::Undefined()));
2223 QCOMPARE(o.error(), false);
2224 QCOMPARE(o.invoked(), -3);
2225 QCOMPARE(o.actuals().count(), 1);
2226 QCOMPARE(o.actuals().at(0), QVariant(9));
2229 QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", v8::Undefined()));
2230 QCOMPARE(o.error(), false);
2231 QCOMPARE(o.invoked(), 21);
2232 QCOMPARE(o.actuals().count(), 2);
2233 QCOMPARE(o.actuals().at(0), QVariant(9));
2234 QCOMPARE(o.actuals().at(1), QVariant());
2237 QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", v8::Undefined()));
2238 QCOMPARE(o.error(), false);
2239 QCOMPARE(o.invoked(), 21);
2240 QCOMPARE(o.actuals().count(), 2);
2241 QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2242 QCOMPARE(o.actuals().at(1), QVariant(QString("World")));
2245 // QTBUG-13047 (check that you can pass registered object types as args)
2246 void tst_qdeclarativeecmascript::invokableObjectArg()
2248 QDeclarativeComponent component(&engine, TEST_FILE("invokableObjectArg.qml"));
2250 QObject *o = component.create();
2252 MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o);
2254 QCOMPARE(qmlobject->myinvokableObject, qmlobject);
2259 // QTBUG-13047 (check that you can return registered object types from methods)
2260 void tst_qdeclarativeecmascript::invokableObjectRet()
2262 QDeclarativeComponent component(&engine, TEST_FILE("invokableObjectRet.qml"));
2264 QObject *o = component.create();
2266 QCOMPARE(o->property("test").toBool(), true);
2271 void tst_qdeclarativeecmascript::listToVariant()
2273 QDeclarativeComponent component(&engine, TEST_FILE("listToVariant.qml"));
2275 MyQmlContainer container;
2277 QDeclarativeContext context(engine.rootContext());
2278 context.setContextObject(&container);
2280 QObject *object = component.create(&context);
2281 QVERIFY(object != 0);
2283 QVariant v = object->property("test");
2284 QCOMPARE(v.userType(), qMetaTypeId<QDeclarativeListReference>());
2285 QVERIFY(qvariant_cast<QDeclarativeListReference>(v).object() == &container);
2291 Q_DECLARE_METATYPE(QDeclarativeListProperty<MyQmlObject>)
2292 void tst_qdeclarativeecmascript::listAssignment()
2294 QDeclarativeComponent component(&engine, TEST_FILE("listAssignment.qml"));
2295 QObject *obj = component.create();
2296 QCOMPARE(obj->property("list1length").toInt(), 2);
2297 QDeclarativeListProperty<MyQmlObject> list1 = obj->property("list1").value<QDeclarativeListProperty<MyQmlObject> >();
2298 QDeclarativeListProperty<MyQmlObject> list2 = obj->property("list2").value<QDeclarativeListProperty<MyQmlObject> >();
2299 QCOMPARE(list1.count(&list1), list2.count(&list2));
2300 QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0));
2301 QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1));
2306 void tst_qdeclarativeecmascript::multiEngineObject()
2309 obj.setStringProperty("Howdy planet");
2311 QDeclarativeEngine e1;
2312 e1.rootContext()->setContextProperty("thing", &obj);
2313 QDeclarativeComponent c1(&e1, TEST_FILE("multiEngineObject.qml"));
2315 QDeclarativeEngine e2;
2316 e2.rootContext()->setContextProperty("thing", &obj);
2317 QDeclarativeComponent c2(&e2, TEST_FILE("multiEngineObject.qml"));
2319 QObject *o1 = c1.create();
2320 QObject *o2 = c2.create();
2322 QCOMPARE(o1->property("test").toString(), QString("Howdy planet"));
2323 QCOMPARE(o2->property("test").toString(), QString("Howdy planet"));
2329 // Test that references to QObjects are cleanup when the object is destroyed
2330 void tst_qdeclarativeecmascript::deletedObject()
2332 QDeclarativeComponent component(&engine, TEST_FILE("deletedObject.qml"));
2334 QObject *object = component.create();
2336 QCOMPARE(object->property("test1").toBool(), true);
2337 QCOMPARE(object->property("test2").toBool(), true);
2338 QCOMPARE(object->property("test3").toBool(), true);
2339 QCOMPARE(object->property("test4").toBool(), true);
2344 void tst_qdeclarativeecmascript::attachedPropertyScope()
2346 QDeclarativeComponent component(&engine, TEST_FILE("attachedPropertyScope.qml"));
2348 QObject *object = component.create();
2349 QVERIFY(object != 0);
2351 MyQmlAttachedObject *attached =
2352 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
2353 QVERIFY(attached != 0);
2355 QCOMPARE(object->property("value2").toInt(), 0);
2357 attached->emitMySignal();
2359 QCOMPARE(object->property("value2").toInt(), 9);
2364 void tst_qdeclarativeecmascript::scriptConnect()
2367 QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.1.qml"));
2369 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2370 QVERIFY(object != 0);
2372 QCOMPARE(object->property("test").toBool(), false);
2373 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2374 QCOMPARE(object->property("test").toBool(), true);
2380 QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.2.qml"));
2382 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2383 QVERIFY(object != 0);
2385 QCOMPARE(object->property("test").toBool(), false);
2386 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2387 QCOMPARE(object->property("test").toBool(), true);
2393 QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.3.qml"));
2395 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2396 QVERIFY(object != 0);
2398 QCOMPARE(object->property("test").toBool(), false);
2399 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2400 QCOMPARE(object->property("test").toBool(), true);
2406 QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.4.qml"));
2408 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2409 QVERIFY(object != 0);
2411 QCOMPARE(object->methodCalled(), false);
2412 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2413 QCOMPARE(object->methodCalled(), true);
2419 QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.5.qml"));
2421 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2422 QVERIFY(object != 0);
2424 QCOMPARE(object->methodCalled(), false);
2425 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2426 QCOMPARE(object->methodCalled(), true);
2432 QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.6.qml"));
2434 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2435 QVERIFY(object != 0);
2437 QCOMPARE(object->property("test").toInt(), 0);
2438 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2439 QCOMPARE(object->property("test").toInt(), 2);
2445 void tst_qdeclarativeecmascript::scriptDisconnect()
2448 QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.1.qml"));
2450 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2451 QVERIFY(object != 0);
2453 QCOMPARE(object->property("test").toInt(), 0);
2454 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2455 QCOMPARE(object->property("test").toInt(), 1);
2456 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2457 QCOMPARE(object->property("test").toInt(), 2);
2458 emit object->basicSignal();
2459 QCOMPARE(object->property("test").toInt(), 2);
2460 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2461 QCOMPARE(object->property("test").toInt(), 2);
2467 QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.2.qml"));
2469 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2470 QVERIFY(object != 0);
2472 QCOMPARE(object->property("test").toInt(), 0);
2473 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2474 QCOMPARE(object->property("test").toInt(), 1);
2475 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2476 QCOMPARE(object->property("test").toInt(), 2);
2477 emit object->basicSignal();
2478 QCOMPARE(object->property("test").toInt(), 2);
2479 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2480 QCOMPARE(object->property("test").toInt(), 2);
2486 QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.3.qml"));
2488 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2489 QVERIFY(object != 0);
2491 QCOMPARE(object->property("test").toInt(), 0);
2492 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2493 QCOMPARE(object->property("test").toInt(), 1);
2494 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2495 QCOMPARE(object->property("test").toInt(), 2);
2496 emit object->basicSignal();
2497 QCOMPARE(object->property("test").toInt(), 2);
2498 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2499 QCOMPARE(object->property("test").toInt(), 3);
2504 QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.4.qml"));
2506 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2507 QVERIFY(object != 0);
2509 QCOMPARE(object->property("test").toInt(), 0);
2510 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2511 QCOMPARE(object->property("test").toInt(), 1);
2512 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2513 QCOMPARE(object->property("test").toInt(), 2);
2514 emit object->basicSignal();
2515 QCOMPARE(object->property("test").toInt(), 2);
2516 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2517 QCOMPARE(object->property("test").toInt(), 3);
2523 class OwnershipObject : public QObject
2527 OwnershipObject() { object = new QObject; }
2529 QPointer<QObject> object;
2532 QObject *getObject() { return object; }
2535 void tst_qdeclarativeecmascript::ownership()
2537 OwnershipObject own;
2538 QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
2539 context->setContextObject(&own);
2542 QDeclarativeComponent component(&engine, TEST_FILE("ownership.qml"));
2544 QVERIFY(own.object != 0);
2546 QObject *object = component.create(context);
2548 engine.collectGarbage();
2550 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
2552 QVERIFY(own.object == 0);
2557 own.object = new QObject(&own);
2560 QDeclarativeComponent component(&engine, TEST_FILE("ownership.qml"));
2562 QVERIFY(own.object != 0);
2564 QObject *object = component.create(context);
2566 engine.collectGarbage();
2568 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
2570 QVERIFY(own.object != 0);
2578 class CppOwnershipReturnValue : public QObject
2582 CppOwnershipReturnValue() : value(0) {}
2583 ~CppOwnershipReturnValue() { delete value; }
2585 Q_INVOKABLE QObject *create() {
2586 value = new QObject;
2587 QDeclarativeEngine::setObjectOwnership(value, QDeclarativeEngine::CppOwnership);
2591 Q_INVOKABLE MyQmlObject *createQmlObject() {
2592 MyQmlObject *rv = new MyQmlObject;
2597 QPointer<QObject> value;
2601 // Test setObjectOwnership(CppOwnership) works even when there is no QDeclarativeData
2602 void tst_qdeclarativeecmascript::cppOwnershipReturnValue()
2604 CppOwnershipReturnValue source;
2607 QDeclarativeEngine engine;
2608 engine.rootContext()->setContextProperty("source", &source);
2610 QVERIFY(source.value == 0);
2612 QDeclarativeComponent component(&engine);
2613 component.setData("import QtQuick 1.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl());
2615 QObject *object = component.create();
2617 QVERIFY(object != 0);
2618 QVERIFY(source.value != 0);
2623 QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion);
2625 QVERIFY(source.value != 0);
2629 void tst_qdeclarativeecmascript::ownershipCustomReturnValue()
2631 CppOwnershipReturnValue source;
2634 QDeclarativeEngine engine;
2635 engine.rootContext()->setContextProperty("source", &source);
2637 QVERIFY(source.value == 0);
2639 QDeclarativeComponent component(&engine);
2640 component.setData("import QtQuick 1.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl());
2642 QObject *object = component.create();
2644 QVERIFY(object != 0);
2645 QVERIFY(source.value != 0);
2650 engine.collectGarbage();
2651 QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion);
2653 QVERIFY(source.value == 0);
2656 class QListQObjectMethodsObject : public QObject
2660 QListQObjectMethodsObject() {
2661 m_objects.append(new MyQmlObject());
2662 m_objects.append(new MyQmlObject());
2665 ~QListQObjectMethodsObject() {
2666 qDeleteAll(m_objects);
2670 QList<QObject *> getObjects() { return m_objects; }
2673 QList<QObject *> m_objects;
2676 // Tests that returning a QList<QObject*> from a method works
2677 void tst_qdeclarativeecmascript::qlistqobjectMethods()
2679 QListQObjectMethodsObject obj;
2680 QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
2681 context->setContextObject(&obj);
2683 QDeclarativeComponent component(&engine, TEST_FILE("qlistqobjectMethods.qml"));
2685 QObject *object = component.create(context);
2687 QCOMPARE(object->property("test").toInt(), 2);
2688 QCOMPARE(object->property("test2").toBool(), true);
2695 void tst_qdeclarativeecmascript::strictlyEquals()
2697 QDeclarativeComponent component(&engine, TEST_FILE("strictlyEquals.qml"));
2699 QObject *object = component.create();
2700 QVERIFY(object != 0);
2702 QCOMPARE(object->property("test1").toBool(), true);
2703 QCOMPARE(object->property("test2").toBool(), true);
2704 QCOMPARE(object->property("test3").toBool(), true);
2705 QCOMPARE(object->property("test4").toBool(), true);
2706 QCOMPARE(object->property("test5").toBool(), true);
2707 QCOMPARE(object->property("test6").toBool(), true);
2708 QCOMPARE(object->property("test7").toBool(), true);
2709 QCOMPARE(object->property("test8").toBool(), true);
2714 void tst_qdeclarativeecmascript::compiled()
2716 QDeclarativeComponent component(&engine, TEST_FILE("compiled.qml"));
2718 QObject *object = component.create();
2719 QVERIFY(object != 0);
2721 QCOMPARE(object->property("test1").toReal(), qreal(15.7));
2722 QCOMPARE(object->property("test2").toReal(), qreal(-6.7));
2723 QCOMPARE(object->property("test3").toBool(), true);
2724 QCOMPARE(object->property("test4").toBool(), false);
2725 QCOMPARE(object->property("test5").toBool(), false);
2726 QCOMPARE(object->property("test6").toBool(), true);
2728 QCOMPARE(object->property("test7").toInt(), 185);
2729 QCOMPARE(object->property("test8").toInt(), 167);
2730 QCOMPARE(object->property("test9").toBool(), true);
2731 QCOMPARE(object->property("test10").toBool(), false);
2732 QCOMPARE(object->property("test11").toBool(), false);
2733 QCOMPARE(object->property("test12").toBool(), true);
2735 QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld"));
2736 QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World"));
2737 QCOMPARE(object->property("test15").toBool(), false);
2738 QCOMPARE(object->property("test16").toBool(), true);
2740 QCOMPARE(object->property("test17").toInt(), 5);
2741 QCOMPARE(object->property("test18").toReal(), qreal(176));
2742 QCOMPARE(object->property("test19").toInt(), 7);
2743 QCOMPARE(object->property("test20").toReal(), qreal(6.7));
2744 QCOMPARE(object->property("test21").toString(), QLatin1String("6.7"));
2745 QCOMPARE(object->property("test22").toString(), QLatin1String("!"));
2746 QCOMPARE(object->property("test23").toBool(), true);
2747 QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33));
2748 QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA));
2753 // Test that numbers assigned in bindings as strings work consistently
2754 void tst_qdeclarativeecmascript::numberAssignment()
2756 QDeclarativeComponent component(&engine, TEST_FILE("numberAssignment.qml"));
2758 QObject *object = component.create();
2759 QVERIFY(object != 0);
2761 QCOMPARE(object->property("test1"), QVariant((qreal)6.7));
2762 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
2763 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
2764 QCOMPARE(object->property("test3"), QVariant((qreal)6));
2765 QCOMPARE(object->property("test4"), QVariant((qreal)6));
2767 QCOMPARE(object->property("test5"), QVariant((int)7));
2768 QCOMPARE(object->property("test6"), QVariant((int)7));
2769 QCOMPARE(object->property("test7"), QVariant((int)6));
2770 QCOMPARE(object->property("test8"), QVariant((int)6));
2772 QCOMPARE(object->property("test9"), QVariant((unsigned int)7));
2773 QCOMPARE(object->property("test10"), QVariant((unsigned int)7));
2774 QCOMPARE(object->property("test11"), QVariant((unsigned int)6));
2775 QCOMPARE(object->property("test12"), QVariant((unsigned int)6));
2780 void tst_qdeclarativeecmascript::propertySplicing()
2782 QDeclarativeComponent component(&engine, TEST_FILE("propertySplicing.qml"));
2784 QObject *object = component.create();
2785 QVERIFY(object != 0);
2787 QCOMPARE(object->property("test").toBool(), true);
2793 void tst_qdeclarativeecmascript::signalWithUnknownTypes()
2795 QDeclarativeComponent component(&engine, TEST_FILE("signalWithUnknownTypes.qml"));
2797 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2798 QVERIFY(object != 0);
2800 MyQmlObject::MyType type;
2801 type.value = 0x8971123;
2802 emit object->signalWithUnknownType(type);
2804 MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant());
2806 QCOMPARE(result.value, type.value);
2812 void tst_qdeclarativeecmascript::signalWithJSValueInVariant_data()
2814 QTest::addColumn<QString>("expression");
2815 QTest::addColumn<QString>("compare");
2817 QString compareStrict("(function(a, b) { return a === b; })");
2818 QTest::newRow("true") << "true" << compareStrict;
2819 QTest::newRow("undefined") << "undefined" << compareStrict;
2820 QTest::newRow("null") << "null" << compareStrict;
2821 QTest::newRow("123") << "123" << compareStrict;
2822 QTest::newRow("'ciao'") << "'ciao'" << compareStrict;
2824 QString comparePropertiesStrict(
2826 " if (typeof b != 'object')"
2828 " var props = Object.getOwnPropertyNames(b);"
2829 " for (var i = 0; i < props.length; ++i) {"
2830 " var p = props[i];"
2831 " return arguments.callee(a[p], b[p]);"
2834 QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })" << comparePropertiesStrict;
2835 QTest::newRow("[10,20,30]") << "[10,20,30]" << comparePropertiesStrict;
2838 void tst_qdeclarativeecmascript::signalWithJSValueInVariant()
2840 QFETCH(QString, expression);
2841 QFETCH(QString, compare);
2843 QDeclarativeComponent component(&engine, TEST_FILE("signalWithJSValueInVariant.qml"));
2844 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
2845 QVERIFY(object != 0);
2847 QJSValue value = engine.evaluate(expression);
2848 QVERIFY(!engine.hasUncaughtException());
2849 object->setProperty("expression", expression);
2850 object->setProperty("compare", compare);
2851 object->setProperty("pass", false);
2853 emit object->signalWithVariant(QVariant::fromValue(value));
2854 QVERIFY(object->property("pass").toBool());
2857 void tst_qdeclarativeecmascript::signalWithJSValueInVariant_twoEngines_data()
2859 signalWithJSValueInVariant_data();
2862 void tst_qdeclarativeecmascript::signalWithJSValueInVariant_twoEngines()
2864 QFETCH(QString, expression);
2865 QFETCH(QString, compare);
2867 QDeclarativeComponent component(&engine, TEST_FILE("signalWithJSValueInVariant.qml"));
2868 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
2869 QVERIFY(object != 0);
2872 QJSValue value = engine2.evaluate(expression);
2873 QVERIFY(!engine2.hasUncaughtException());
2874 object->setProperty("expression", expression);
2875 object->setProperty("compare", compare);
2876 object->setProperty("pass", false);
2878 QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine.");
2879 emit object->signalWithVariant(QVariant::fromValue(value));
2880 QVERIFY(!object->property("pass").toBool());
2883 void tst_qdeclarativeecmascript::moduleApi_data()
2885 QTest::addColumn<QUrl>("testfile");
2886 QTest::addColumn<QString>("errorMessage");
2887 QTest::addColumn<QStringList>("warningMessages");
2888 QTest::addColumn<QStringList>("readProperties");
2889 QTest::addColumn<QVariantList>("readExpectedValues");
2890 QTest::addColumn<QStringList>("writeProperties");
2891 QTest::addColumn<QVariantList>("writeValues");
2892 QTest::addColumn<QStringList>("readBackProperties");
2893 QTest::addColumn<QVariantList>("readBackExpectedValues");
2895 QTest::newRow("qobject, register + read + method")
2896 << TEST_FILE("moduleapi/qobjectModuleApi.qml")
2899 << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
2900 << "qobjectMinorVersionTest" << "qobjectMajorVersionTest" << "qobjectParentedTest")
2901 << (QVariantList() << 20 << 20 << 1 << 20 << 20 << 26)
2907 QTest::newRow("script, register + read")
2908 << TEST_FILE("moduleapi/scriptModuleApi.qml")
2911 << (QStringList() << "scriptTest")
2912 << (QVariantList() << 13)
2918 QTest::newRow("qobject, caching + read")
2919 << TEST_FILE("moduleapi/qobjectModuleApiCaching.qml")
2922 << (QStringList() << "existingUriTest" << "qobjectParentedTest")
2923 << (QVariantList() << 20 << 26) // 26, shouldn't have incremented to 27.
2929 QTest::newRow("script, caching + read")
2930 << TEST_FILE("moduleapi/scriptModuleApiCaching.qml")
2933 << (QStringList() << "scriptTest")
2934 << (QVariantList() << 13) // 13, shouldn't have incremented to 14.
2940 QTest::newRow("qobject, writing + readonly constraints")
2941 << TEST_FILE("moduleapi/qobjectModuleApiWriting.qml")
2943 << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("moduleapi/qobjectModuleApiWriting.qml").toLocalFile() + QLatin1String(":14: Error: Cannot assign to read-only property \"qobjectTestProperty\"")))
2944 << (QStringList() << "readOnlyProperty" << "writableProperty")
2945 << (QVariantList() << 20 << 50)
2946 << (QStringList() << "firstProperty" << "writableProperty")
2947 << (QVariantList() << 30 << 30)
2948 << (QStringList() << "readOnlyProperty" << "writableProperty")
2949 << (QVariantList() << 20 << 30);
2951 QTest::newRow("script, writing + readonly constraints")
2952 << TEST_FILE("moduleapi/scriptModuleApiWriting.qml")
2954 << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("moduleapi/scriptModuleApiWriting.qml").toLocalFile() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\"")))
2955 << (QStringList() << "readBack" << "unchanged")
2956 << (QVariantList() << 13 << 42)
2957 << (QStringList() << "firstProperty" << "secondProperty")
2958 << (QVariantList() << 30 << 30)
2959 << (QStringList() << "readBack" << "unchanged")
2960 << (QVariantList() << 30 << 42);
2962 QTest::newRow("qobject module API enum values in JS")
2963 << TEST_FILE("moduleapi/qobjectModuleApiEnums.qml")
2966 << (QStringList() << "enumValue" << "enumMethod")
2967 << (QVariantList() << 42 << 30)
2973 QTest::newRow("qobject, invalid major version fail")
2974 << TEST_FILE("moduleapi/moduleApiMajorVersionFail.qml")
2975 << QString("QDeclarativeComponent: Component is not ready")
2984 QTest::newRow("qobject, invalid minor version fail")
2985 << TEST_FILE("moduleapi/moduleApiMinorVersionFail.qml")
2986 << QString("QDeclarativeComponent: Component is not ready")
2996 void tst_qdeclarativeecmascript::moduleApi()
2998 QFETCH(QUrl, testfile);
2999 QFETCH(QString, errorMessage);
3000 QFETCH(QStringList, warningMessages);
3001 QFETCH(QStringList, readProperties);
3002 QFETCH(QVariantList, readExpectedValues);
3003 QFETCH(QStringList, writeProperties);
3004 QFETCH(QVariantList, writeValues);
3005 QFETCH(QStringList, readBackProperties);
3006 QFETCH(QVariantList, readBackExpectedValues);
3008 QDeclarativeComponent component(&engine, testfile);
3010 if (!errorMessage.isEmpty())
3011 QTest::ignoreMessage(QtWarningMsg, errorMessage.toAscii().constData());
3013 if (warningMessages.size())
3014 foreach (const QString &warning, warningMessages)
3015 QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
3017 QObject *object = component.create();
3018 if (!errorMessage.isEmpty()) {
3019 QVERIFY(object == 0);
3021 QVERIFY(object != 0);
3022 for (int i = 0; i < readProperties.size(); ++i)
3023 QCOMPARE(object->property(readProperties.at(i).toAscii().constData()), readExpectedValues.at(i));
3024 for (int i = 0; i < writeProperties.size(); ++i)
3025 QVERIFY(object->setProperty(writeProperties.at(i).toAscii().constData(), writeValues.at(i)));
3026 for (int i = 0; i < readBackProperties.size(); ++i)
3027 QCOMPARE(object->property(readBackProperties.at(i).toAscii().constData()), readBackExpectedValues.at(i));
3032 void tst_qdeclarativeecmascript::importScripts_data()
3034 QTest::addColumn<QUrl>("testfile");
3035 QTest::addColumn<QString>("errorMessage");
3036 QTest::addColumn<QStringList>("warningMessages");
3037 QTest::addColumn<QStringList>("propertyNames");
3038 QTest::addColumn<QVariantList>("propertyValues");
3040 QTest::newRow("basic functionality")
3041 << TEST_FILE("jsimport/testImport.qml")
3044 << (QStringList() << QLatin1String("importedScriptStringValue")
3045 << QLatin1String("importedScriptFunctionValue")
3046 << QLatin1String("importedModuleAttachedPropertyValue")
3047 << QLatin1String("importedModuleEnumValue"))
3048 << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3053 QTest::newRow("import scoping")
3054 << TEST_FILE("jsimport/testImportScoping.qml")
3057 << (QStringList() << QLatin1String("componentError"))
3058 << (QVariantList() << QVariant(5));
3060 QTest::newRow("parent scope shouldn't be inherited by import with imports")
3061 << TEST_FILE("jsimportfail/failOne.qml")
3063 << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/failOne.qml").toLocalFile() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
3064 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3065 << (QVariantList() << QVariant(QString()));
3067 QTest::newRow("javascript imports in an import should be private to the import scope")
3068 << TEST_FILE("jsimportfail/failTwo.qml")
3070 << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/failTwo.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: ImportOneJs")))
3071 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3072 << (QVariantList() << QVariant(QString()));
3074 QTest::newRow("module imports in an import should be private to the import scope")
3075 << TEST_FILE("jsimportfail/failThree.qml")
3077 << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/failThree.qml").toLocalFile() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
3078 << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
3079 << (QVariantList() << QVariant(false));
3081 QTest::newRow("typenames in an import should be private to the import scope")
3082 << TEST_FILE("jsimportfail/failFour.qml")
3084 << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/failFour.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: JsQtTest")))
3085 << (QStringList() << QLatin1String("importedModuleEnumValue"))
3086 << (QVariantList() << QVariant(0));
3088 QTest::newRow("import with imports has it's own activation scope")
3089 << TEST_FILE("jsimportfail/failFive.qml")
3091 << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/importWithImports.js").toLocalFile() + QLatin1String(":8: ReferenceError: Can't find variable: Component"))
3092 << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/importPragmaLibrary.js").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: Component")))
3093 << (QStringList() << QLatin1String("componentError"))
3094 << (QVariantList() << QVariant(0));
3096 QTest::newRow("import pragma library script")
3097 << TEST_FILE("jsimport/testImportPragmaLibrary.qml")
3100 << (QStringList() << QLatin1String("testValue"))
3101 << (QVariantList() << QVariant(31));
3103 QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3104 << TEST_FILE("jsimportfail/testImportPragmaLibrary.qml")
3107 << (QStringList() << QLatin1String("testValue"))
3108 << (QVariantList() << QVariant(0));
3110 QTest::newRow("import pragma library script which has an import")
3111 << TEST_FILE("jsimport/testImportPragmaLibraryWithImports.qml")
3114 << (QStringList() << QLatin1String("testValue"))
3115 << (QVariantList() << QVariant(55));
3117 QTest::newRow("import pragma library script which has a pragma library import")
3118 << TEST_FILE("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3121 << (QStringList() << QLatin1String("testValue"))
3122 << (QVariantList() << QVariant(18));
3125 void tst_qdeclarativeecmascript::importScripts()
3127 QFETCH(QUrl, testfile);
3128 QFETCH(QString, errorMessage);
3129 QFETCH(QStringList, warningMessages);
3130 QFETCH(QStringList, propertyNames);
3131 QFETCH(QVariantList, propertyValues);
3133 QDeclarativeComponent component(&engine, testfile);
3135 if (!errorMessage.isEmpty())
3136 QTest::ignoreMessage(QtWarningMsg, errorMessage.toAscii().constData());
3138 if (warningMessages.size())
3139 foreach (const QString &warning, warningMessages)
3140 QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
3142 QObject *object = component.create();
3143 if (!errorMessage.isEmpty()) {
3144 QVERIFY(object == 0);
3146 QVERIFY(object != 0);
3147 for (int i = 0; i < propertyNames.size(); ++i)
3148 QCOMPARE(object->property(propertyNames.at(i).toAscii().constData()), propertyValues.at(i));
3153 void tst_qdeclarativeecmascript::scarceResources()
3155 QPixmap origPixmap(100, 100);
3156 origPixmap.fill(Qt::blue);
3158 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&engine);
3159 ScarceResourceObject *eo = 0;
3160 QObject *object = 0;
3162 // in the following three cases, the instance created from the component
3163 // has a property which is a copy of the scarce resource; hence, the
3164 // resource should NOT be detached prior to deletion of the object instance,
3165 // unless the resource is destroyed explicitly.
3166 QDeclarativeComponent component(&engine, TEST_FILE("scarceresources/scarceResourceCopy.qml"));
3167 object = component.create();
3168 QVERIFY(object != 0);
3169 QVERIFY(object->property("scarceResourceCopy").isValid());
3170 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3171 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3172 eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3173 QVERIFY(!eo->scarceResourceIsDetached()); // there are two copies of it in existence: the property of object, and the property of eo.
3176 QDeclarativeComponent componentTwo(&engine, TEST_FILE("scarceresources/scarceResourceCopyFromJs.qml"));
3177 object = componentTwo.create();
3178 QVERIFY(object != 0);
3179 QVERIFY(object->property("scarceResourceCopy").isValid());
3180 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3181 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3182 eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3183 QVERIFY(!eo->scarceResourceIsDetached()); // there are two copies of it in existence: the property of object, and the property of eo.
3186 QDeclarativeComponent componentThree(&engine, TEST_FILE("scarceresources/scarceResourceDestroyedCopy.qml"));
3187 object = componentThree.create();
3188 QVERIFY(object != 0);
3189 QVERIFY(!(object->property("scarceResourceCopy").isValid())); // was manually released prior to being returned.
3190 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3191 eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3192 QVERIFY(eo->scarceResourceIsDetached()); // should have explicitly been released during the evaluation of the binding.
3195 // in the following three cases, no other copy should exist in memory,
3196 // and so it should be detached (unless explicitly preserved).
3197 QDeclarativeComponent componentFour(&engine, TEST_FILE("scarceresources/scarceResourceTest.qml"));
3198 object = componentFour.create();
3199 QVERIFY(object != 0);
3200 QVERIFY(object->property("scarceResourceTest").isValid());
3201 QCOMPARE(object->property("scarceResourceTest").toInt(), 100);
3202 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3203 eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3204 QVERIFY(eo->scarceResourceIsDetached()); // the resource should have been released after the binding was evaluated.
3207 QDeclarativeComponent componentFive(&engine, TEST_FILE("scarceresources/scarceResourceTestPreserve.qml"));
3208 object = componentFive.create();
3209 QVERIFY(object != 0);
3210 QVERIFY(object->property("scarceResourceTest").isValid());
3211 QCOMPARE(object->property("scarceResourceTest").toInt(), 100);
3212 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3213 eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3214 QVERIFY(!eo->scarceResourceIsDetached()); // this won't be detached since we explicitly preserved it.
3217 QDeclarativeComponent componentSix(&engine, TEST_FILE("scarceresources/scarceResourceTestMultiple.qml"));
3218 object = componentSix.create();
3219 QVERIFY(object != 0);
3220 QVERIFY(object->property("scarceResourceTest").isValid());
3221 QCOMPARE(object->property("scarceResourceTest").toInt(), 100);
3222 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3223 eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3224 QVERIFY(eo->scarceResourceIsDetached()); // all resources were released manually or automatically released.
3227 // test that scarce resources are handled correctly for imports
3228 QDeclarativeComponent componentSeven(&engine, TEST_FILE("scarceresources/scarceResourceCopyImportNoBinding.qml"));
3229 object = componentSeven.create();
3230 QVERIFY(object != 0); // the import should have caused the addition of a resource to the ScarceResources list
3231 QVERIFY(ep->scarceResources.isEmpty()); // but they should have been released by this point.
3234 QDeclarativeComponent componentEight(&engine, TEST_FILE("scarceresources/scarceResourceCopyImportFail.qml"));
3235 object = componentEight.create();
3236 QVERIFY(object != 0);
3237 QVERIFY(!object->property("scarceResourceCopy").isValid()); // wasn't preserved, so shouldn't be valid.
3238 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3241 QDeclarativeComponent componentNine(&engine, TEST_FILE("scarceresources/scarceResourceCopyImport.qml"));
3242 object = componentNine.create();
3243 QVERIFY(object != 0);
3244 QVERIFY(object->property("scarceResourceCopy").isValid()); // preserved, so should be valid.
3245 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3246 QVERIFY(object->property("scarceResourceAssignedCopyOne").isValid()); // assigned before destroy(), so should be valid.
3247 QCOMPARE(object->property("scarceResourceAssignedCopyOne").value<QPixmap>(), origPixmap);
3248 QVERIFY(!object->property("scarceResourceAssignedCopyTwo").isValid()); // assigned after destroy(), so should be invalid.
3249 QVERIFY(ep->scarceResources.isEmpty()); // this will still be zero, because "preserve()" REMOVES it from this list.
3252 // test that scarce resources are handled properly in signal invocation
3253 QDeclarativeComponent componentTen(&engine, TEST_FILE("scarceresources/scarceResourceSignal.qml"));
3254 object = componentTen.create();
3255 QVERIFY(object != 0);
3256 QObject *srsc = object->findChild<QObject*>("srsc");
3258 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3259 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3260 eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3261 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3262 QMetaObject::invokeMethod(srsc, "testSignal");
3263 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3264 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3265 eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3266 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3267 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3268 QVERIFY(srsc->property("scarceResourceCopy").isValid());
3269 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3270 eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3271 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3272 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3275 // test that scarce resources are handled properly from js functions in qml files
3276 QDeclarativeComponent componentEleven(&engine, TEST_FILE("scarceresources/scarceResourceFunction.qml"));
3277 object = componentEleven.create();
3278 QVERIFY(object != 0);
3279 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3280 eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3281 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3282 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3283 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3284 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3285 eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3286 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3287 QMetaObject::invokeMethod(object, "releaseScarceResource");
3288 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3289 eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3290 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3291 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3294 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3295 QDeclarativeComponent componentTwelve(&engine, TEST_FILE("scarceresources/scarceResourceFunctionFail.qml"));
3296 object = componentTwelve.create();
3297 QVERIFY(object != 0);
3298 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3299 eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3300 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3301 QString srp_name = object->property("srp_name").toString();
3302 QString expectedWarning = componentTwelve.url().toString() + QLatin1String(":17: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3303 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3304 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3305 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3306 eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3307 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3308 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3312 void tst_qdeclarativeecmascript::propertyChangeSlots()
3314 // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
3315 QDeclarativeComponent component(&engine, TEST_FILE("changeslots/propertyChangeSlots.qml"));
3316 QObject *object = component.create();
3317 QVERIFY(object != 0);
3320 // ensure that invalid property names fail properly.
3321 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
3322 QDeclarativeComponent e1(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.1.qml"));
3323 QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
3324 QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
3325 object = e1.create();
3326 QVERIFY(object == 0);
3329 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
3330 QDeclarativeComponent e2(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.2.qml"));
3331 expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
3332 QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
3333 object = e2.create();
3334 QVERIFY(object == 0);
3337 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
3338 QDeclarativeComponent e3(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.3.qml"));
3339 expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
3340 QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
3341 object = e3.create();
3342 QVERIFY(object == 0);
3345 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
3346 QDeclarativeComponent e4(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.4.qml"));
3347 expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
3348 QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
3349 object = e4.create();
3350 QVERIFY(object == 0);
3354 void tst_qdeclarativeecmascript::propertyVar_data()
3356 QTest::addColumn<QUrl>("qmlFile");
3359 QTest::newRow("non-bindable object subproperty changed") << TEST_FILE("propertyVar.1.qml");
3360 QTest::newRow("non-bindable object changed") << TEST_FILE("propertyVar.2.qml");
3361 QTest::newRow("primitive changed") << TEST_FILE("propertyVar.3.qml");
3362 QTest::newRow("javascript array modification") << TEST_FILE("propertyVar.4.qml");
3363 QTest::newRow("javascript map modification") << TEST_FILE("propertyVar.5.qml");
3364 QTest::newRow("javascript array assignment") << TEST_FILE("propertyVar.6.qml");
3365 QTest::newRow("javascript map assignment") << TEST_FILE("propertyVar.7.qml");
3366 QTest::newRow("literal property assignment") << TEST_FILE("propertyVar.8.qml");
3367 QTest::newRow("qobject property assignment") << TEST_FILE("propertyVar.9.qml");
3370 void tst_qdeclarativeecmascript::propertyVar()
3372 QFETCH(QUrl, qmlFile);
3374 QDeclarativeComponent component(&engine, qmlFile);
3375 QObject *object = component.create();
3376 QVERIFY(object != 0);
3378 QCOMPARE(object->property("test").toBool(), true);
3383 // Tests that we can write QVariant values to var properties from C++
3384 void tst_qdeclarativeecmascript::propertyVarCpp()
3386 QObject *object = 0;
3388 // ensure that writing to and reading from a var property from cpp works as required.
3389 // Literal values stored in var properties can be read and written as QVariants
3390 // of a specific type, whereas object values are read as QVariantMaps.
3391 QDeclarativeComponent component(&engine, TEST_FILE("propertyVarCpp.qml"));
3392 object = component.create();
3393 QVERIFY(object != 0);
3394 // assign int to property var that currently has int assigned
3395 QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
3396 QCOMPARE(object->property("varBound"), QVariant(15));
3397 QCOMPARE(object->property("intBound"), QVariant(15));
3398 QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
3399 QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
3400 // assign string to property var that current has bool assigned
3401 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
3402 QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
3403 QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
3404 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
3405 // now enforce behaviour when accessing JavaScript objects from cpp.
3406 QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
3410 static void gc(QDeclarativeEngine &engine)
3412 engine.collectGarbage();
3413 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3416 void tst_qdeclarativeecmascript::propertyVarOwnership()
3418 // Referenced JS objects are not collected
3420 QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.qml"));
3421 QObject *object = component.create();
3422 QVERIFY(object != 0);
3423 QCOMPARE(object->property("test").toBool(), false);
3424 QMetaObject::invokeMethod(object, "runTest");
3425 QCOMPARE(object->property("test").toBool(), true);
3428 // Referenced JS objects are not collected
3430 QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.2.qml"));
3431 QObject *object = component.create();
3432 QVERIFY(object != 0);
3433 QCOMPARE(object->property("test").toBool(), false);
3434 QMetaObject::invokeMethod(object, "runTest");
3435 QCOMPARE(object->property("test").toBool(), true);
3438 // Qt objects are not collected until they've been dereferenced
3440 QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.3.qml"));
3441 QObject *object = component.create();
3442 QVERIFY(object != 0);
3444 QCOMPARE(object->property("test2").toBool(), false);
3445 QCOMPARE(object->property("test2").toBool(), false);
3447 QMetaObject::invokeMethod(object, "runTest");
3448 QCOMPARE(object->property("test1").toBool(), true);
3450 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
3451 QVERIFY(!referencedObject.isNull());
3453 QVERIFY(!referencedObject.isNull());
3455 QMetaObject::invokeMethod(object, "runTest2");
3456 QCOMPARE(object->property("test2").toBool(), true);
3458 QVERIFY(referencedObject.isNull());
3462 // Self reference does not prevent Qt object collection
3464 QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.4.qml"));
3465 QObject *object = component.create();
3466 QVERIFY(object != 0);
3468 QCOMPARE(object->property("test").toBool(), true);
3470 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
3471 QVERIFY(!referencedObject.isNull());
3473 QVERIFY(!referencedObject.isNull());
3475 QMetaObject::invokeMethod(object, "runTest");
3477 QVERIFY(referencedObject.isNull());
3483 void tst_qdeclarativeecmascript::propertyVarImplicitOwnership()
3485 // The childObject has a reference to a different QObject. We want to ensure
3486 // that the different item will not be cleaned up until required. IE, the childObject
3487 // has implicit ownership of the constructed QObject.
3488 QDeclarativeComponent component(&engine, TEST_FILE("propertyVarImplicitOwnership.qml"));
3489 QObject *object = component.create();
3490 QVERIFY(object != 0);
3491 QMetaObject::invokeMethod(object, "assignCircular");
3492 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3493 QObject *rootObject = object->property("vp").value<QObject*>();
3494 QVERIFY(rootObject != 0);
3495 QObject *childObject = rootObject->findChild<QObject*>("text");
3496 QVERIFY(childObject != 0);
3497 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
3498 QCOMPARE(childObject->property("textCanary").toInt(), 10);
3499 QMetaObject::invokeMethod(childObject, "constructQObject"); // creates a reference to a constructed QObject.
3500 QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
3501 QVERIFY(!qobjectGuard.isNull());
3502 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3503 QVERIFY(!qobjectGuard.isNull());
3504 QMetaObject::invokeMethod(object, "deassignCircular");
3505 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3506 QVERIFY(qobjectGuard.isNull()); // should have been collected now.
3510 void tst_qdeclarativeecmascript::propertyVarReparent()
3512 // ensure that nothing breaks if we re-parent objects
3513 QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.reparent.qml"));
3514 QObject *object = component.create();
3515 QVERIFY(object != 0);
3516 QMetaObject::invokeMethod(object, "assignVarProp");
3517 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3518 QObject *rect = object->property("vp").value<QObject*>();
3519 QObject *text = rect->findChild<QObject*>("textOne");
3520 QObject *text2 = rect->findChild<QObject*>("textTwo");
3521 QWeakPointer<QObject> rectGuard(rect);
3522 QWeakPointer<QObject> textGuard(text);
3523 QWeakPointer<QObject> text2Guard(text2);
3524 QVERIFY(!rectGuard.isNull());
3525 QVERIFY(!textGuard.isNull());
3526 QVERIFY(!text2Guard.isNull());
3527 QCOMPARE(text->property("textCanary").toInt(), 11);
3528 QCOMPARE(text2->property("textCanary").toInt(), 12);
3529 // now construct an image which we will reparent.
3530 QMetaObject::invokeMethod(text2, "constructQObject");
3531 QObject *image = text2->property("vp").value<QObject*>();
3532 QWeakPointer<QObject> imageGuard(image);
3533 QVERIFY(!imageGuard.isNull());
3534 QCOMPARE(image->property("imageCanary").toInt(), 13);
3535 // now reparent the "Image" object (currently, it has JS ownership)
3536 image->setParent(text); // shouldn't be collected after deassignVp now, since has a parent.
3537 QMetaObject::invokeMethod(text2, "deassignVp");
3538 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3539 QCOMPARE(text->property("textCanary").toInt(), 11);
3540 QCOMPARE(text2->property("textCanary").toInt(), 22);
3541 QVERIFY(!imageGuard.isNull()); // should still be alive.
3542 QCOMPARE(image->property("imageCanary").toInt(), 13); // still able to access var properties
3543 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
3544 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3545 QVERIFY(imageGuard.isNull()); // should now have been deleted, due to parent being deleted.
3549 void tst_qdeclarativeecmascript::propertyVarReparentNullContext()
3551 // sometimes reparenting can cause problems
3552 // (eg, if the ctxt is collected, varproperties are no longer available)
3553 // this test ensures that no crash occurs in that situation.
3554 QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.reparent.qml"));
3555 QObject *object = component.create();
3556 QVERIFY(object != 0);
3557 QMetaObject::invokeMethod(object, "assignVarProp");
3558 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3559 QObject *rect = object->property("vp").value<QObject*>();
3560 QObject *text = rect->findChild<QObject*>("textOne");
3561 QObject *text2 = rect->findChild<QObject*>("textTwo");
3562 QWeakPointer<QObject> rectGuard(rect);
3563 QWeakPointer<QObject> textGuard(text);
3564 QWeakPointer<QObject> text2Guard(text2);
3565 QVERIFY(!rectGuard.isNull());
3566 QVERIFY(!textGuard.isNull());
3567 QVERIFY(!text2Guard.isNull());
3568 QCOMPARE(text->property("textCanary").toInt(), 11);
3569 QCOMPARE(text2->property("textCanary").toInt(), 12);
3570 // now construct an image which we will reparent.
3571 QMetaObject::invokeMethod(text2, "constructQObject");
3572 QObject *image = text2->property("vp").value<QObject*>();
3573 QWeakPointer<QObject> imageGuard(image);
3574 QVERIFY(!imageGuard.isNull());
3575 QCOMPARE(image->property("imageCanary").toInt(), 13);
3576 // now reparent the "Image" object (currently, it has JS ownership)
3577 image->setParent(object); // reparented to base object. after deassignVarProp, the ctxt will be invalid.
3578 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
3579 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3580 QVERIFY(!imageGuard.isNull()); // should still be alive.
3581 QVERIFY(!image->property("imageCanary").isValid()); // but varProperties won't be available (null context).
3583 QVERIFY(imageGuard.isNull()); // should now be dead.
3586 void tst_qdeclarativeecmascript::propertyVarCircular()
3588 // enforce behaviour regarding circular references - ensure qdvmemo deletion.
3589 QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.circular.qml"));
3590 QObject *object = component.create();
3591 QVERIFY(object != 0);
3592 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
3593 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3594 QCOMPARE(object->property("canaryInt"), QVariant(5));
3595 QVariant canaryResourceVariant = object->property("canaryResource");
3596 QVERIFY(canaryResourceVariant.isValid());
3597 QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
3598 canaryResourceVariant = QVariant(); // invalidate it to remove one copy of the pixmap from memory.
3599 QMetaObject::invokeMethod(object, "deassignCanaryResource"); // remove one copy of the pixmap from memory
3600 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3601 QVERIFY(!canaryResourcePixmap.isDetached()); // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
3602 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
3603 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3604 QCOMPARE(object->property("canaryInt"), QVariant(2));
3605 QCOMPARE(object->property("canaryResource"), QVariant(1));
3606 QVERIFY(canaryResourcePixmap.isDetached()); // now detached, since orig copy was member of qdvmemo which was deleted.
3610 void tst_qdeclarativeecmascript::propertyVarCircular2()
3612 // track deletion of JS-owned parent item with Cpp-owned child
3613 // where the child has a var property referencing its parent.
3614 QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.circular.2.qml"));
3615 QObject *object = component.create();
3616 QVERIFY(object != 0);
3617 QMetaObject::invokeMethod(object, "assignCircular");
3618 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3619 QObject *rootObject = object->property("vp").value<QObject*>();
3620 QVERIFY(rootObject != 0);
3621 QObject *childObject = rootObject->findChild<QObject*>("text");
3622 QVERIFY(childObject != 0);
3623 QWeakPointer<QObject> rootObjectTracker(rootObject);
3624 QVERIFY(!rootObjectTracker.isNull());
3625 QWeakPointer<QObject> childObjectTracker(childObject);
3626 QVERIFY(!childObjectTracker.isNull());
3628 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
3629 QCOMPARE(childObject->property("textCanary").toInt(), 10);
3630 QMetaObject::invokeMethod(object, "deassignCircular");
3631 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3632 QVERIFY(rootObjectTracker.isNull()); // should have been collected
3633 QVERIFY(childObjectTracker.isNull()); // should have been collected
3637 void tst_qdeclarativeecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
3639 *(int*)(parameter) += 1;
3640 qPersistentDispose(object);
3643 void tst_qdeclarativeecmascript::propertyVarInheritance()
3645 int propertyVarWeakRefCallbackCount = 0;
3647 // enforce behaviour regarding element inheritance - ensure handle disposal.
3648 // The particular component under test here has a chain of references.
3649 QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.inherit.qml"));
3650 QObject *object = component.create();
3651 QVERIFY(object != 0);
3652 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
3653 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3654 // we want to be able to track when the varProperties array of the last metaobject is disposed
3655 QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
3656 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*>();
3657 QDeclarativeVMEMetaObject *icovmemo = ((QDeclarativeVMEMetaObject *)(ico5->metaObject()));
3658 QDeclarativeVMEMetaObject *ccovmemo = ((QDeclarativeVMEMetaObject *)(cco5->metaObject()));
3659 v8::Persistent<v8::Value> icoCanaryHandle;
3660 v8::Persistent<v8::Value> ccoCanaryHandle;
3663 // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only
3664 // public function which can return us a handle to something in the varProperties array.
3665 icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(41));
3666 ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(41));
3667 // we make them weak and invoke the gc, but we should not hit the weak-callback yet
3668 // as the varproperties array of each vmemo still references the resource.
3669 icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
3670 ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
3672 QVERIFY(propertyVarWeakRefCallbackCount == 0);
3674 // now we deassign the var prop, which should trigger collection of item subtrees.
3675 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
3676 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3677 // ensure that there are only weak handles to the underlying varProperties array remaining.
3679 QCOMPARE(propertyVarWeakRefCallbackCount, 2); // should have been called for both, since all refs should be weak.
3681 // since there are no parent vmemo's to keep implicit references alive, and the only handles
3682 // to what remains are weak, all varProperties arrays must have been collected.
3685 void tst_qdeclarativeecmascript::propertyVarInheritance2()
3687 int propertyVarWeakRefCallbackCount = 0;
3689 // The particular component under test here does NOT have a chain of references; the
3690 // only link between rootObject and childObject is that rootObject is the parent of childObject.
3691 QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.circular.2.qml"));
3692 QObject *object = component.create();
3693 QVERIFY(object != 0);
3694 QMetaObject::invokeMethod(object, "assignCircular");
3695 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3696 QObject *rootObject = object->property("vp").value<QObject*>();
3697 QVERIFY(rootObject != 0);
3698 QObject *childObject = rootObject->findChild<QObject*>("text");
3699 QVERIFY(childObject != 0);
3700 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
3701 QCOMPARE(childObject->property("textCanary").toInt(), 10);
3702 v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
3705 propertyVarWeakRefCallbackCount = 0; // reset callback count.
3706 childObjectVarArrayValueHandle = qPersistentNew(((QDeclarativeVMEMetaObject *)(childObject->metaObject()))->vmeProperty(58));
3707 childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
3709 QVERIFY(propertyVarWeakRefCallbackCount == 0); // should not have been collected yet.
3710 QCOMPARE(childObject->property("textCanary").toInt(), 10);
3712 QMetaObject::invokeMethod(object, "deassignCircular");
3713 QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3714 QVERIFY(propertyVarWeakRefCallbackCount == 1); // should have been collected now.
3718 // Ensure that QObject type conversion works on binding assignment
3719 void tst_qdeclarativeecmascript::elementAssign()
3721 QDeclarativeComponent component(&engine, TEST_FILE("elementAssign.qml"));
3723 QObject *object = component.create();
3724 QVERIFY(object != 0);
3726 QCOMPARE(object->property("test").toBool(), true);
3732 void tst_qdeclarativeecmascript::objectPassThroughSignals()
3734 QDeclarativeComponent component(&engine, TEST_FILE("objectsPassThroughSignals.qml"));
3736 QObject *object = component.create();
3737 QVERIFY(object != 0);
3739 QCOMPARE(object->property("test").toBool(), true);
3745 void tst_qdeclarativeecmascript::objectConversion()
3747 QDeclarativeComponent component(&engine, TEST_FILE("objectConversion.qml"));
3749 QObject *object = component.create();
3750 QVERIFY(object != 0);
3752 QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
3753 QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
3760 void tst_qdeclarativeecmascript::booleanConversion()
3762 QDeclarativeComponent component(&engine, TEST_FILE("booleanConversion.qml"));
3764 QObject *object = component.create();
3765 QVERIFY(object != 0);
3767 QCOMPARE(object->property("test_true1").toBool(), true);
3768 QCOMPARE(object->property("test_true2").toBool(), true);
3769 QCOMPARE(object->property("test_true3").toBool(), true);
3770 QCOMPARE(object->property("test_true4").toBool(), true);
3771 QCOMPARE(object->property("test_true5").toBool(), true);
3773 QCOMPARE(object->property("test_false1").toBool(), false);
3774 QCOMPARE(object->property("test_false2").toBool(), false);
3775 QCOMPARE(object->property("test_false3").toBool(), false);
3780 void tst_qdeclarativeecmascript::handleReferenceManagement()
3785 // Linear QObject reference
3786 QDeclarativeEngine hrmEngine;
3787 QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.object.1.qml"));
3788 QObject *object = component.create();
3789 QVERIFY(object != 0);
3790 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
3791 cro->setDtorCount(&dtorCount);
3792 QMetaObject::invokeMethod(object, "createReference");
3794 QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
3796 hrmEngine.collectGarbage();
3797 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3798 QCOMPARE(dtorCount, 3);
3803 // Circular QObject reference
3804 QDeclarativeEngine hrmEngine;
3805 QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.object.2.qml"));
3806 QObject *object = component.create();
3807 QVERIFY(object != 0);
3808 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
3809 cro->setDtorCount(&dtorCount);
3810 QMetaObject::invokeMethod(object, "circularReference");
3812 QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
3814 hrmEngine.collectGarbage();
3815 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3816 QCOMPARE(dtorCount, 3);
3821 // Linear handle reference
3822 QDeclarativeEngine hrmEngine;
3823 QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.handle.1.qml"));
3824 QObject *object = component.create();
3825 QVERIFY(object != 0);
3826 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
3828 crh->setDtorCount(&dtorCount);
3829 QMetaObject::invokeMethod(object, "createReference");
3830 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
3831 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
3832 QVERIFY(first != 0);
3833 QVERIFY(second != 0);
3834 first->addReference(QDeclarativeData::get(second)->v8object); // create reference
3835 // now we have to reparent second and make second owned by JS.
3836 second->setParent(0);
3837 QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership);
3839 QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
3841 hrmEngine.collectGarbage();
3842 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3843 QCOMPARE(dtorCount, 3);
3848 // Circular handle reference
3849 QDeclarativeEngine hrmEngine;
3850 QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.handle.2.qml"));
3851 QObject *object = component.create();
3852 QVERIFY(object != 0);
3853 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
3855 crh->setDtorCount(&dtorCount);
3856 QMetaObject::invokeMethod(object, "circularReference");
3857 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
3858 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
3859 QVERIFY(first != 0);
3860 QVERIFY(second != 0);
3861 first->addReference(QDeclarativeData::get(second)->v8object); // create circular reference
3862 second->addReference(QDeclarativeData::get(first)->v8object); // note: must be weak.
3863 // now we have to reparent and change ownership.
3864 first->setParent(0);
3865 second->setParent(0);
3866 QDeclarativeEngine::setObjectOwnership(first, QDeclarativeEngine::JavaScriptOwnership);
3867 QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership);
3869 QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
3871 hrmEngine.collectGarbage();
3872 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3873 QCOMPARE(dtorCount, 3);
3878 // multiple engine interaction - linear reference
3879 QDeclarativeEngine hrmEngine1;
3880 QDeclarativeEngine hrmEngine2;
3881 QDeclarativeComponent component1(&hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
3882 QDeclarativeComponent component2(&hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
3883 QObject *object1 = component1.create();
3884 QObject *object2 = component2.create();
3885 QVERIFY(object1 != 0);
3886 QVERIFY(object2 != 0);
3887 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
3888 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
3891 crh1->setDtorCount(&dtorCount);
3892 crh2->setDtorCount(&dtorCount);
3893 QMetaObject::invokeMethod(object1, "createReference");
3894 QMetaObject::invokeMethod(object2, "createReference");
3895 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
3896 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
3897 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
3898 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
3899 QVERIFY(first1 != 0);
3900 QVERIFY(second1 != 0);
3901 QVERIFY(first2 != 0);
3902 QVERIFY(second2 != 0);
3903 first1->addReference(QDeclarativeData::get(second2)->v8object); // create reference across engines
3904 // now we have to reparent second2 and make second2 owned by JS.
3905 second2->setParent(0);
3906 QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
3908 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3909 QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
3912 hrmEngine1.collectGarbage();
3913 hrmEngine2.collectGarbage();
3914 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3915 QCOMPARE(dtorCount, 6);
3920 // multiple engine interaction - circular reference
3921 QDeclarativeEngine hrmEngine1;
3922 QDeclarativeEngine hrmEngine2;
3923 QDeclarativeComponent component1(&hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
3924 QDeclarativeComponent component2(&hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
3925 QObject *object1 = component1.create();
3926 QObject *object2 = component2.create();
3927 QVERIFY(object1 != 0);
3928 QVERIFY(object2 != 0);
3929 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
3930 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
3933 crh1->setDtorCount(&dtorCount);
3934 crh2->setDtorCount(&dtorCount);
3935 QMetaObject::invokeMethod(object1, "createReference");
3936 QMetaObject::invokeMethod(object2, "createReference");
3937 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
3938 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
3939 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
3940 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
3941 QVERIFY(first1 != 0);
3942 QVERIFY(second1 != 0);
3943 QVERIFY(first2 != 0);
3944 QVERIFY(second2 != 0);
3945 first1->addReference(QDeclarativeData::get(second1)->v8object); // create linear reference within engine1
3946 second1->addReference(QDeclarativeData::get(second2)->v8object); // create linear reference across engines
3947 second2->addReference(QDeclarativeData::get(first2)->v8object); // create linear reference within engine2
3948 first2->addReference(QDeclarativeData::get(first1)->v8object); // close the loop - circular ref across engines
3949 // now we have to reparent and change ownership to JS.
3950 first1->setParent(0);
3951 second1->setParent(0);
3952 first2->setParent(0);
3953 second2->setParent(0);
3954 QDeclarativeEngine::setObjectOwnership(first1, QDeclarativeEngine::JavaScriptOwnership);
3955 QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership);
3956 QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership);
3957 QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
3959 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3960 QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
3963 hrmEngine1.collectGarbage();
3964 hrmEngine2.collectGarbage();
3965 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3966 QCOMPARE(dtorCount, 6);
3971 // multiple engine interaction - linear reference with engine deletion
3972 QDeclarativeEngine *hrmEngine1 = new QDeclarativeEngine;
3973 QDeclarativeEngine *hrmEngine2 = new QDeclarativeEngine;
3974 QDeclarativeComponent component1(hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
3975 QDeclarativeComponent component2(hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
3976 QObject *object1 = component1.create();
3977 QObject *object2 = component2.create();
3978 QVERIFY(object1 != 0);
3979 QVERIFY(object2 != 0);
3980 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
3981 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
3984 crh1->setDtorCount(&dtorCount);
3985 crh2->setDtorCount(&dtorCount);
3986 QMetaObject::invokeMethod(object1, "createReference");
3987 QMetaObject::invokeMethod(object2, "createReference");
3988 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
3989 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
3990 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
3991 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
3992 QVERIFY(first1 != 0);
3993 QVERIFY(second1 != 0);
3994 QVERIFY(first2 != 0);
3995 QVERIFY(second2 != 0);
3996 first1->addReference(QDeclarativeData::get(second1)->v8object); // create linear reference within engine1
3997 second1->addReference(QDeclarativeData::get(second2)->v8object); // create linear reference across engines
3998 second2->addReference(QDeclarativeData::get(first2)->v8object); // create linear reference within engine2
3999 // now we have to reparent and change ownership to JS.
4000 first1->setParent(crh1);
4001 second1->setParent(0);
4002 first2->setParent(0);
4003 second2->setParent(0);
4004 QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership);
4005 QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership);
4006 QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
4008 QCOMPARE(dtorCount, 0);
4011 QCOMPARE(dtorCount, 0);
4014 hrmEngine1->collectGarbage();
4015 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
4016 QCOMPARE(dtorCount, 6);
4021 void tst_qdeclarativeecmascript::stringArg()
4023 QDeclarativeComponent component(&engine, TEST_FILE("stringArg.qml"));
4024 QObject *object = component.create();
4025 QVERIFY(object != 0);
4026 QMetaObject::invokeMethod(object, "success");
4027 QVERIFY(object->property("returnValue").toBool());
4029 QString w1 = TEST_FILE("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
4030 QTest::ignoreMessage(QtWarningMsg, w1.toAscii().constData());
4031 QMetaObject::invokeMethod(object, "failure");
4032 QVERIFY(object->property("returnValue").toBool());
4037 // Test that assigning a null object works
4038 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
4039 void tst_qdeclarativeecmascript::nullObjectBinding()
4041 QDeclarativeComponent component(&engine, TEST_FILE("nullObjectBinding.qml"));
4043 QObject *object = component.create();
4044 QVERIFY(object != 0);
4046 QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
4051 // Test that bindings don't evaluate once the engine has been destroyed
4052 void tst_qdeclarativeecmascript::deletedEngine()
4054 QDeclarativeEngine *engine = new QDeclarativeEngine;
4055 QDeclarativeComponent component(engine, TEST_FILE("deletedEngine.qml"));
4057 QObject *object = component.create();
4058 QVERIFY(object != 0);
4060 QCOMPARE(object->property("a").toInt(), 39);
4061 object->setProperty("b", QVariant(9));
4062 QCOMPARE(object->property("a").toInt(), 117);
4066 QCOMPARE(object->property("a").toInt(), 117);
4067 object->setProperty("b", QVariant(10));
4068 QCOMPARE(object->property("a").toInt(), 117);
4073 // Test the crashing part of QTBUG-9705
4074 void tst_qdeclarativeecmascript::libraryScriptAssert()
4076 QDeclarativeComponent component(&engine, TEST_FILE("libraryScriptAssert.qml"));
4078 QObject *object = component.create();
4079 QVERIFY(object != 0);
4084 void tst_qdeclarativeecmascript::variantsAssignedUndefined()
4086 QDeclarativeComponent component(&engine, TEST_FILE("variantsAssignedUndefined.qml"));
4088 QObject *object = component.create();
4089 QVERIFY(object != 0);
4091 QCOMPARE(object->property("test1").toInt(), 10);
4092 QCOMPARE(object->property("test2").toInt(), 11);
4094 object->setProperty("runTest", true);
4096 QCOMPARE(object->property("test1"), QVariant());
4097 QCOMPARE(object->property("test2"), QVariant());
4103 void tst_qdeclarativeecmascript::qtbug_9792()
4105 QDeclarativeComponent component(&engine, TEST_FILE("qtbug_9792.qml"));
4107 QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
4109 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
4110 QVERIFY(object != 0);
4112 QTest::ignoreMessage(QtDebugMsg, "Hello world!");
4113 object->basicSignal();
4117 transientErrorsMsgCount = 0;
4118 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
4120 object->basicSignal();
4122 qInstallMsgHandler(old);
4124 QCOMPARE(transientErrorsMsgCount, 0);
4129 // Verifies that QDeclarativeGuard<>s used in the vmemetaobject are cleaned correctly
4130 void tst_qdeclarativeecmascript::qtcreatorbug_1289()
4132 QDeclarativeComponent component(&engine, TEST_FILE("qtcreatorbug_1289.qml"));
4134 QObject *o = component.create();
4137 QObject *nested = qvariant_cast<QObject *>(o->property("object"));
4138 QVERIFY(nested != 0);
4140 QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
4143 nested = qvariant_cast<QObject *>(o->property("object"));
4144 QVERIFY(nested == 0);
4146 // If the bug is present, the next line will crash
4150 // Test that we shut down without stupid warnings
4151 void tst_qdeclarativeecmascript::noSpuriousWarningsAtShutdown()
4154 QDeclarativeComponent component(&engine, TEST_FILE("noSpuriousWarningsAtShutdown.qml"));
4156 QObject *o = component.create();
4158 transientErrorsMsgCount = 0;
4159 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
4163 qInstallMsgHandler(old);
4165 QCOMPARE(transientErrorsMsgCount, 0);
4170 QDeclarativeComponent component(&engine, TEST_FILE("noSpuriousWarningsAtShutdown.2.qml"));
4172 QObject *o = component.create();
4174 transientErrorsMsgCount = 0;
4175 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
4179 qInstallMsgHandler(old);
4181 QCOMPARE(transientErrorsMsgCount, 0);
4185 void tst_qdeclarativeecmascript::canAssignNullToQObject()
4188 QDeclarativeComponent component(&engine, TEST_FILE("canAssignNullToQObject.1.qml"));
4190 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4193 QVERIFY(o->objectProperty() != 0);
4195 o->setProperty("runTest", true);
4197 QVERIFY(o->objectProperty() == 0);
4203 QDeclarativeComponent component(&engine, TEST_FILE("canAssignNullToQObject.2.qml"));
4205 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4208 QVERIFY(o->objectProperty() == 0);
4214 void tst_qdeclarativeecmascript::functionAssignment_fromBinding()
4216 QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.1.qml"));
4218 QString url = component.url().toString();
4219 QString warning = url + ":4: Unable to assign a function to a property.";
4220 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
4222 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4225 QVERIFY(!o->property("a").isValid());
4230 void tst_qdeclarativeecmascript::functionAssignment_fromJS()
4232 QFETCH(QString, triggerProperty);
4234 QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.2.qml"));
4235 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
4237 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4239 QVERIFY(!o->property("a").isValid());
4241 o->setProperty("aNumber", QVariant(5));
4242 o->setProperty(triggerProperty.toUtf8().constData(), true);
4243 QCOMPARE(o->property("a"), QVariant(50));
4245 o->setProperty("aNumber", QVariant(10));
4246 QCOMPARE(o->property("a"), QVariant(100));
4251 void tst_qdeclarativeecmascript::functionAssignment_fromJS_data()
4253 QTest::addColumn<QString>("triggerProperty");
4255 QTest::newRow("assign to property") << "assignToProperty";
4256 QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
4258 QTest::newRow("assign to value type") << "assignToValueType";
4260 QTest::newRow("use 'this'") << "assignWithThis";
4261 QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
4264 void tst_qdeclarativeecmascript::functionAssignmentfromJS_invalid()
4266 QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.2.qml"));
4267 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
4269 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4271 QVERIFY(!o->property("a").isValid());
4273 o->setProperty("assignFuncWithoutReturn", true);
4274 QVERIFY(!o->property("a").isValid());
4276 QString url = component.url().toString();
4277 QString warning = url + ":67: Unable to assign QString to int";
4278 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
4279 o->setProperty("assignWrongType", true);
4281 warning = url + ":71: Unable to assign QString to int";
4282 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
4283 o->setProperty("assignWrongTypeToValueType", true);
4288 void tst_qdeclarativeecmascript::eval()
4290 QDeclarativeComponent component(&engine, TEST_FILE("eval.qml"));
4292 QObject *o = component.create();
4295 QCOMPARE(o->property("test1").toBool(), true);
4296 QCOMPARE(o->property("test2").toBool(), true);
4297 QCOMPARE(o->property("test3").toBool(), true);
4298 QCOMPARE(o->property("test4").toBool(), true);
4299 QCOMPARE(o->property("test5").toBool(), true);
4304 void tst_qdeclarativeecmascript::function()
4306 QDeclarativeComponent component(&engine, TEST_FILE("function.qml"));
4308 QObject *o = component.create();
4311 QCOMPARE(o->property("test1").toBool(), true);
4312 QCOMPARE(o->property("test2").toBool(), true);
4313 QCOMPARE(o->property("test3").toBool(), true);
4318 // Test the "Qt.include" method
4319 void tst_qdeclarativeecmascript::include()
4321 // Non-library relative include
4323 QDeclarativeComponent component(&engine, TEST_FILE("include.qml"));
4324 QObject *o = component.create();
4327 QCOMPARE(o->property("test0").toInt(), 99);
4328 QCOMPARE(o->property("test1").toBool(), true);
4329 QCOMPARE(o->property("test2").toBool(), true);
4330 QCOMPARE(o->property("test2_1").toBool(), true);
4331 QCOMPARE(o->property("test3").toBool(), true);
4332 QCOMPARE(o->property("test3_1").toBool(), true);
4337 // Library relative include
4339 QDeclarativeComponent component(&engine, TEST_FILE("include_shared.qml"));
4340 QObject *o = component.create();
4343 QCOMPARE(o->property("test0").toInt(), 99);
4344 QCOMPARE(o->property("test1").toBool(), true);
4345 QCOMPARE(o->property("test2").toBool(), true);
4346 QCOMPARE(o->property("test2_1").toBool(), true);
4347 QCOMPARE(o->property("test3").toBool(), true);
4348 QCOMPARE(o->property("test3_1").toBool(), true);
4355 QDeclarativeComponent component(&engine, TEST_FILE("include_callback.qml"));
4356 QObject *o = component.create();
4359 QCOMPARE(o->property("test1").toBool(), true);
4360 QCOMPARE(o->property("test2").toBool(), true);
4361 QCOMPARE(o->property("test3").toBool(), true);
4362 QCOMPARE(o->property("test4").toBool(), true);
4363 QCOMPARE(o->property("test5").toBool(), true);
4364 QCOMPARE(o->property("test6").toBool(), true);
4369 // Including file with ".pragma library"
4371 QDeclarativeComponent component(&engine, TEST_FILE("include_pragma.qml"));
4372 QObject *o = component.create();
4374 QCOMPARE(o->property("test1").toInt(), 100);
4381 TestHTTPServer server(8111);
4382 QVERIFY(server.isValid());
4383 server.serveDirectory(TESTDATA(""));
4385 QDeclarativeComponent component(&engine, TEST_FILE("include_remote.qml"));
4386 QObject *o = component.create();
4389 QTRY_VERIFY(o->property("done").toBool() == true);
4390 QTRY_VERIFY(o->property("done2").toBool() == true);
4392 QCOMPARE(o->property("test1").toBool(), true);
4393 QCOMPARE(o->property("test2").toBool(), true);
4394 QCOMPARE(o->property("test3").toBool(), true);
4395 QCOMPARE(o->property("test4").toBool(), true);
4396 QCOMPARE(o->property("test5").toBool(), true);
4398 QCOMPARE(o->property("test6").toBool(), true);
4399 QCOMPARE(o->property("test7").toBool(), true);
4400 QCOMPARE(o->property("test8").toBool(), true);
4401 QCOMPARE(o->property("test9").toBool(), true);
4402 QCOMPARE(o->property("test10").toBool(), true);
4409 TestHTTPServer server(8111);
4410 QVERIFY(server.isValid());
4411 server.serveDirectory(TESTDATA(""));
4413 QDeclarativeComponent component(&engine, TEST_FILE("include_remote_missing.qml"));
4414 QObject *o = component.create();
4417 QTRY_VERIFY(o->property("done").toBool() == true);
4419 QCOMPARE(o->property("test1").toBool(), true);
4420 QCOMPARE(o->property("test2").toBool(), true);
4421 QCOMPARE(o->property("test3").toBool(), true);
4427 void tst_qdeclarativeecmascript::signalHandlers()
4429 QDeclarativeComponent component(&engine, TEST_FILE("signalHandlers.qml"));
4430 QObject *o = component.create();
4433 QVERIFY(o->property("count").toInt() == 0);
4434 QMetaObject::invokeMethod(o, "testSignalCall");
4435 QCOMPARE(o->property("count").toInt(), 1);
4437 QMetaObject::invokeMethod(o, "testSignalHandlerCall");
4438 QCOMPARE(o->property("count").toInt(), 1);
4439 QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
4441 QVERIFY(o->property("funcCount").toInt() == 0);
4442 QMetaObject::invokeMethod(o, "testSignalConnection");
4443 QCOMPARE(o->property("funcCount").toInt(), 1);
4445 QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
4446 QCOMPARE(o->property("funcCount").toInt(), 2);
4448 QMetaObject::invokeMethod(o, "testSignalDefined");
4449 QCOMPARE(o->property("definedResult").toBool(), true);
4451 QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
4452 QCOMPARE(o->property("definedHandlerResult").toBool(), true);
4457 void tst_qdeclarativeecmascript::qtbug_10696()
4459 QDeclarativeComponent component(&engine, TEST_FILE("qtbug_10696.qml"));
4460 QObject *o = component.create();
4465 void tst_qdeclarativeecmascript::qtbug_11606()
4467 QDeclarativeComponent component(&engine, TEST_FILE("qtbug_11606.qml"));
4468 QObject *o = component.create();
4470 QCOMPARE(o->property("test").toBool(), true);
4474 void tst_qdeclarativeecmascript::qtbug_11600()
4476 QDeclarativeComponent component(&engine, TEST_FILE("qtbug_11600.qml"));
4477 QObject *o = component.create();
4479 QCOMPARE(o->property("test").toBool(), true);
4483 // Reading and writing non-scriptable properties should fail
4484 void tst_qdeclarativeecmascript::nonscriptable()
4486 QDeclarativeComponent component(&engine, TEST_FILE("nonscriptable.qml"));
4487 QObject *o = component.create();
4489 QCOMPARE(o->property("readOk").toBool(), true);
4490 QCOMPARE(o->property("writeOk").toBool(), true);
4494 // deleteLater() should not be callable from QML
4495 void tst_qdeclarativeecmascript::deleteLater()
4497 QDeclarativeComponent component(&engine, TEST_FILE("deleteLater.qml"));
4498 QObject *o = component.create();
4500 QCOMPARE(o->property("test").toBool(), true);
4504 void tst_qdeclarativeecmascript::in()
4506 QDeclarativeComponent component(&engine, TEST_FILE("in.qml"));
4507 QObject *o = component.create();
4509 QCOMPARE(o->property("test1").toBool(), true);
4510 QCOMPARE(o->property("test2").toBool(), true);
4514 void tst_qdeclarativeecmascript::sharedAttachedObject()
4516 QDeclarativeComponent component(&engine, TEST_FILE("sharedAttachedObject.qml"));
4517 QObject *o = component.create();
4519 QCOMPARE(o->property("test1").toBool(), true);
4520 QCOMPARE(o->property("test2").toBool(), true);
4525 void tst_qdeclarativeecmascript::objectName()
4527 QDeclarativeComponent component(&engine, TEST_FILE("objectName.qml"));
4528 QObject *o = component.create();
4531 QCOMPARE(o->property("test1").toString(), QString("hello"));
4532 QCOMPARE(o->property("test2").toString(), QString("ell"));
4534 o->setObjectName("world");
4536 QCOMPARE(o->property("test1").toString(), QString("world"));
4537 QCOMPARE(o->property("test2").toString(), QString("orl"));
4542 void tst_qdeclarativeecmascript::writeRemovesBinding()
4544 QDeclarativeComponent component(&engine, TEST_FILE("writeRemovesBinding.qml"));
4545 QObject *o = component.create();
4548 QCOMPARE(o->property("test").toBool(), true);
4553 // Test bindings assigned to alias properties actually assign to the alias' target
4554 void tst_qdeclarativeecmascript::aliasBindingsAssignCorrectly()
4556 QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsAssignCorrectly.qml"));
4557 QObject *o = component.create();
4560 QCOMPARE(o->property("test").toBool(), true);
4565 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
4566 void tst_qdeclarativeecmascript::aliasBindingsOverrideTarget()
4569 QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.qml"));
4570 QObject *o = component.create();
4573 QCOMPARE(o->property("test").toBool(), true);
4579 QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.2.qml"));
4580 QObject *o = component.create();
4583 QCOMPARE(o->property("test").toBool(), true);
4589 QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.3.qml"));
4590 QObject *o = component.create();
4593 QCOMPARE(o->property("test").toBool(), true);
4599 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
4600 void tst_qdeclarativeecmascript::aliasWritesOverrideBindings()
4603 QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.qml"));
4604 QObject *o = component.create();
4607 QCOMPARE(o->property("test").toBool(), true);
4613 QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.2.qml"));
4614 QObject *o = component.create();
4617 QCOMPARE(o->property("test").toBool(), true);
4623 QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.3.qml"));
4624 QObject *o = component.create();
4627 QCOMPARE(o->property("test").toBool(), true);
4633 // Allow an alais to a composite element
4635 void tst_qdeclarativeecmascript::aliasToCompositeElement()
4637 QDeclarativeComponent component(&engine, TEST_FILE("aliasToCompositeElement.qml"));
4639 QObject *object = component.create();
4640 QVERIFY(object != 0);
4645 void tst_qdeclarativeecmascript::revisionErrors()
4648 QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors.qml"));
4649 QString url = component.url().toString();
4651 QString warning1 = url + ":8: ReferenceError: Can't find variable: prop2";
4652 QString warning2 = url + ":11: ReferenceError: Can't find variable: prop2";
4653 QString warning3 = url + ":13: ReferenceError: Can't find variable: method2";
4655 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
4656 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
4657 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
4658 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
4659 QVERIFY(object != 0);
4663 QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors2.qml"));
4664 QString url = component.url().toString();
4666 // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
4667 // method2, prop2 from MyRevisionedClass not available
4668 // method4, prop4 from MyRevisionedSubclass not available
4669 QString warning1 = url + ":8: ReferenceError: Can't find variable: prop2";
4670 QString warning2 = url + ":14: ReferenceError: Can't find variable: prop2";
4671 QString warning3 = url + ":10: ReferenceError: Can't find variable: prop4";
4672 QString warning4 = url + ":16: ReferenceError: Can't find variable: prop4";
4673 QString warning5 = url + ":20: ReferenceError: Can't find variable: method2";
4675 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
4676 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
4677 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
4678 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
4679 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
4680 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
4681 QVERIFY(object != 0);
4685 QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors3.qml"));
4686 QString url = component.url().toString();
4688 // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
4689 // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
4690 QString warning1 = url + ":30: ReferenceError: Can't find variable: methodD";
4691 QString warning2 = url + ":10: ReferenceError: Can't find variable: propD";
4692 QString warning3 = url + ":20: ReferenceError: Can't find variable: propD";
4693 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
4694 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
4695 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
4696 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
4697 QVERIFY(object != 0);
4702 void tst_qdeclarativeecmascript::revision()
4705 QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision.qml"));
4706 QString url = component.url().toString();
4708 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
4709 QVERIFY(object != 0);
4713 QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision2.qml"));
4714 QString url = component.url().toString();
4716 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
4717 QVERIFY(object != 0);
4721 QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision3.qml"));
4722 QString url = component.url().toString();
4724 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
4725 QVERIFY(object != 0);
4728 // Test that non-root classes can resolve revisioned methods
4730 QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision4.qml"));
4732 QObject *object = component.create();
4733 QVERIFY(object != 0);
4734 QCOMPARE(object->property("test").toReal(), 11.);
4739 void tst_qdeclarativeecmascript::realToInt()
4741 QDeclarativeComponent component(&engine, TEST_FILE("realToInt.qml"));
4742 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
4743 QVERIFY(object != 0);
4745 QMetaObject::invokeMethod(object, "test1");
4746 QCOMPARE(object->value(), int(4));
4747 QMetaObject::invokeMethod(object, "test2");
4748 QCOMPARE(object->value(), int(8));
4750 void tst_qdeclarativeecmascript::dynamicString()
4752 QDeclarativeComponent component(&engine, TEST_FILE("dynamicString.qml"));
4753 QObject *object = component.create();
4754 QVERIFY(object != 0);
4755 QCOMPARE(object->property("stringProperty").toString(),
4756 QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
4759 void tst_qdeclarativeecmascript::automaticSemicolon()
4761 QDeclarativeComponent component(&engine, TEST_FILE("automaticSemicolon.qml"));
4762 QObject *object = component.create();
4763 QVERIFY(object != 0);
4766 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
4767 void tst_qdeclarativeecmascript::doubleEvaluate()
4769 QDeclarativeComponent component(&engine, TEST_FILE("doubleEvaluate.qml"));
4770 QObject *object = component.create();
4771 QVERIFY(object != 0);
4772 WriteCounter *wc = qobject_cast<WriteCounter *>(object);
4774 QCOMPARE(wc->count(), 1);
4776 wc->setProperty("x", 9);
4778 QCOMPARE(wc->count(), 2);
4783 QTEST_MAIN(tst_qdeclarativeecmascript)
4785 #include "tst_qdeclarativeecmascript.moc"