1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
41 #include <QtTest/QtTest>
42 #include <QtQml/qqmlcomponent.h>
43 #include <QtQml/qqmlengine.h>
44 #include <QtQml/qqmlexpression.h>
45 #include <QtQml/qqmlcontext.h>
46 #include <QtCore/qfileinfo.h>
47 #include <QtCore/qdebug.h>
48 #include <QtQml/private/qqmlguard_p.h>
49 #include <QtCore/qdir.h>
50 #include <QtCore/qnumeric.h>
51 #include <private/qqmlengine_p.h>
52 #include <private/qqmlvmemetaobject_p.h>
53 #include <private/qv4compiler_p.h>
54 #include "testtypes.h"
55 #include "testhttpserver.h"
56 #include "../../shared/util.h"
59 This test covers evaluation of ECMAScript expressions and bindings from within
60 QML. This does not include static QML language issues.
62 Static QML language issues are covered in qmllanguage
65 class tst_qqmlecmascript : public QQmlDataTest
69 tst_qqmlecmascript() {}
73 void assignBasicTypes();
74 void assignDate_data();
76 void exportDate_data();
78 void idShortcutInvalidates();
79 void boolPropertiesEvaluateAsBool();
81 void signalAssignment();
83 void basicExpressions();
84 void basicExpressions_data();
85 void arrayExpressions();
86 void contextPropertiesTriggerReeval();
87 void objectPropertiesTriggerReeval();
88 void deferredProperties();
89 void deferredPropertiesErrors();
90 void extensionObjects();
91 void overrideExtensionProperties();
92 void attachedProperties();
94 void valueTypeFunctions();
95 void constantsOverrideBindings();
96 void outerBindingOverridesInnerBinding();
97 void aliasPropertyAndBinding();
98 void aliasPropertyReset();
99 void nonExistentAttachedObject();
102 void signalParameterTypes();
103 void objectsCompareAsEqual();
104 void componentCreation_data();
105 void componentCreation();
106 void dynamicCreation_data();
107 void dynamicCreation();
108 void dynamicDestruction();
109 void objectToString();
110 void objectHasOwnProperty();
111 void selfDeletingBinding();
112 void extendedObjectPropertyLookup();
113 void extendedObjectPropertyLookup2();
115 void functionErrors();
116 void propertyAssignmentErrors();
117 void signalTriggeredBindings();
118 void listProperties();
119 void exceptionClearsOnReeval();
120 void exceptionSlotProducesWarning();
121 void exceptionBindingProducesWarning();
122 void compileInvalidBinding();
123 void transientErrors();
124 void shutdownErrors();
125 void compositePropertyType();
127 void undefinedResetsProperty();
128 void listToVariant();
129 void listAssignment();
130 void multiEngineObject();
131 void deletedObject();
132 void attachedPropertyScope();
133 void scriptConnect();
134 void scriptDisconnect();
136 void cppOwnershipReturnValue();
137 void ownershipCustomReturnValue();
138 void ownershipRootObject();
139 void ownershipConsistency();
140 void ownershipQmlIncubated();
141 void qlistqobjectMethods();
142 void strictlyEquals();
144 void numberAssignment();
145 void propertySplicing();
146 void signalWithUnknownTypes();
147 void signalWithJSValueInVariant_data();
148 void signalWithJSValueInVariant();
149 void signalWithJSValueInVariant_twoEngines_data();
150 void signalWithJSValueInVariant_twoEngines();
151 void signalWithQJSValue_data();
152 void signalWithQJSValue();
153 void moduleApi_data();
155 void importScripts_data();
156 void importScripts();
157 void scarceResources();
158 void scarceResources_data();
159 void scarceResources_other();
160 void propertyChangeSlots();
161 void propertyVar_data();
163 void propertyQJSValue_data();
164 void propertyQJSValue();
165 void propertyVarCpp();
166 void propertyVarOwnership();
167 void propertyVarImplicitOwnership();
168 void propertyVarReparent();
169 void propertyVarReparentNullContext();
170 void propertyVarCircular();
171 void propertyVarCircular2();
172 void propertyVarInheritance();
173 void propertyVarInheritance2();
174 void elementAssign();
175 void objectPassThroughSignals();
176 void objectConversion();
177 void booleanConversion();
178 void handleReferenceManagement();
180 void readonlyDeclaration();
181 void sequenceConversionRead();
182 void sequenceConversionWrite();
183 void sequenceConversionArray();
184 void sequenceConversionIndexes();
185 void sequenceConversionThreads();
186 void sequenceConversionBindings();
187 void sequenceConversionCopy();
188 void assignSequenceTypes();
191 void singleV8BindingDestroyedDuringEvaluation();
193 #ifndef QT_NO_WIDGETS
196 void dynamicCreationCrash();
197 void dynamicCreationOwnership();
199 void nullObjectBinding();
200 void deletedEngine();
201 void libraryScriptAssert();
202 void variantsAssignedUndefined();
204 void qtcreatorbug_1289();
205 void noSpuriousWarningsAtShutdown();
206 void canAssignNullToQObject();
207 void functionAssignment_fromBinding();
208 void functionAssignment_fromJS();
209 void functionAssignment_fromJS_data();
210 void functionAssignmentfromJS_invalid();
211 void functionAssignment_afterBinding();
214 void functionException();
219 void qobjectConnectionListExceptionHandling();
220 void nonscriptable();
222 void objectNameChangedSignal();
223 void destroyedSignal();
227 void sharedAttachedObject();
229 void writeRemovesBinding();
230 void aliasBindingsAssignCorrectly();
231 void aliasBindingsOverrideTarget();
232 void aliasWritesOverrideBindings();
233 void aliasToCompositeElement();
236 void urlPropertyWithEncoding();
237 void urlListPropertyWithEncoding();
238 void dynamicString();
240 void signalHandlers();
241 void doubleEvaluate();
243 void nonNotifyable();
244 void deleteWhileBindingRunning();
245 void callQtInvokables();
246 void invokableObjectArg();
247 void invokableObjectRet();
248 void invokableEnumRet();
251 void qtbug_22843_data();
253 void rewriteMultiLineStrings();
254 void revisionErrors();
256 void invokableWithQObjectDerived();
257 void realTypePrecision();
258 void registeredFlagMethod();
259 void deleteLaterObjectMethodCall();
260 void automaticSemicolon();
261 void unaryExpression();
262 void switchStatement();
263 void withStatement();
265 void replaceBinding();
266 void deleteRootObjectInCreation();
267 void onDestruction();
268 void bindingSuppression();
269 void signalEmitted();
271 void qqmldataDestroyed();
274 void overrideDataAssert();
275 void fallbackBindings_data();
276 void fallbackBindings();
279 static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
283 void tst_qqmlecmascript::initTestCase()
285 QQmlDataTest::initTestCase();
288 QString dataDir(dataDirectory() + QLatin1Char('/') + QLatin1String("lib"));
289 engine.addImportPath(dataDir);
292 void tst_qqmlecmascript::assignBasicTypes()
295 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.qml"));
296 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
297 QVERIFY(object != 0);
298 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
299 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
300 QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue);
301 QCOMPARE(object->stringProperty(), QString("Hello World!"));
302 QCOMPARE(object->uintProperty(), uint(10));
303 QCOMPARE(object->intProperty(), -19);
304 QCOMPARE((float)object->realProperty(), float(23.2));
305 QCOMPARE((float)object->doubleProperty(), float(-19.75));
306 QCOMPARE((float)object->floatProperty(), float(8.5));
307 QCOMPARE(object->colorProperty(), QColor("red"));
308 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
309 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
310 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
311 QCOMPARE(object->pointProperty(), QPoint(99,13));
312 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
313 QCOMPARE(object->sizeProperty(), QSize(99, 13));
314 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
315 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
316 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
317 QCOMPARE(object->boolProperty(), true);
318 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
319 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
320 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
324 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.2.qml"));
325 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
326 QVERIFY(object != 0);
327 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
328 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
329 QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue);
330 QCOMPARE(object->stringProperty(), QString("Hello World!"));
331 QCOMPARE(object->uintProperty(), uint(10));
332 QCOMPARE(object->intProperty(), -19);
333 QCOMPARE((float)object->realProperty(), float(23.2));
334 QCOMPARE((float)object->doubleProperty(), float(-19.75));
335 QCOMPARE((float)object->floatProperty(), float(8.5));
336 QCOMPARE(object->colorProperty(), QColor("red"));
337 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
338 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
339 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
340 QCOMPARE(object->pointProperty(), QPoint(99,13));
341 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
342 QCOMPARE(object->sizeProperty(), QSize(99, 13));
343 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
344 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
345 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
346 QCOMPARE(object->boolProperty(), true);
347 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
348 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
349 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
354 void tst_qqmlecmascript::assignDate_data()
356 QTest::addColumn<QUrl>("source");
358 QTest::newRow("Component.onComplete JS Parse") << testFileUrl("assignDate.qml");
359 QTest::newRow("Component.onComplete JS") << testFileUrl("assignDate.1.qml");
360 QTest::newRow("Binding JS") << testFileUrl("assignDate.2.qml");
361 QTest::newRow("Binding UTC") << testFileUrl("assignDate.3.qml");
362 QTest::newRow("Binding JS UTC") << testFileUrl("assignDate.4.qml");
363 QTest::newRow("Binding UTC+2") << testFileUrl("assignDate.5.qml");
364 QTest::newRow("Binding JS UTC+2 ") << testFileUrl("assignDate.6.qml");
367 void tst_qqmlecmascript::assignDate()
369 QFETCH(QUrl, source);
371 QQmlComponent component(&engine, source);
372 QScopedPointer<QObject> obj(component.create());
373 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
374 QVERIFY(object != 0);
376 // Dates received from JS are automatically converted to local time
377 QDate expectedDate(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 0), Qt::UTC).toLocalTime().date());
378 QDateTime expectedDateTime(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 1), Qt::UTC).toLocalTime());
379 QDateTime expectedDateTime2(QDateTime(QDate(2009, 5, 12), QTime(23, 59, 59), Qt::UTC).toLocalTime());
381 QCOMPARE(object->dateProperty(), expectedDate);
382 QCOMPARE(object->dateTimeProperty(), expectedDateTime);
383 QCOMPARE(object->dateTimeProperty2(), expectedDateTime2);
384 QCOMPARE(object->boolProperty(), true);
387 void tst_qqmlecmascript::exportDate_data()
389 QTest::addColumn<QUrl>("source");
390 QTest::addColumn<QDateTime>("datetime");
392 // Verify that we can export datetime information to QML and that consumers can access
393 // the data correctly provided they know the TZ info associated with the value
395 const QDate date(2009, 5, 12);
396 const QTime early(0, 0, 1);
397 const QTime late(23, 59, 59);
398 const int offset(((11 * 60) + 30) * 60);
400 QTest::newRow("Localtime early") << testFileUrl("exportDate.qml") << QDateTime(date, early, Qt::LocalTime);
401 QTest::newRow("Localtime late") << testFileUrl("exportDate.2.qml") << QDateTime(date, late, Qt::LocalTime);
402 QTest::newRow("UTC early") << testFileUrl("exportDate.3.qml") << QDateTime(date, early, Qt::UTC);
403 QTest::newRow("UTC late") << testFileUrl("exportDate.4.qml") << QDateTime(date, late, Qt::UTC);
405 QDateTime dt(date, early, Qt::OffsetFromUTC);
406 dt.setUtcOffset(offset);
407 QTest::newRow("+11:30 early") << testFileUrl("exportDate.5.qml") << dt;
410 QDateTime dt(date, late, Qt::OffsetFromUTC);
411 dt.setUtcOffset(offset);
412 QTest::newRow("+11:30 late") << testFileUrl("exportDate.6.qml") << dt;
415 QDateTime dt(date, early, Qt::OffsetFromUTC);
416 dt.setUtcOffset(-offset);
417 QTest::newRow("-11:30 early") << testFileUrl("exportDate.7.qml") << dt;
420 QDateTime dt(date, late, Qt::OffsetFromUTC);
421 dt.setUtcOffset(-offset);
422 QTest::newRow("-11:30 late") << testFileUrl("exportDate.8.qml") << dt;
426 void tst_qqmlecmascript::exportDate()
428 QFETCH(QUrl, source);
429 QFETCH(QDateTime, datetime);
431 DateTimeExporter exporter(datetime);
434 e.rootContext()->setContextProperty("datetimeExporter", &exporter);
436 QQmlComponent component(&e, source);
437 QScopedPointer<QObject> obj(component.create());
438 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
439 QVERIFY(object != 0);
440 QCOMPARE(object->boolProperty(), true);
443 void tst_qqmlecmascript::idShortcutInvalidates()
446 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.qml"));
447 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
448 QVERIFY(object != 0);
449 QVERIFY(object->objectProperty() != 0);
450 delete object->objectProperty();
451 QVERIFY(object->objectProperty() == 0);
456 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.1.qml"));
457 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
458 QVERIFY(object != 0);
459 QVERIFY(object->objectProperty() != 0);
460 delete object->objectProperty();
461 QVERIFY(object->objectProperty() == 0);
466 void tst_qqmlecmascript::boolPropertiesEvaluateAsBool()
469 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.1.qml"));
470 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
471 QVERIFY(object != 0);
472 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
476 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.2.qml"));
477 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
478 QVERIFY(object != 0);
479 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
484 void tst_qqmlecmascript::signalAssignment()
487 QQmlComponent component(&engine, testFileUrl("signalAssignment.1.qml"));
488 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
489 QVERIFY(object != 0);
490 QCOMPARE(object->string(), QString());
491 emit object->basicSignal();
492 QCOMPARE(object->string(), QString("pass"));
497 QQmlComponent component(&engine, testFileUrl("signalAssignment.2.qml"));
498 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
499 QVERIFY(object != 0);
500 QCOMPARE(object->string(), QString());
501 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
502 QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
507 QQmlComponent component(&engine, testFileUrl("signalAssignment.3.qml"));
508 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
509 QVERIFY(object != 0);
510 QCOMPARE(object->string(), QString());
511 emit object->unnamedArgumentSignal(19, 10.25, "Hello world!");
512 QEXPECT_FAIL("", "QTBUG-24481", Continue);
513 QCOMPARE(object->string(), QString("pass 19 Hello world!"));
518 QQmlComponent component(&engine, testFileUrl("signalAssignment.4.qml"));
519 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
520 QVERIFY(object != 0);
521 QCOMPARE(object->string(), QString());
522 emit object->signalWithGlobalName(19);
523 QCOMPARE(object->string(), QString("pass 5"));
528 void tst_qqmlecmascript::methods()
531 QQmlComponent component(&engine, testFileUrl("methods.1.qml"));
532 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
533 QVERIFY(object != 0);
534 QCOMPARE(object->methodCalled(), false);
535 QCOMPARE(object->methodIntCalled(), false);
536 emit object->basicSignal();
537 QCOMPARE(object->methodCalled(), true);
538 QCOMPARE(object->methodIntCalled(), false);
543 QQmlComponent component(&engine, testFileUrl("methods.2.qml"));
544 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
545 QVERIFY(object != 0);
546 QCOMPARE(object->methodCalled(), false);
547 QCOMPARE(object->methodIntCalled(), false);
548 emit object->basicSignal();
549 QCOMPARE(object->methodCalled(), false);
550 QCOMPARE(object->methodIntCalled(), true);
555 QQmlComponent component(&engine, testFileUrl("methods.3.qml"));
556 QObject *object = component.create();
557 QVERIFY(object != 0);
558 QCOMPARE(object->property("test").toInt(), 19);
563 QQmlComponent component(&engine, testFileUrl("methods.4.qml"));
564 QObject *object = component.create();
565 QVERIFY(object != 0);
566 QCOMPARE(object->property("test").toInt(), 19);
567 QCOMPARE(object->property("test2").toInt(), 17);
568 QCOMPARE(object->property("test3").toInt(), 16);
573 QQmlComponent component(&engine, testFileUrl("methods.5.qml"));
574 QObject *object = component.create();
575 QVERIFY(object != 0);
576 QCOMPARE(object->property("test").toInt(), 9);
581 void tst_qqmlecmascript::bindingLoop()
583 QQmlComponent component(&engine, testFileUrl("bindingLoop.qml"));
584 QString warning = component.url().toString() + ":5:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\"";
585 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
586 QObject *object = component.create();
587 QVERIFY(object != 0);
591 void tst_qqmlecmascript::basicExpressions_data()
593 QTest::addColumn<QString>("expression");
594 QTest::addColumn<QVariant>("result");
595 QTest::addColumn<bool>("nest");
597 QTest::newRow("Syntax error (self test)") << "{console.log({'a':1'}.a)}" << QVariant() << false;
598 QTest::newRow("Context property") << "a" << QVariant(1944) << false;
599 QTest::newRow("Context property") << "a" << QVariant(1944) << true;
600 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << false;
601 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << true;
602 QTest::newRow("Overridden context property") << "b" << QVariant("Milk") << false;
603 QTest::newRow("Overridden context property") << "b" << QVariant("Cow") << true;
604 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << false;
605 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << true;
606 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object2") << false;
607 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object3") << true;
608 QTest::newRow("Default object property") << "horseLegs" << QVariant(4) << false;
609 QTest::newRow("Default object property") << "antLegs" << QVariant(6) << false;
610 QTest::newRow("Default object property") << "emuLegs" << QVariant(2) << false;
611 QTest::newRow("Nested default object property") << "horseLegs" << QVariant(4) << true;
612 QTest::newRow("Nested default object property") << "antLegs" << QVariant(7) << true;
613 QTest::newRow("Nested default object property") << "emuLegs" << QVariant(2) << true;
614 QTest::newRow("Nested default object property") << "humanLegs" << QVariant(2) << true;
615 QTest::newRow("Context property override default object property") << "millipedeLegs" << QVariant(100) << true;
618 void tst_qqmlecmascript::basicExpressions()
620 QFETCH(QString, expression);
621 QFETCH(QVariant, result);
627 MyDefaultObject1 default1;
628 MyDefaultObject3 default3;
629 object1.setStringProperty("Object1");
630 object2.setStringProperty("Object2");
631 object3.setStringProperty("Object3");
633 QQmlContext context(engine.rootContext());
634 QQmlContext nestedContext(&context);
636 context.setContextObject(&default1);
637 context.setContextProperty("a", QVariant(1944));
638 context.setContextProperty("b", QVariant("Milk"));
639 context.setContextProperty("object", &object1);
640 context.setContextProperty("objectOverride", &object2);
641 nestedContext.setContextObject(&default3);
642 nestedContext.setContextProperty("b", QVariant("Cow"));
643 nestedContext.setContextProperty("objectOverride", &object3);
644 nestedContext.setContextProperty("millipedeLegs", QVariant(100));
646 MyExpression expr(nest?&nestedContext:&context, expression);
647 QCOMPARE(expr.evaluate(), result);
650 void tst_qqmlecmascript::arrayExpressions()
656 QQmlContext context(engine.rootContext());
657 context.setContextProperty("a", &obj1);
658 context.setContextProperty("b", &obj2);
659 context.setContextProperty("c", &obj3);
661 MyExpression expr(&context, "[a, b, c, 10]");
662 QVariant result = expr.evaluate();
663 QCOMPARE(result.userType(), qMetaTypeId<QVariantList>());
664 QVariantList list = qvariant_cast<QVariantList>(result);
665 QCOMPARE(list.count(), 4);
666 QCOMPARE(list.at(0).value<QObject*>(), &obj1);
667 QCOMPARE(list.at(1).value<QObject*>(), &obj2);
668 QCOMPARE(list.at(2).value<QObject*>(), &obj3);
669 QCOMPARE(list.at(3).value<int>(), 10);
672 // Tests that modifying a context property will reevaluate expressions
673 void tst_qqmlecmascript::contextPropertiesTriggerReeval()
675 QQmlContext context(engine.rootContext());
678 MyQmlObject *object3 = new MyQmlObject;
680 object1.setStringProperty("Hello");
681 object2.setStringProperty("World");
683 context.setContextProperty("testProp", QVariant(1));
684 context.setContextProperty("testObj", &object1);
685 context.setContextProperty("testObj2", object3);
688 MyExpression expr(&context, "testProp + 1");
689 QCOMPARE(expr.changed, false);
690 QCOMPARE(expr.evaluate(), QVariant(2));
692 context.setContextProperty("testProp", QVariant(2));
693 QCOMPARE(expr.changed, true);
694 QCOMPARE(expr.evaluate(), QVariant(3));
698 MyExpression expr(&context, "testProp + testProp + testProp");
699 QCOMPARE(expr.changed, false);
700 QCOMPARE(expr.evaluate(), QVariant(6));
702 context.setContextProperty("testProp", QVariant(4));
703 QCOMPARE(expr.changed, true);
704 QCOMPARE(expr.evaluate(), QVariant(12));
708 MyExpression expr(&context, "testObj.stringProperty");
709 QCOMPARE(expr.changed, false);
710 QCOMPARE(expr.evaluate(), QVariant("Hello"));
712 context.setContextProperty("testObj", &object2);
713 QCOMPARE(expr.changed, true);
714 QCOMPARE(expr.evaluate(), QVariant("World"));
718 MyExpression expr(&context, "testObj.stringProperty /**/");
719 QCOMPARE(expr.changed, false);
720 QCOMPARE(expr.evaluate(), QVariant("World"));
722 context.setContextProperty("testObj", &object1);
723 QCOMPARE(expr.changed, true);
724 QCOMPARE(expr.evaluate(), QVariant("Hello"));
728 MyExpression expr(&context, "testObj2");
729 QCOMPARE(expr.changed, false);
730 QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3));
736 void tst_qqmlecmascript::objectPropertiesTriggerReeval()
738 QQmlContext context(engine.rootContext());
742 context.setContextProperty("testObj", &object1);
744 object1.setStringProperty(QLatin1String("Hello"));
745 object2.setStringProperty(QLatin1String("Dog"));
746 object3.setStringProperty(QLatin1String("Cat"));
749 MyExpression expr(&context, "testObj.stringProperty");
750 QCOMPARE(expr.changed, false);
751 QCOMPARE(expr.evaluate(), QVariant("Hello"));
753 object1.setStringProperty(QLatin1String("World"));
754 QCOMPARE(expr.changed, true);
755 QCOMPARE(expr.evaluate(), QVariant("World"));
759 MyExpression expr(&context, "testObj.objectProperty.stringProperty");
760 QCOMPARE(expr.changed, false);
761 QCOMPARE(expr.evaluate(), QVariant());
763 object1.setObjectProperty(&object2);
764 QCOMPARE(expr.changed, true);
765 expr.changed = false;
766 QCOMPARE(expr.evaluate(), QVariant("Dog"));
768 object1.setObjectProperty(&object3);
769 QCOMPARE(expr.changed, true);
770 expr.changed = false;
771 QCOMPARE(expr.evaluate(), QVariant("Cat"));
773 object1.setObjectProperty(0);
774 QCOMPARE(expr.changed, true);
775 expr.changed = false;
776 QCOMPARE(expr.evaluate(), QVariant());
778 object1.setObjectProperty(&object3);
779 QCOMPARE(expr.changed, true);
780 expr.changed = false;
781 QCOMPARE(expr.evaluate(), QVariant("Cat"));
783 object3.setStringProperty("Donkey");
784 QCOMPARE(expr.changed, true);
785 expr.changed = false;
786 QCOMPARE(expr.evaluate(), QVariant("Donkey"));
790 void tst_qqmlecmascript::deferredProperties()
792 QQmlComponent component(&engine, testFileUrl("deferredProperties.qml"));
793 MyDeferredObject *object =
794 qobject_cast<MyDeferredObject *>(component.create());
795 QVERIFY(object != 0);
796 QCOMPARE(object->value(), 0);
797 QVERIFY(object->objectProperty() == 0);
798 QVERIFY(object->objectProperty2() != 0);
799 qmlExecuteDeferred(object);
800 QCOMPARE(object->value(), 10);
801 QVERIFY(object->objectProperty() != 0);
802 MyQmlObject *qmlObject =
803 qobject_cast<MyQmlObject *>(object->objectProperty());
804 QVERIFY(qmlObject != 0);
805 QCOMPARE(qmlObject->value(), 10);
806 object->setValue(19);
807 QCOMPARE(qmlObject->value(), 19);
812 // Check errors on deferred properties are correctly emitted
813 void tst_qqmlecmascript::deferredPropertiesErrors()
815 QQmlComponent component(&engine, testFileUrl("deferredPropertiesErrors.qml"));
816 MyDeferredObject *object =
817 qobject_cast<MyDeferredObject *>(component.create());
818 QVERIFY(object != 0);
819 QCOMPARE(object->value(), 0);
820 QVERIFY(object->objectProperty() == 0);
821 QVERIFY(object->objectProperty2() == 0);
823 QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject*";
824 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
826 qmlExecuteDeferred(object);
831 void tst_qqmlecmascript::extensionObjects()
833 QQmlComponent component(&engine, testFileUrl("extensionObjects.qml"));
834 MyExtendedObject *object =
835 qobject_cast<MyExtendedObject *>(component.create());
836 QVERIFY(object != 0);
837 QCOMPARE(object->baseProperty(), 13);
838 QCOMPARE(object->coreProperty(), 9);
839 object->setProperty("extendedProperty", QVariant(11));
840 object->setProperty("baseExtendedProperty", QVariant(92));
841 QCOMPARE(object->coreProperty(), 11);
842 QCOMPARE(object->baseProperty(), 92);
844 MyExtendedObject *nested = qobject_cast<MyExtendedObject*>(qvariant_cast<QObject *>(object->property("nested")));
846 QCOMPARE(nested->baseProperty(), 13);
847 QCOMPARE(nested->coreProperty(), 9);
848 nested->setProperty("extendedProperty", QVariant(11));
849 nested->setProperty("baseExtendedProperty", QVariant(92));
850 QCOMPARE(nested->coreProperty(), 11);
851 QCOMPARE(nested->baseProperty(), 92);
856 void tst_qqmlecmascript::overrideExtensionProperties()
858 QQmlComponent component(&engine, testFileUrl("extensionObjectsPropertyOverride.qml"));
859 OverrideDefaultPropertyObject *object =
860 qobject_cast<OverrideDefaultPropertyObject *>(component.create());
861 QVERIFY(object != 0);
862 QVERIFY(object->secondProperty() != 0);
863 QVERIFY(object->firstProperty() == 0);
868 void tst_qqmlecmascript::attachedProperties()
871 QQmlComponent component(&engine, testFileUrl("attachedProperty.qml"));
872 QObject *object = component.create();
873 QVERIFY(object != 0);
874 QCOMPARE(object->property("a").toInt(), 19);
875 QCOMPARE(object->property("b").toInt(), 19);
876 QCOMPARE(object->property("c").toInt(), 19);
877 QCOMPARE(object->property("d").toInt(), 19);
882 QQmlComponent component(&engine, testFileUrl("attachedProperty.2.qml"));
883 QObject *object = component.create();
884 QVERIFY(object != 0);
885 QCOMPARE(object->property("a").toInt(), 26);
886 QCOMPARE(object->property("b").toInt(), 26);
887 QCOMPARE(object->property("c").toInt(), 26);
888 QCOMPARE(object->property("d").toInt(), 26);
894 QQmlComponent component(&engine, testFileUrl("writeAttachedProperty.qml"));
895 QObject *object = component.create();
896 QVERIFY(object != 0);
898 QMetaObject::invokeMethod(object, "writeValue2");
900 MyQmlAttachedObject *attached =
901 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
902 QVERIFY(attached != 0);
904 QCOMPARE(attached->value2(), 9);
909 void tst_qqmlecmascript::enums()
913 QQmlComponent component(&engine, testFileUrl("enums.1.qml"));
914 QObject *object = component.create();
915 QVERIFY(object != 0);
917 QCOMPARE(object->property("enumProperty").toInt(), (int)MyQmlObject::EnumValue2);
918 QCOMPARE(object->property("relatedEnumProperty").toInt(), (int)MyEnumContainer::RelatedValue);
919 QCOMPARE(object->property("unrelatedEnumProperty").toInt(), (int)MyEnumContainer::RelatedValue);
920 QCOMPARE(object->property("qtEnumProperty").toInt(), (int)Qt::CaseInsensitive);
921 QCOMPARE(object->property("a").toInt(), 0);
922 QCOMPARE(object->property("b").toInt(), 1);
923 QCOMPARE(object->property("c").toInt(), 2);
924 QCOMPARE(object->property("d").toInt(), 3);
925 QCOMPARE(object->property("e").toInt(), 0);
926 QCOMPARE(object->property("f").toInt(), 1);
927 QCOMPARE(object->property("g").toInt(), 2);
928 QCOMPARE(object->property("h").toInt(), 3);
929 QCOMPARE(object->property("i").toInt(), 19);
930 QCOMPARE(object->property("j").toInt(), 19);
931 QCOMPARE(object->property("k").toInt(), 42);
932 QCOMPARE(object->property("l").toInt(), 333);
936 // Non-existent enums
938 QUrl file = testFileUrl("enums.2.qml");
939 QString w1 = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'MyEnum' for property 'MyUnregisteredEnumTypeObject::enumProperty'");
940 QString w2 = QLatin1String("QQmlExpression: Expression ") + testFileUrl("enums.2.qml").toString() + QLatin1String(":9 depends on non-NOTIFYable properties:");
941 QString w3 = QLatin1String(" MyUnregisteredEnumTypeObject::enumProperty");
942 QString w4 = file.toString() + ":7: Unable to assign [undefined] to int";
943 QString w5 = file.toString() + ":8: Unable to assign [undefined] to int";
944 QString w6 = file.toString() + ":9: Unable to assign [undefined] to int";
945 QString w7 = file.toString() + ":13: Unable to assign [undefined] to [unknown property type]";
946 QString w8 = file.toString() + ":31: Unable to assign int to [unknown property type]";
947 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
948 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
949 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
950 QTest::ignoreMessage(QtWarningMsg, qPrintable(w4));
951 QTest::ignoreMessage(QtWarningMsg, qPrintable(w5));
952 QTest::ignoreMessage(QtWarningMsg, qPrintable(w6));
953 QTest::ignoreMessage(QtWarningMsg, qPrintable(w7));
954 QTest::ignoreMessage(QtWarningMsg, qPrintable(w8));
956 QQmlComponent component(&engine, testFileUrl("enums.2.qml"));
957 QObject *object = component.create();
958 QVERIFY(object != 0);
959 QCOMPARE(object->property("a").toInt(), 0);
960 QCOMPARE(object->property("b").toInt(), 0);
961 QCOMPARE(object->property("c").toInt(), 0);
963 QString w9 = file.toString() + ":18: Error: Cannot assign JavaScript function to [unknown property type]";
964 QTest::ignoreMessage(QtWarningMsg, qPrintable(w9));
965 QMetaObject::invokeMethod(object, "testAssignmentOne");
967 QString w10 = file.toString() + ":21: Error: Cannot assign [undefined] to [unknown property type]";
968 QTest::ignoreMessage(QtWarningMsg, qPrintable(w10));
969 QMetaObject::invokeMethod(object, "testAssignmentTwo");
971 QString w11 = file.toString() + ":24: Error: Cannot assign [undefined] to [unknown property type]";
972 QTest::ignoreMessage(QtWarningMsg, qPrintable(w11));
973 QMetaObject::invokeMethod(object, "testAssignmentThree");
975 QString w12 = file.toString() + ":34: Error: Cannot assign int to an unregistered type";
976 QTest::ignoreMessage(QtWarningMsg, qPrintable(w12));
977 QMetaObject::invokeMethod(object, "testAssignmentFour");
983 QQmlComponent component(&engine, testFileUrl("enums.3.qml"));
984 QObject *object = component.create();
985 QVERIFY(object != 0);
987 // check the values are what we expect
988 QCOMPARE(object->property("a").toInt(), 4);
989 QCOMPARE(object->property("b").toInt(), 5);
990 QCOMPARE(object->property("c").toInt(), 9);
991 QCOMPARE(object->property("d").toInt(), 13);
992 QCOMPARE(object->property("e").toInt(), 2);
993 QCOMPARE(object->property("f").toInt(), 3);
994 QCOMPARE(object->property("h").toInt(), 2);
995 QCOMPARE(object->property("i").toInt(), 3);
996 QCOMPARE(object->property("j").toInt(), -1);
997 QCOMPARE(object->property("k").toInt(), 42);
999 // count of change signals
1000 QCOMPARE(object->property("ac").toInt(), 0);
1001 QCOMPARE(object->property("bc").toInt(), 0);
1002 QCOMPARE(object->property("cc").toInt(), 0);
1003 QCOMPARE(object->property("dc").toInt(), 0);
1004 QCOMPARE(object->property("ec").toInt(), 0);
1005 QCOMPARE(object->property("fc").toInt(), 0);
1006 QCOMPARE(object->property("hc").toInt(), 1); // namespace -> binding
1007 QCOMPARE(object->property("ic").toInt(), 1); // namespace -> binding
1008 QCOMPARE(object->property("jc").toInt(), 0);
1009 QCOMPARE(object->property("kc").toInt(), 0);
1015 void tst_qqmlecmascript::valueTypeFunctions()
1017 QQmlComponent component(&engine, testFileUrl("valueTypeFunctions.qml"));
1018 MyTypeObject *obj = qobject_cast<MyTypeObject*>(component.create());
1020 QCOMPARE(obj->rectProperty(), QRect(0,0,100,100));
1021 QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5));
1027 Tests that writing a constant to a property with a binding on it disables the
1030 void tst_qqmlecmascript::constantsOverrideBindings()
1034 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.1.qml"));
1035 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1036 QVERIFY(object != 0);
1038 QCOMPARE(object->property("c2").toInt(), 0);
1039 object->setProperty("c1", QVariant(9));
1040 QCOMPARE(object->property("c2").toInt(), 9);
1042 emit object->basicSignal();
1044 QCOMPARE(object->property("c2").toInt(), 13);
1045 object->setProperty("c1", QVariant(8));
1046 QCOMPARE(object->property("c2").toInt(), 13);
1051 // During construction
1053 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.2.qml"));
1054 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1055 QVERIFY(object != 0);
1057 QCOMPARE(object->property("c1").toInt(), 0);
1058 QCOMPARE(object->property("c2").toInt(), 10);
1059 object->setProperty("c1", QVariant(9));
1060 QCOMPARE(object->property("c1").toInt(), 9);
1061 QCOMPARE(object->property("c2").toInt(), 10);
1069 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.3.qml"));
1070 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1071 QVERIFY(object != 0);
1073 QCOMPARE(object->property("c2").toInt(), 0);
1074 object->setProperty("c1", QVariant(9));
1075 QCOMPARE(object->property("c2").toInt(), 9);
1077 object->setProperty("c2", QVariant(13));
1078 QCOMPARE(object->property("c2").toInt(), 13);
1079 object->setProperty("c1", QVariant(7));
1080 QCOMPARE(object->property("c1").toInt(), 7);
1081 QCOMPARE(object->property("c2").toInt(), 13);
1089 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.4.qml"));
1090 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1091 QVERIFY(object != 0);
1093 QCOMPARE(object->property("c1").toInt(), 0);
1094 QCOMPARE(object->property("c3").toInt(), 10);
1095 object->setProperty("c1", QVariant(9));
1096 QCOMPARE(object->property("c1").toInt(), 9);
1097 QCOMPARE(object->property("c3").toInt(), 10);
1104 Tests that assigning a binding to a property that already has a binding causes
1105 the original binding to be disabled.
1107 void tst_qqmlecmascript::outerBindingOverridesInnerBinding()
1109 QQmlComponent component(&engine,
1110 testFileUrl("outerBindingOverridesInnerBinding.qml"));
1111 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1112 QVERIFY(object != 0);
1114 QCOMPARE(object->property("c1").toInt(), 0);
1115 QCOMPARE(object->property("c2").toInt(), 0);
1116 QCOMPARE(object->property("c3").toInt(), 0);
1118 object->setProperty("c1", QVariant(9));
1119 QCOMPARE(object->property("c1").toInt(), 9);
1120 QCOMPARE(object->property("c2").toInt(), 0);
1121 QCOMPARE(object->property("c3").toInt(), 0);
1123 object->setProperty("c3", QVariant(8));
1124 QCOMPARE(object->property("c1").toInt(), 9);
1125 QCOMPARE(object->property("c2").toInt(), 8);
1126 QCOMPARE(object->property("c3").toInt(), 8);
1132 Access a non-existent attached object.
1134 Tests for a regression where this used to crash.
1136 void tst_qqmlecmascript::nonExistentAttachedObject()
1138 QQmlComponent component(&engine, testFileUrl("nonExistentAttachedObject.qml"));
1140 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString";
1141 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
1143 QObject *object = component.create();
1144 QVERIFY(object != 0);
1149 void tst_qqmlecmascript::scope()
1152 QQmlComponent component(&engine, testFileUrl("scope.qml"));
1153 QObject *object = component.create();
1154 QVERIFY(object != 0);
1156 QCOMPARE(object->property("test1").toInt(), 1);
1157 QCOMPARE(object->property("test2").toInt(), 2);
1158 QCOMPARE(object->property("test3").toString(), QString("1Test"));
1159 QCOMPARE(object->property("test4").toString(), QString("2Test"));
1160 QCOMPARE(object->property("test5").toInt(), 1);
1161 QCOMPARE(object->property("test6").toInt(), 1);
1162 QCOMPARE(object->property("test7").toInt(), 2);
1163 QCOMPARE(object->property("test8").toInt(), 2);
1164 QCOMPARE(object->property("test9").toInt(), 1);
1165 QCOMPARE(object->property("test10").toInt(), 3);
1171 QQmlComponent component(&engine, testFileUrl("scope.2.qml"));
1172 QObject *object = component.create();
1173 QVERIFY(object != 0);
1175 QCOMPARE(object->property("test1").toInt(), 19);
1176 QCOMPARE(object->property("test2").toInt(), 19);
1177 QCOMPARE(object->property("test3").toInt(), 14);
1178 QCOMPARE(object->property("test4").toInt(), 14);
1179 QCOMPARE(object->property("test5").toInt(), 24);
1180 QCOMPARE(object->property("test6").toInt(), 24);
1186 QQmlComponent component(&engine, testFileUrl("scope.3.qml"));
1187 QObject *object = component.create();
1188 QVERIFY(object != 0);
1190 QCOMPARE(object->property("test1").toBool(), true);
1191 QCOMPARE(object->property("test2").toBool(), true);
1192 QCOMPARE(object->property("test3").toBool(), true);
1197 // Signal argument scope
1199 QQmlComponent component(&engine, testFileUrl("scope.4.qml"));
1200 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1201 QVERIFY(object != 0);
1203 QCOMPARE(object->property("test").toInt(), 0);
1204 QCOMPARE(object->property("test2").toString(), QString());
1206 emit object->argumentSignal(13, "Argument Scope", 9, MyQmlObject::EnumValue4, Qt::RightButton);
1208 QCOMPARE(object->property("test").toInt(), 13);
1209 QCOMPARE(object->property("test2").toString(), QString("Argument Scope"));
1215 QQmlComponent component(&engine, testFileUrl("scope.5.qml"));
1216 QObject *object = component.create();
1217 QVERIFY(object != 0);
1219 QCOMPARE(object->property("test1").toBool(), true);
1220 QCOMPARE(object->property("test2").toBool(), true);
1226 QQmlComponent component(&engine, testFileUrl("scope.6.qml"));
1227 QObject *object = component.create();
1228 QVERIFY(object != 0);
1230 QCOMPARE(object->property("test").toBool(), true);
1236 // In 4.7, non-library javascript files that had no imports shared the imports of their
1237 // importing context
1238 void tst_qqmlecmascript::importScope()
1240 QQmlComponent component(&engine, testFileUrl("importScope.qml"));
1241 QObject *o = component.create();
1244 QCOMPARE(o->property("test").toInt(), 240);
1250 Tests that "any" type passes through a synthesized signal parameter. This
1251 is essentially a test of QQmlMetaType::copy()
1253 void tst_qqmlecmascript::signalParameterTypes()
1255 QQmlComponent component(&engine, testFileUrl("signalParameterTypes.qml"));
1256 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1257 QVERIFY(object != 0);
1259 emit object->basicSignal();
1261 QCOMPARE(object->property("intProperty").toInt(), 10);
1262 QCOMPARE(object->property("realProperty").toReal(), 19.2);
1263 QVERIFY(object->property("colorProperty").value<QColor>() == QColor(255, 255, 0, 255));
1264 QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255)));
1265 QVERIFY(object->property("enumProperty") == MyQmlObject::EnumValue3);
1266 QVERIFY(object->property("qtEnumProperty") == Qt::LeftButton);
1272 Test that two JS objects for the same QObject compare as equal.
1274 void tst_qqmlecmascript::objectsCompareAsEqual()
1276 QQmlComponent component(&engine, testFileUrl("objectsCompareAsEqual.qml"));
1277 QObject *object = component.create();
1278 QVERIFY(object != 0);
1280 QCOMPARE(object->property("test1").toBool(), true);
1281 QCOMPARE(object->property("test2").toBool(), true);
1282 QCOMPARE(object->property("test3").toBool(), true);
1283 QCOMPARE(object->property("test4").toBool(), true);
1284 QCOMPARE(object->property("test5").toBool(), true);
1290 Confirm bindings and alias properties can coexist.
1292 Tests for a regression where the binding would not reevaluate.
1294 void tst_qqmlecmascript::aliasPropertyAndBinding()
1296 QQmlComponent component(&engine, testFileUrl("aliasPropertyAndBinding.qml"));
1297 QObject *object = component.create();
1298 QVERIFY(object != 0);
1300 QCOMPARE(object->property("c2").toInt(), 3);
1301 QCOMPARE(object->property("c3").toInt(), 3);
1303 object->setProperty("c2", QVariant(19));
1305 QCOMPARE(object->property("c2").toInt(), 19);
1306 QCOMPARE(object->property("c3").toInt(), 19);
1312 Ensure that we can write undefined value to an alias property,
1313 and that the aliased property is reset correctly if possible.
1315 void tst_qqmlecmascript::aliasPropertyReset()
1317 QObject *object = 0;
1319 // test that a manual write (of undefined) to a resettable aliased property succeeds
1320 QQmlComponent c1(&engine, testFileUrl("aliasreset/aliasPropertyReset.1.qml"));
1321 object = c1.create();
1322 QVERIFY(object != 0);
1323 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1324 QCOMPARE(object->property("aliasIsUndefined"), QVariant(false));
1325 QMetaObject::invokeMethod(object, "resetAliased");
1326 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1327 QCOMPARE(object->property("aliasIsUndefined"), QVariant(true));
1330 // test that a manual write (of undefined) to a resettable alias property succeeds
1331 QQmlComponent c2(&engine, testFileUrl("aliasreset/aliasPropertyReset.2.qml"));
1332 object = c2.create();
1333 QVERIFY(object != 0);
1334 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1335 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false));
1336 QMetaObject::invokeMethod(object, "resetAlias");
1337 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1338 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(true));
1341 // test that an alias to a bound property works correctly
1342 QQmlComponent c3(&engine, testFileUrl("aliasreset/aliasPropertyReset.3.qml"));
1343 object = c3.create();
1344 QVERIFY(object != 0);
1345 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1346 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false));
1347 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1348 QMetaObject::invokeMethod(object, "resetAlias");
1349 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1350 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(true));
1351 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1354 // test that a manual write (of undefined) to a resettable alias property
1355 // whose aliased property's object has been deleted, does not crash.
1356 QQmlComponent c4(&engine, testFileUrl("aliasreset/aliasPropertyReset.4.qml"));
1357 object = c4.create();
1358 QVERIFY(object != 0);
1359 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1360 QObject *loader = object->findChild<QObject*>("loader");
1361 QVERIFY(loader != 0);
1363 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // deletion should have caused value unset.
1364 QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash.
1365 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1366 QMetaObject::invokeMethod(object, "setAlias"); // shouldn't crash, and shouldn't change value (since it's no longer referencing anything).
1367 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1370 // test that binding an alias property to an undefined value works correctly
1371 QQmlComponent c5(&engine, testFileUrl("aliasreset/aliasPropertyReset.5.qml"));
1372 object = c5.create();
1373 QVERIFY(object != 0);
1374 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // bound to undefined value.
1377 // test that a manual write (of undefined) to a non-resettable property fails properly
1378 QUrl url = testFileUrl("aliasreset/aliasPropertyReset.error.1.qml");
1379 QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int");
1380 QQmlComponent e1(&engine, url);
1381 object = e1.create();
1382 QVERIFY(object != 0);
1383 QCOMPARE(object->property("intAlias").value<int>(), 12);
1384 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1385 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1386 QMetaObject::invokeMethod(object, "resetAlias");
1387 QCOMPARE(object->property("intAlias").value<int>(), 12);
1388 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1392 void tst_qqmlecmascript::componentCreation_data()
1394 QTest::addColumn<QString>("method");
1395 QTest::addColumn<QString>("creationError");
1396 QTest::addColumn<QString>("createdParent");
1398 QTest::newRow("url")
1402 QTest::newRow("urlMode")
1406 QTest::newRow("urlParent")
1410 QTest::newRow("urlNullParent")
1414 QTest::newRow("urlModeParent")
1418 QTest::newRow("urlModeNullParent")
1419 << "urlModeNullParent"
1422 QTest::newRow("invalidSecondArg")
1423 << "invalidSecondArg"
1424 << ":40: Error: Qt.createComponent(): Invalid arguments"
1426 QTest::newRow("invalidThirdArg")
1427 << "invalidThirdArg"
1428 << ":45: Error: Qt.createComponent(): Invalid parent object"
1430 QTest::newRow("invalidMode")
1432 << ":50: Error: Qt.createComponent(): Invalid arguments"
1437 Test using createComponent to dynamically generate a component.
1439 void tst_qqmlecmascript::componentCreation()
1441 QFETCH(QString, method);
1442 QFETCH(QString, creationError);
1443 QFETCH(QString, createdParent);
1445 QUrl testUrl(testFileUrl("componentCreation.qml"));
1447 if (!creationError.isEmpty()) {
1448 QString warning = testUrl.toString() + creationError;
1449 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1452 QQmlComponent component(&engine, testUrl);
1453 MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create());
1454 QVERIFY(object != 0);
1456 QMetaObject::invokeMethod(object, method.toUtf8());
1457 QQmlComponent *created = object->componentProperty();
1459 if (creationError.isEmpty()) {
1462 QObject *expectedParent;
1463 if (createdParent == QLatin1String("obj")) {
1464 expectedParent = object;
1465 } else if ((createdParent == QLatin1String("null")) || createdParent.isEmpty()) {
1468 QCOMPARE(created->parent(), expectedParent);
1472 void tst_qqmlecmascript::dynamicCreation_data()
1474 QTest::addColumn<QString>("method");
1475 QTest::addColumn<QString>("createdName");
1477 QTest::newRow("One") << "createOne" << "objectOne";
1478 QTest::newRow("Two") << "createTwo" << "objectTwo";
1479 QTest::newRow("Three") << "createThree" << "objectThree";
1483 Test using createQmlObject to dynamically generate an item
1484 Also using createComponent is tested.
1486 void tst_qqmlecmascript::dynamicCreation()
1488 QFETCH(QString, method);
1489 QFETCH(QString, createdName);
1491 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
1492 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1493 QVERIFY(object != 0);
1495 QMetaObject::invokeMethod(object, method.toUtf8());
1496 QObject *created = object->objectProperty();
1498 QCOMPARE(created->objectName(), createdName);
1504 Tests the destroy function
1506 void tst_qqmlecmascript::dynamicDestruction()
1509 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.qml"));
1510 QQmlGuard<MyQmlObject> object = qobject_cast<MyQmlObject*>(component.create());
1511 QVERIFY(object != 0);
1512 QQmlGuard<QObject> createdQmlObject = 0;
1514 QMetaObject::invokeMethod(object, "create");
1515 createdQmlObject = object->objectProperty();
1516 QVERIFY(createdQmlObject);
1517 QCOMPARE(createdQmlObject->objectName(), QString("emptyObject"));
1519 QMetaObject::invokeMethod(object, "killOther");
1520 QVERIFY(createdQmlObject);
1522 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1523 QCoreApplication::processEvents();
1524 QVERIFY(createdQmlObject);
1525 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1526 if (createdQmlObject) {
1528 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1529 QCoreApplication::processEvents();
1532 QVERIFY(!createdQmlObject);
1534 QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
1535 QMetaObject::invokeMethod(object, "killMe");
1537 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1538 QCoreApplication::processEvents();
1543 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.2.qml"));
1544 QObject *o = component.create();
1547 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1549 QMetaObject::invokeMethod(o, "create");
1551 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) != 0);
1553 QMetaObject::invokeMethod(o, "destroy");
1555 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1556 QCoreApplication::processEvents();
1558 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1565 QQmlGuard<QObject> createdQmlObject = 0;
1566 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.3.qml"));
1567 QObject *o = component.create();
1569 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1570 QMetaObject::invokeMethod(o, "create");
1571 createdQmlObject = qvariant_cast<QObject*>(o->property("objectProperty"));
1572 QVERIFY(createdQmlObject);
1573 QMetaObject::invokeMethod(o, "destroy");
1574 QVERIFY(qvariant_cast<bool>(o->property("test")) == false);
1575 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1577 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1578 QCoreApplication::processEvents();
1580 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1581 QVERIFY(qvariant_cast<bool>(o->property("test")) == true);
1587 tests that id.toString() works
1589 void tst_qqmlecmascript::objectToString()
1591 QQmlComponent component(&engine, testFileUrl("qmlToString.qml"));
1592 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1593 QVERIFY(object != 0);
1594 QMetaObject::invokeMethod(object, "testToString");
1595 QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_"));
1596 QVERIFY(object->stringProperty().endsWith(", \"objName\")"));
1602 tests that id.hasOwnProperty() works
1604 void tst_qqmlecmascript::objectHasOwnProperty()
1606 QUrl url = testFileUrl("qmlHasOwnProperty.qml");
1607 QString warning1 = url.toString() + ":59: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1608 QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1609 QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1611 QQmlComponent component(&engine, url);
1612 QObject *object = component.create();
1613 QVERIFY(object != 0);
1615 // test QObjects in QML
1616 QMetaObject::invokeMethod(object, "testHasOwnPropertySuccess");
1617 QVERIFY(object->property("result").value<bool>() == true);
1618 QMetaObject::invokeMethod(object, "testHasOwnPropertyFailure");
1619 QVERIFY(object->property("result").value<bool>() == false);
1621 // now test other types in QML
1622 QObject *child = object->findChild<QObject*>("typeObj");
1623 QVERIFY(child != 0);
1624 QMetaObject::invokeMethod(child, "testHasOwnPropertySuccess");
1625 QCOMPARE(child->property("valueTypeHasOwnProperty").toBool(), true);
1626 QCOMPARE(child->property("valueTypeHasOwnProperty2").toBool(), true);
1627 QCOMPARE(child->property("variantTypeHasOwnProperty").toBool(), true);
1628 QCOMPARE(child->property("stringTypeHasOwnProperty").toBool(), true);
1629 QCOMPARE(child->property("listTypeHasOwnProperty").toBool(), true);
1630 QCOMPARE(child->property("emptyListTypeHasOwnProperty").toBool(), true);
1631 QCOMPARE(child->property("enumTypeHasOwnProperty").toBool(), true);
1632 QCOMPARE(child->property("typenameHasOwnProperty").toBool(), true);
1633 QCOMPARE(child->property("typenameHasOwnProperty2").toBool(), true);
1634 QCOMPARE(child->property("moduleApiTypeHasOwnProperty").toBool(), true);
1635 QCOMPARE(child->property("moduleApiPropertyTypeHasOwnProperty").toBool(), true);
1637 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1638 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureOne");
1639 QCOMPARE(child->property("enumNonValueHasOwnProperty").toBool(), false);
1640 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1641 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureTwo");
1642 QCOMPARE(child->property("moduleApiNonPropertyHasOwnProperty").toBool(), false);
1643 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1644 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureThree");
1645 QCOMPARE(child->property("listAtInvalidHasOwnProperty").toBool(), false);
1651 Tests bindings that indirectly cause their own deletion work.
1653 This test is best run under valgrind to ensure no invalid memory access occur.
1655 void tst_qqmlecmascript::selfDeletingBinding()
1658 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.qml"));
1659 QObject *object = component.create();
1660 QVERIFY(object != 0);
1661 object->setProperty("triggerDelete", true);
1666 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.2.qml"));
1667 QObject *object = component.create();
1668 QVERIFY(object != 0);
1669 object->setProperty("triggerDelete", true);
1675 Test that extended object properties can be accessed.
1677 This test a regression where this used to crash. The issue was specificially
1678 for extended objects that did not include a synthesized meta object (so non-root
1679 and no synthesiszed properties).
1681 void tst_qqmlecmascript::extendedObjectPropertyLookup()
1683 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup.qml"));
1684 QObject *object = component.create();
1685 QVERIFY(object != 0);
1690 Test that extended object properties can be accessed correctly.
1692 void tst_qqmlecmascript::extendedObjectPropertyLookup2()
1694 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup2.qml"));
1695 QObject *object = component.create();
1696 QVERIFY(object != 0);
1698 QVariant returnValue;
1699 QVERIFY(QMetaObject::invokeMethod(object, "getValue", Q_RETURN_ARG(QVariant, returnValue)));
1700 QCOMPARE(returnValue.toInt(), 42);
1705 Test file/lineNumbers for binding/Script errors.
1707 void tst_qqmlecmascript::scriptErrors()
1709 QQmlComponent component(&engine, testFileUrl("scriptErrors.qml"));
1710 QString url = component.url().toString();
1712 QString warning1 = url.left(url.length() - 3) + "js:2: Error: Invalid write to global property \"a\"";
1713 QString warning2 = url + ":5: ReferenceError: a is not defined";
1714 QString warning3 = url.left(url.length() - 3) + "js:4: Error: Invalid write to global property \"a\"";
1715 QString warning4 = url + ":13: ReferenceError: a is not defined";
1716 QString warning5 = url + ":11: ReferenceError: a is not defined";
1717 QString warning6 = url + ":10: Unable to assign [undefined] to int";
1718 QString warning7 = url + ":15: Error: Cannot assign to read-only property \"trueProperty\"";
1719 QString warning8 = url + ":16: Error: Cannot assign to non-existent property \"fakeProperty\"";
1721 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1722 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1723 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1724 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
1725 QTest::ignoreMessage(QtWarningMsg, warning6.toLatin1().constData());
1726 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1727 QVERIFY(object != 0);
1729 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
1730 emit object->basicSignal();
1732 QTest::ignoreMessage(QtWarningMsg, warning7.toLatin1().constData());
1733 emit object->anotherBasicSignal();
1735 QTest::ignoreMessage(QtWarningMsg, warning8.toLatin1().constData());
1736 emit object->thirdBasicSignal();
1742 Test file/lineNumbers for inline functions.
1744 void tst_qqmlecmascript::functionErrors()
1746 QQmlComponent component(&engine, testFileUrl("functionErrors.qml"));
1747 QString url = component.url().toString();
1749 QString warning = url + ":5: Error: Invalid write to global property \"a\"";
1751 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1753 QObject *object = component.create();
1754 QVERIFY(object != 0);
1757 // test that if an exception occurs while invoking js function from cpp, it is reported as expected.
1758 QQmlComponent componentTwo(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
1759 url = componentTwo.url().toString();
1760 object = componentTwo.create();
1761 QVERIFY(object != 0);
1763 QString srpname = object->property("srp_name").toString();
1765 warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srpname
1766 + QLatin1String(" is not a function");
1767 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed.
1768 QMetaObject::invokeMethod(object, "retrieveScarceResource");
1773 Test various errors that can occur when assigning a property from script
1775 void tst_qqmlecmascript::propertyAssignmentErrors()
1777 QQmlComponent component(&engine, testFileUrl("propertyAssignmentErrors.qml"));
1779 QString url = component.url().toString();
1781 QObject *object = component.create();
1782 QVERIFY(object != 0);
1784 QCOMPARE(object->property("test1").toBool(), true);
1785 QCOMPARE(object->property("test2").toBool(), true);
1791 Test bindings still work when the reeval is triggered from within
1794 void tst_qqmlecmascript::signalTriggeredBindings()
1796 QQmlComponent component(&engine, testFileUrl("signalTriggeredBindings.qml"));
1797 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1798 QVERIFY(object != 0);
1800 QCOMPARE(object->property("base").toReal(), 50.);
1801 QCOMPARE(object->property("test1").toReal(), 50.);
1802 QCOMPARE(object->property("test2").toReal(), 50.);
1804 object->basicSignal();
1806 QCOMPARE(object->property("base").toReal(), 200.);
1807 QCOMPARE(object->property("test1").toReal(), 200.);
1808 QCOMPARE(object->property("test2").toReal(), 200.);
1810 object->argumentSignal(10, QString(), 10, MyQmlObject::EnumValue4, Qt::RightButton);
1812 QCOMPARE(object->property("base").toReal(), 400.);
1813 QCOMPARE(object->property("test1").toReal(), 400.);
1814 QCOMPARE(object->property("test2").toReal(), 400.);
1820 Test that list properties can be iterated from ECMAScript
1822 void tst_qqmlecmascript::listProperties()
1824 QQmlComponent component(&engine, testFileUrl("listProperties.qml"));
1825 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1826 QVERIFY(object != 0);
1828 QCOMPARE(object->property("test1").toInt(), 21);
1829 QCOMPARE(object->property("test2").toInt(), 2);
1830 QCOMPARE(object->property("test3").toBool(), true);
1831 QCOMPARE(object->property("test4").toBool(), true);
1836 void tst_qqmlecmascript::exceptionClearsOnReeval()
1838 QQmlComponent component(&engine, testFileUrl("exceptionClearsOnReeval.qml"));
1839 QString url = component.url().toString();
1841 QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null";
1843 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1844 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1845 QVERIFY(object != 0);
1847 QCOMPARE(object->property("test").toBool(), false);
1849 MyQmlObject object2;
1850 MyQmlObject object3;
1851 object2.setObjectProperty(&object3);
1852 object->setObjectProperty(&object2);
1854 QCOMPARE(object->property("test").toBool(), true);
1859 void tst_qqmlecmascript::exceptionSlotProducesWarning()
1861 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning.qml"));
1862 QString url = component.url().toString();
1864 QString warning = component.url().toString() + ":6: Error: JS exception";
1866 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1867 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1868 QVERIFY(object != 0);
1872 void tst_qqmlecmascript::exceptionBindingProducesWarning()
1874 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning2.qml"));
1875 QString url = component.url().toString();
1877 QString warning = component.url().toString() + ":5: Error: JS exception";
1879 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1880 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1881 QVERIFY(object != 0);
1885 void tst_qqmlecmascript::compileInvalidBinding()
1887 // QTBUG-23387: ensure that invalid bindings don't cause a crash.
1888 QQmlComponent component(&engine, testFileUrl("v8bindingException.qml"));
1889 QObject *object = component.create();
1890 QVERIFY(object != 0);
1894 static int transientErrorsMsgCount = 0;
1895 static void transientErrorsMsgHandler(QtMsgType, const char *)
1897 ++transientErrorsMsgCount;
1900 // Check that transient binding errors are not displayed
1901 void tst_qqmlecmascript::transientErrors()
1904 QQmlComponent component(&engine, testFileUrl("transientErrors.qml"));
1906 transientErrorsMsgCount = 0;
1907 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1909 QObject *object = component.create();
1910 QVERIFY(object != 0);
1912 qInstallMsgHandler(old);
1914 QCOMPARE(transientErrorsMsgCount, 0);
1919 // One binding erroring multiple times, but then resolving
1921 QQmlComponent component(&engine, testFileUrl("transientErrors.2.qml"));
1923 transientErrorsMsgCount = 0;
1924 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1926 QObject *object = component.create();
1927 QVERIFY(object != 0);
1929 qInstallMsgHandler(old);
1931 QCOMPARE(transientErrorsMsgCount, 0);
1937 // Check that errors during shutdown are minimized
1938 void tst_qqmlecmascript::shutdownErrors()
1940 QQmlComponent component(&engine, testFileUrl("shutdownErrors.qml"));
1941 QObject *object = component.create();
1942 QVERIFY(object != 0);
1944 transientErrorsMsgCount = 0;
1945 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1949 qInstallMsgHandler(old);
1950 QCOMPARE(transientErrorsMsgCount, 0);
1953 void tst_qqmlecmascript::compositePropertyType()
1955 QQmlComponent component(&engine, testFileUrl("compositePropertyType.qml"));
1957 QTest::ignoreMessage(QtDebugMsg, "hello world");
1958 QObject *object = qobject_cast<QObject *>(component.create());
1963 void tst_qqmlecmascript::jsObject()
1965 QQmlComponent component(&engine, testFileUrl("jsObject.qml"));
1966 QObject *object = component.create();
1967 QVERIFY(object != 0);
1969 QCOMPARE(object->property("test").toInt(), 92);
1974 void tst_qqmlecmascript::undefinedResetsProperty()
1977 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.qml"));
1978 QObject *object = component.create();
1979 QVERIFY(object != 0);
1981 QCOMPARE(object->property("resettableProperty").toInt(), 92);
1983 object->setProperty("setUndefined", true);
1985 QCOMPARE(object->property("resettableProperty").toInt(), 13);
1987 object->setProperty("setUndefined", false);
1989 QCOMPARE(object->property("resettableProperty").toInt(), 92);
1994 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.2.qml"));
1995 QObject *object = component.create();
1996 QVERIFY(object != 0);
1998 QCOMPARE(object->property("resettableProperty").toInt(), 19);
2000 QMetaObject::invokeMethod(object, "doReset");
2002 QCOMPARE(object->property("resettableProperty").toInt(), 13);
2008 // Aliases to variant properties should work
2009 void tst_qqmlecmascript::qtbug_22464()
2011 QQmlComponent component(&engine, testFileUrl("qtbug_22464.qml"));
2012 QObject *object = component.create();
2013 QVERIFY(object != 0);
2015 QCOMPARE(object->property("test").toBool(), true);
2020 void tst_qqmlecmascript::qtbug_21580()
2022 QQmlComponent component(&engine, testFileUrl("qtbug_21580.qml"));
2024 QObject *object = component.create();
2025 QVERIFY(object != 0);
2027 QCOMPARE(object->property("test").toBool(), true);
2032 // Causes a v8 binding, but not all v8 bindings to be destroyed during evaluation
2033 void tst_qqmlecmascript::singleV8BindingDestroyedDuringEvaluation()
2035 QQmlComponent component(&engine, testFileUrl("singleV8BindingDestroyedDuringEvaluation.qml"));
2037 QObject *object = component.create();
2038 QVERIFY(object != 0);
2043 void tst_qqmlecmascript::bug1()
2045 QQmlComponent component(&engine, testFileUrl("bug.1.qml"));
2046 QObject *object = component.create();
2047 QVERIFY(object != 0);
2049 QCOMPARE(object->property("test").toInt(), 14);
2051 object->setProperty("a", 11);
2053 QCOMPARE(object->property("test").toInt(), 3);
2055 object->setProperty("b", true);
2057 QCOMPARE(object->property("test").toInt(), 9);
2062 #ifndef QT_NO_WIDGETS
2063 void tst_qqmlecmascript::bug2()
2065 QQmlComponent component(&engine);
2066 component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl());
2068 QObject *object = component.create();
2069 QVERIFY(object != 0);
2075 // Don't crash in createObject when the component has errors.
2076 void tst_qqmlecmascript::dynamicCreationCrash()
2078 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
2079 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2080 QVERIFY(object != 0);
2082 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2083 QMetaObject::invokeMethod(object, "dontCrash");
2084 QObject *created = object->objectProperty();
2085 QVERIFY(created == 0);
2090 // ownership transferred to JS, ensure that GC runs the dtor
2091 void tst_qqmlecmascript::dynamicCreationOwnership()
2094 int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
2096 // allow the engine to go out of scope too.
2098 QQmlEngine dcoEngine;
2099 QQmlComponent component(&dcoEngine, testFileUrl("dynamicCreationOwnership.qml"));
2100 QObject *object = component.create();
2101 QVERIFY(object != 0);
2102 MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo");
2103 QVERIFY(mdcdo != 0);
2104 mdcdo->setDtorCount(&dtorCount);
2106 for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
2107 QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
2109 // we do this once manually, but it should be done automatically
2110 // when the engine goes out of scope (since it should gc in dtor)
2111 QMetaObject::invokeMethod(object, "performGc");
2114 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2115 QCoreApplication::processEvents();
2121 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2122 QCoreApplication::processEvents();
2123 QCOMPARE(dtorCount, expectedDtorCount);
2126 void tst_qqmlecmascript::regExpBug()
2130 QQmlComponent component(&engine, testFileUrl("regExp.qml"));
2131 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2132 QVERIFY(object != 0);
2133 QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]"));
2139 QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString());
2140 QQmlComponent component(&engine, testFileUrl("regExp.2.qml"));
2141 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2142 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2144 QCOMPARE(component.errorString(), err);
2148 static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source)
2150 QString functionSource = QLatin1String("(function(object) { return ") +
2151 QLatin1String(source) + QLatin1String(" })");
2153 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2156 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2157 if (function.IsEmpty())
2159 v8::Handle<v8::Value> args[] = { o };
2160 function->Call(engine->global(), 1, args);
2161 return tc.HasCaught();
2164 static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o,
2165 const char *source, v8::Handle<v8::Value> result)
2167 QString functionSource = QLatin1String("(function(object) { return ") +
2168 QLatin1String(source) + QLatin1String(" })");
2170 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2173 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2174 if (function.IsEmpty())
2176 v8::Handle<v8::Value> args[] = { o };
2178 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2183 return value->StrictEquals(result);
2186 static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o,
2189 QString functionSource = QLatin1String("(function(object) { return ") +
2190 QLatin1String(source) + QLatin1String(" })");
2192 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2194 return v8::Handle<v8::Value>();
2195 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2196 if (function.IsEmpty())
2197 return v8::Handle<v8::Value>();
2198 v8::Handle<v8::Value> args[] = { o };
2200 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2203 return v8::Handle<v8::Value>();
2207 #define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
2208 #define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result)
2209 #define EVALUATE(source) evaluate(engine, object, source)
2211 void tst_qqmlecmascript::callQtInvokables()
2213 MyInvokableObject o;
2215 QQmlEngine qmlengine;
2216 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine);
2218 QV8Engine *engine = ep->v8engine();
2220 v8::HandleScope handle_scope;
2221 v8::Context::Scope scope(engine->context());
2223 v8::Local<v8::Object> object = engine->newQObject(&o)->ToObject();
2225 // Non-existent methods
2227 QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
2228 QCOMPARE(o.error(), false);
2229 QCOMPARE(o.invoked(), -1);
2230 QCOMPARE(o.actuals().count(), 0);
2233 QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)"));
2234 QCOMPARE(o.error(), false);
2235 QCOMPARE(o.invoked(), -1);
2236 QCOMPARE(o.actuals().count(), 0);
2238 // Insufficient arguments
2240 QVERIFY(EVALUATE_ERROR("object.method_int()"));
2241 QCOMPARE(o.error(), false);
2242 QCOMPARE(o.invoked(), -1);
2243 QCOMPARE(o.actuals().count(), 0);
2246 QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
2247 QCOMPARE(o.error(), false);
2248 QCOMPARE(o.invoked(), -1);
2249 QCOMPARE(o.actuals().count(), 0);
2251 // Excessive arguments
2253 QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", v8::Undefined()));
2254 QCOMPARE(o.error(), false);
2255 QCOMPARE(o.invoked(), 8);
2256 QCOMPARE(o.actuals().count(), 1);
2257 QCOMPARE(o.actuals().at(0), QVariant(10));
2260 QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", v8::Undefined()));
2261 QCOMPARE(o.error(), false);
2262 QCOMPARE(o.invoked(), 9);
2263 QCOMPARE(o.actuals().count(), 2);
2264 QCOMPARE(o.actuals().at(0), QVariant(10));
2265 QCOMPARE(o.actuals().at(1), QVariant(11));
2267 // Test return types
2269 QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", v8::Undefined()));
2270 QCOMPARE(o.error(), false);
2271 QCOMPARE(o.invoked(), 0);
2272 QCOMPARE(o.actuals().count(), 0);
2275 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", v8::Integer::New(6)));
2276 QCOMPARE(o.error(), false);
2277 QCOMPARE(o.invoked(), 1);
2278 QCOMPARE(o.actuals().count(), 0);
2281 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", v8::Number::New(19.75)));
2282 QCOMPARE(o.error(), false);
2283 QCOMPARE(o.invoked(), 2);
2284 QCOMPARE(o.actuals().count(), 0);
2288 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QPointF()");
2289 QVERIFY(!ret.IsEmpty());
2290 QCOMPARE(engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5)));
2291 QCOMPARE(o.error(), false);
2292 QCOMPARE(o.invoked(), 3);
2293 QCOMPARE(o.actuals().count(), 0);
2298 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QObject()");
2299 QCOMPARE(engine->toQObject(ret), (QObject *)&o);
2300 QCOMPARE(o.error(), false);
2301 QCOMPARE(o.invoked(), 4);
2302 QCOMPARE(o.actuals().count(), 0);
2306 QVERIFY(EVALUATE_ERROR("object.method_NoArgs_unknown()"));
2307 QCOMPARE(o.error(), false);
2308 QCOMPARE(o.invoked(), -1);
2309 QCOMPARE(o.actuals().count(), 0);
2313 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QScriptValue()");
2314 QVERIFY(ret->IsString());
2315 QCOMPARE(engine->toString(ret), QString("Hello world"));
2316 QCOMPARE(o.error(), false);
2317 QCOMPARE(o.invoked(), 6);
2318 QCOMPARE(o.actuals().count(), 0);
2322 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks")));
2323 QCOMPARE(o.error(), false);
2324 QCOMPARE(o.invoked(), 7);
2325 QCOMPARE(o.actuals().count(), 0);
2329 QVERIFY(EVALUATE_VALUE("object.method_int(94)", v8::Undefined()));
2330 QCOMPARE(o.error(), false);
2331 QCOMPARE(o.invoked(), 8);
2332 QCOMPARE(o.actuals().count(), 1);
2333 QCOMPARE(o.actuals().at(0), QVariant(94));
2336 QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", v8::Undefined()));
2337 QCOMPARE(o.error(), false);
2338 QCOMPARE(o.invoked(), 8);
2339 QCOMPARE(o.actuals().count(), 1);
2340 QCOMPARE(o.actuals().at(0), QVariant(94));
2343 QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", v8::Undefined()));
2344 QCOMPARE(o.error(), false);
2345 QCOMPARE(o.invoked(), 8);
2346 QCOMPARE(o.actuals().count(), 1);
2347 QCOMPARE(o.actuals().at(0), QVariant(0));
2350 QVERIFY(EVALUATE_VALUE("object.method_int(null)", v8::Undefined()));
2351 QCOMPARE(o.error(), false);
2352 QCOMPARE(o.invoked(), 8);
2353 QCOMPARE(o.actuals().count(), 1);
2354 QCOMPARE(o.actuals().at(0), QVariant(0));
2357 QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", v8::Undefined()));
2358 QCOMPARE(o.error(), false);
2359 QCOMPARE(o.invoked(), 8);
2360 QCOMPARE(o.actuals().count(), 1);
2361 QCOMPARE(o.actuals().at(0), QVariant(0));
2364 QVERIFY(EVALUATE_VALUE("object.method_int(object)", v8::Undefined()));
2365 QCOMPARE(o.error(), false);
2366 QCOMPARE(o.invoked(), 8);
2367 QCOMPARE(o.actuals().count(), 1);
2368 QCOMPARE(o.actuals().at(0), QVariant(0));
2371 QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", v8::Undefined()));
2372 QCOMPARE(o.error(), false);
2373 QCOMPARE(o.invoked(), 9);
2374 QCOMPARE(o.actuals().count(), 2);
2375 QCOMPARE(o.actuals().at(0), QVariant(122));
2376 QCOMPARE(o.actuals().at(1), QVariant(9));
2379 QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", v8::Undefined()));
2380 QCOMPARE(o.error(), false);
2381 QCOMPARE(o.invoked(), 10);
2382 QCOMPARE(o.actuals().count(), 1);
2383 QCOMPARE(o.actuals().at(0), QVariant(94.3));
2386 QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", v8::Undefined()));
2387 QCOMPARE(o.error(), false);
2388 QCOMPARE(o.invoked(), 10);
2389 QCOMPARE(o.actuals().count(), 1);
2390 QCOMPARE(o.actuals().at(0), QVariant(94.3));
2393 QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", v8::Undefined()));
2394 QCOMPARE(o.error(), false);
2395 QCOMPARE(o.invoked(), 10);
2396 QCOMPARE(o.actuals().count(), 1);
2397 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2400 QVERIFY(EVALUATE_VALUE("object.method_real(null)", v8::Undefined()));
2401 QCOMPARE(o.error(), false);
2402 QCOMPARE(o.invoked(), 10);
2403 QCOMPARE(o.actuals().count(), 1);
2404 QCOMPARE(o.actuals().at(0), QVariant(0));
2407 QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", v8::Undefined()));
2408 QCOMPARE(o.error(), false);
2409 QCOMPARE(o.invoked(), 10);
2410 QCOMPARE(o.actuals().count(), 1);
2411 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2414 QVERIFY(EVALUATE_VALUE("object.method_real(object)", v8::Undefined()));
2415 QCOMPARE(o.error(), false);
2416 QCOMPARE(o.invoked(), 10);
2417 QCOMPARE(o.actuals().count(), 1);
2418 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2421 QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", v8::Undefined()));
2422 QCOMPARE(o.error(), false);
2423 QCOMPARE(o.invoked(), 11);
2424 QCOMPARE(o.actuals().count(), 1);
2425 QCOMPARE(o.actuals().at(0), QVariant("Hello world"));
2428 QVERIFY(EVALUATE_VALUE("object.method_QString(19)", v8::Undefined()));
2429 QCOMPARE(o.error(), false);
2430 QCOMPARE(o.invoked(), 11);
2431 QCOMPARE(o.actuals().count(), 1);
2432 QCOMPARE(o.actuals().at(0), QVariant("19"));
2436 QString expected = "MyInvokableObject(0x" + QString::number((quintptr)&o, 16) + ")";
2437 QVERIFY(EVALUATE_VALUE("object.method_QString(object)", v8::Undefined()));
2438 QCOMPARE(o.error(), false);
2439 QCOMPARE(o.invoked(), 11);
2440 QCOMPARE(o.actuals().count(), 1);
2441 QCOMPARE(o.actuals().at(0), QVariant(expected));
2445 QVERIFY(EVALUATE_VALUE("object.method_QString(null)", v8::Undefined()));
2446 QCOMPARE(o.error(), false);
2447 QCOMPARE(o.invoked(), 11);
2448 QCOMPARE(o.actuals().count(), 1);
2449 QCOMPARE(o.actuals().at(0), QVariant(QString()));
2452 QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", v8::Undefined()));
2453 QCOMPARE(o.error(), false);
2454 QCOMPARE(o.invoked(), 11);
2455 QCOMPARE(o.actuals().count(), 1);
2456 QCOMPARE(o.actuals().at(0), QVariant(QString()));
2459 QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", v8::Undefined()));
2460 QCOMPARE(o.error(), false);
2461 QCOMPARE(o.invoked(), 12);
2462 QCOMPARE(o.actuals().count(), 1);
2463 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2466 QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", v8::Undefined()));
2467 QCOMPARE(o.error(), false);
2468 QCOMPARE(o.invoked(), 12);
2469 QCOMPARE(o.actuals().count(), 1);
2470 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2473 QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", v8::Undefined()));
2474 QCOMPARE(o.error(), false);
2475 QCOMPARE(o.invoked(), 12);
2476 QCOMPARE(o.actuals().count(), 1);
2477 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2480 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", v8::Undefined()));
2481 QCOMPARE(o.error(), false);
2482 QCOMPARE(o.invoked(), 12);
2483 QCOMPARE(o.actuals().count(), 1);
2484 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2487 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", v8::Undefined()));
2488 QCOMPARE(o.error(), false);
2489 QCOMPARE(o.invoked(), 12);
2490 QCOMPARE(o.actuals().count(), 1);
2491 QCOMPARE(o.actuals().at(0), QVariant(QPointF(99.3, -10.2)));
2494 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", v8::Undefined()));
2495 QCOMPARE(o.error(), false);
2496 QCOMPARE(o.invoked(), 12);
2497 QCOMPARE(o.actuals().count(), 1);
2498 QCOMPARE(o.actuals().at(0), QVariant(QPointF(9, 12)));
2501 QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", v8::Undefined()));
2502 QCOMPARE(o.error(), false);
2503 QCOMPARE(o.invoked(), 13);
2504 QCOMPARE(o.actuals().count(), 1);
2505 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2508 QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", v8::Undefined()));
2509 QCOMPARE(o.error(), false);
2510 QCOMPARE(o.invoked(), 13);
2511 QCOMPARE(o.actuals().count(), 1);
2512 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2515 QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", v8::Undefined()));
2516 QCOMPARE(o.error(), false);
2517 QCOMPARE(o.invoked(), 13);
2518 QCOMPARE(o.actuals().count(), 1);
2519 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2522 QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", v8::Undefined()));
2523 QCOMPARE(o.error(), false);
2524 QCOMPARE(o.invoked(), 13);
2525 QCOMPARE(o.actuals().count(), 1);
2526 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2529 QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", v8::Undefined()));
2530 QCOMPARE(o.error(), false);
2531 QCOMPARE(o.invoked(), 13);
2532 QCOMPARE(o.actuals().count(), 1);
2533 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)&o));
2536 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined()));
2537 QCOMPARE(o.error(), false);
2538 QCOMPARE(o.invoked(), 14);
2539 QCOMPARE(o.actuals().count(), 1);
2540 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isNull());
2543 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined()));
2544 QCOMPARE(o.error(), false);
2545 QCOMPARE(o.invoked(), 14);
2546 QCOMPARE(o.actuals().count(), 1);
2547 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isUndefined());
2550 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined()));
2551 QCOMPARE(o.error(), false);
2552 QCOMPARE(o.invoked(), 14);
2553 QCOMPARE(o.actuals().count(), 1);
2554 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).strictlyEquals(QJSValue(19)));
2557 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined()));
2558 QCOMPARE(o.error(), false);
2559 QCOMPARE(o.invoked(), 14);
2560 QCOMPARE(o.actuals().count(), 1);
2561 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isArray());
2564 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined()));
2565 QCOMPARE(o.error(), false);
2566 QCOMPARE(o.invoked(), 15);
2567 QCOMPARE(o.actuals().count(), 2);
2568 QCOMPARE(o.actuals().at(0), QVariant(4));
2569 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isNull());
2572 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined()));
2573 QCOMPARE(o.error(), false);
2574 QCOMPARE(o.invoked(), 15);
2575 QCOMPARE(o.actuals().count(), 2);
2576 QCOMPARE(o.actuals().at(0), QVariant(8));
2577 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isUndefined());
2580 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined()));
2581 QCOMPARE(o.error(), false);
2582 QCOMPARE(o.invoked(), 15);
2583 QCOMPARE(o.actuals().count(), 2);
2584 QCOMPARE(o.actuals().at(0), QVariant(3));
2585 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).strictlyEquals(QJSValue(19)));
2588 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined()));
2589 QCOMPARE(o.error(), false);
2590 QCOMPARE(o.invoked(), 15);
2591 QCOMPARE(o.actuals().count(), 2);
2592 QCOMPARE(o.actuals().at(0), QVariant(44));
2593 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isArray());
2596 QVERIFY(EVALUATE_ERROR("object.method_overload()"));
2597 QCOMPARE(o.error(), false);
2598 QCOMPARE(o.invoked(), -1);
2599 QCOMPARE(o.actuals().count(), 0);
2602 QVERIFY(EVALUATE_VALUE("object.method_overload(10)", v8::Undefined()));
2603 QCOMPARE(o.error(), false);
2604 QCOMPARE(o.invoked(), 16);
2605 QCOMPARE(o.actuals().count(), 1);
2606 QCOMPARE(o.actuals().at(0), QVariant(10));
2609 QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", v8::Undefined()));
2610 QCOMPARE(o.error(), false);
2611 QCOMPARE(o.invoked(), 17);
2612 QCOMPARE(o.actuals().count(), 2);
2613 QCOMPARE(o.actuals().at(0), QVariant(10));
2614 QCOMPARE(o.actuals().at(1), QVariant(11));
2617 QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", v8::Undefined()));
2618 QCOMPARE(o.error(), false);
2619 QCOMPARE(o.invoked(), 18);
2620 QCOMPARE(o.actuals().count(), 1);
2621 QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2624 QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", v8::Undefined()));
2625 QCOMPARE(o.error(), false);
2626 QCOMPARE(o.invoked(), 19);
2627 QCOMPARE(o.actuals().count(), 1);
2628 QCOMPARE(o.actuals().at(0), QVariant(9));
2631 QVERIFY(EVALUATE_VALUE("object.method_default(10)", v8::Integer::New(19)));
2632 QCOMPARE(o.error(), false);
2633 QCOMPARE(o.invoked(), 20);
2634 QCOMPARE(o.actuals().count(), 2);
2635 QCOMPARE(o.actuals().at(0), QVariant(10));
2636 QCOMPARE(o.actuals().at(1), QVariant(19));
2639 QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", v8::Integer::New(13)));
2640 QCOMPARE(o.error(), false);
2641 QCOMPARE(o.invoked(), 20);
2642 QCOMPARE(o.actuals().count(), 2);
2643 QCOMPARE(o.actuals().at(0), QVariant(10));
2644 QCOMPARE(o.actuals().at(1), QVariant(13));
2647 QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", v8::Undefined()));
2648 QCOMPARE(o.error(), false);
2649 QCOMPARE(o.invoked(), -3);
2650 QCOMPARE(o.actuals().count(), 1);
2651 QCOMPARE(o.actuals().at(0), QVariant(9));
2654 QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", v8::Undefined()));
2655 QCOMPARE(o.error(), false);
2656 QCOMPARE(o.invoked(), 21);
2657 QCOMPARE(o.actuals().count(), 2);
2658 QCOMPARE(o.actuals().at(0), QVariant(9));
2659 QCOMPARE(o.actuals().at(1), QVariant());
2662 QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", v8::Undefined()));
2663 QCOMPARE(o.error(), false);
2664 QCOMPARE(o.invoked(), 21);
2665 QCOMPARE(o.actuals().count(), 2);
2666 QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2667 QCOMPARE(o.actuals().at(1), QVariant(QString("World")));
2670 QVERIFY(EVALUATE_VALUE("object.method_QJsonObject({foo:123})", v8::Undefined()));
2671 QCOMPARE(o.error(), false);
2672 QCOMPARE(o.invoked(), 22);
2673 QCOMPARE(o.actuals().count(), 1);
2674 QCOMPARE(qvariant_cast<QJsonObject>(o.actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2677 QVERIFY(EVALUATE_VALUE("object.method_QJsonArray([123])", v8::Undefined()));
2678 QCOMPARE(o.error(), false);
2679 QCOMPARE(o.invoked(), 23);
2680 QCOMPARE(o.actuals().count(), 1);
2681 QCOMPARE(qvariant_cast<QJsonArray>(o.actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2684 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(123)", v8::Undefined()));
2685 QCOMPARE(o.error(), false);
2686 QCOMPARE(o.invoked(), 24);
2687 QCOMPARE(o.actuals().count(), 1);
2688 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(123));
2691 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(42.35)", v8::Undefined()));
2692 QCOMPARE(o.error(), false);
2693 QCOMPARE(o.invoked(), 24);
2694 QCOMPARE(o.actuals().count(), 1);
2695 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(42.35));
2698 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue('ciao')", v8::Undefined()));
2699 QCOMPARE(o.error(), false);
2700 QCOMPARE(o.invoked(), 24);
2701 QCOMPARE(o.actuals().count(), 1);
2702 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QStringLiteral("ciao")));
2705 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(true)", v8::Undefined()));
2706 QCOMPARE(o.error(), false);
2707 QCOMPARE(o.invoked(), 24);
2708 QCOMPARE(o.actuals().count(), 1);
2709 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(true));
2712 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(false)", v8::Undefined()));
2713 QCOMPARE(o.error(), false);
2714 QCOMPARE(o.invoked(), 24);
2715 QCOMPARE(o.actuals().count(), 1);
2716 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(false));
2719 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(null)", v8::Undefined()));
2720 QCOMPARE(o.error(), false);
2721 QCOMPARE(o.invoked(), 24);
2722 QCOMPARE(o.actuals().count(), 1);
2723 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Null));
2726 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(undefined)", v8::Undefined()));
2727 QCOMPARE(o.error(), false);
2728 QCOMPARE(o.invoked(), 24);
2729 QCOMPARE(o.actuals().count(), 1);
2730 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2733 QVERIFY(EVALUATE_VALUE("object.method_overload({foo:123})", v8::Undefined()));
2734 QCOMPARE(o.error(), false);
2735 QCOMPARE(o.invoked(), 25);
2736 QCOMPARE(o.actuals().count(), 1);
2737 QCOMPARE(qvariant_cast<QJsonObject>(o.actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2740 QVERIFY(EVALUATE_VALUE("object.method_overload([123])", v8::Undefined()));
2741 QCOMPARE(o.error(), false);
2742 QCOMPARE(o.invoked(), 26);
2743 QCOMPARE(o.actuals().count(), 1);
2744 QCOMPARE(qvariant_cast<QJsonArray>(o.actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2747 QVERIFY(EVALUATE_VALUE("object.method_overload(null)", v8::Undefined()));
2748 QCOMPARE(o.error(), false);
2749 QCOMPARE(o.invoked(), 27);
2750 QCOMPARE(o.actuals().count(), 1);
2751 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Null));
2754 QVERIFY(EVALUATE_VALUE("object.method_overload(undefined)", v8::Undefined()));
2755 QCOMPARE(o.error(), false);
2756 QCOMPARE(o.invoked(), 27);
2757 QCOMPARE(o.actuals().count(), 1);
2758 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2761 QVERIFY(EVALUATE_ERROR("object.method_unknown(null)"));
2762 QCOMPARE(o.error(), false);
2763 QCOMPARE(o.invoked(), -1);
2764 QCOMPARE(o.actuals().count(), 0);
2767 // QTBUG-13047 (check that you can pass registered object types as args)
2768 void tst_qqmlecmascript::invokableObjectArg()
2770 QQmlComponent component(&engine, testFileUrl("invokableObjectArg.qml"));
2772 QObject *o = component.create();
2774 MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o);
2776 QCOMPARE(qmlobject->myinvokableObject, qmlobject);
2781 // QTBUG-13047 (check that you can return registered object types from methods)
2782 void tst_qqmlecmascript::invokableObjectRet()
2784 QQmlComponent component(&engine, testFileUrl("invokableObjectRet.qml"));
2786 QObject *o = component.create();
2788 QCOMPARE(o->property("test").toBool(), true);
2792 void tst_qqmlecmascript::invokableEnumRet()
2794 QQmlComponent component(&engine, testFileUrl("invokableEnumRet.qml"));
2796 QObject *o = component.create();
2798 QCOMPARE(o->property("test").toBool(), true);
2803 void tst_qqmlecmascript::listToVariant()
2805 QQmlComponent component(&engine, testFileUrl("listToVariant.qml"));
2807 MyQmlContainer container;
2809 QQmlContext context(engine.rootContext());
2810 context.setContextObject(&container);
2812 QObject *object = component.create(&context);
2813 QVERIFY(object != 0);
2815 QVariant v = object->property("test");
2816 QCOMPARE(v.userType(), qMetaTypeId<QQmlListReference>());
2817 QVERIFY(qvariant_cast<QQmlListReference>(v).object() == &container);
2823 Q_DECLARE_METATYPE(QQmlListProperty<MyQmlObject>)
2824 void tst_qqmlecmascript::listAssignment()
2826 QQmlComponent component(&engine, testFileUrl("listAssignment.qml"));
2827 QObject *obj = component.create();
2828 QCOMPARE(obj->property("list1length").toInt(), 2);
2829 QQmlListProperty<MyQmlObject> list1 = obj->property("list1").value<QQmlListProperty<MyQmlObject> >();
2830 QQmlListProperty<MyQmlObject> list2 = obj->property("list2").value<QQmlListProperty<MyQmlObject> >();
2831 QCOMPARE(list1.count(&list1), list2.count(&list2));
2832 QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0));
2833 QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1));
2838 void tst_qqmlecmascript::multiEngineObject()
2841 obj.setStringProperty("Howdy planet");
2844 e1.rootContext()->setContextProperty("thing", &obj);
2845 QQmlComponent c1(&e1, testFileUrl("multiEngineObject.qml"));
2848 e2.rootContext()->setContextProperty("thing", &obj);
2849 QQmlComponent c2(&e2, testFileUrl("multiEngineObject.qml"));
2851 QObject *o1 = c1.create();
2852 QObject *o2 = c2.create();
2854 QCOMPARE(o1->property("test").toString(), QString("Howdy planet"));
2855 QCOMPARE(o2->property("test").toString(), QString("Howdy planet"));
2861 // Test that references to QObjects are cleanup when the object is destroyed
2862 void tst_qqmlecmascript::deletedObject()
2864 QQmlComponent component(&engine, testFileUrl("deletedObject.qml"));
2866 QObject *object = component.create();
2868 QCOMPARE(object->property("test1").toBool(), true);
2869 QCOMPARE(object->property("test2").toBool(), true);
2870 QCOMPARE(object->property("test3").toBool(), true);
2871 QCOMPARE(object->property("test4").toBool(), true);
2876 void tst_qqmlecmascript::attachedPropertyScope()
2878 QQmlComponent component(&engine, testFileUrl("attachedPropertyScope.qml"));
2880 QObject *object = component.create();
2881 QVERIFY(object != 0);
2883 MyQmlAttachedObject *attached =
2884 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
2885 QVERIFY(attached != 0);
2887 QCOMPARE(object->property("value2").toInt(), 0);
2889 attached->emitMySignal();
2891 QCOMPARE(object->property("value2").toInt(), 9);
2896 void tst_qqmlecmascript::scriptConnect()
2899 QQmlComponent component(&engine, testFileUrl("scriptConnect.1.qml"));
2901 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2902 QVERIFY(object != 0);
2904 QCOMPARE(object->property("test").toBool(), false);
2905 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2906 QCOMPARE(object->property("test").toBool(), true);
2912 QQmlComponent component(&engine, testFileUrl("scriptConnect.2.qml"));
2914 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2915 QVERIFY(object != 0);
2917 QCOMPARE(object->property("test").toBool(), false);
2918 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2919 QCOMPARE(object->property("test").toBool(), true);
2925 QQmlComponent component(&engine, testFileUrl("scriptConnect.3.qml"));
2927 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2928 QVERIFY(object != 0);
2930 QCOMPARE(object->property("test").toBool(), false);
2931 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2932 QCOMPARE(object->property("test").toBool(), true);
2938 QQmlComponent component(&engine, testFileUrl("scriptConnect.4.qml"));
2940 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2941 QVERIFY(object != 0);
2943 QCOMPARE(object->methodCalled(), false);
2944 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2945 QCOMPARE(object->methodCalled(), true);
2951 QQmlComponent component(&engine, testFileUrl("scriptConnect.5.qml"));
2953 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2954 QVERIFY(object != 0);
2956 QCOMPARE(object->methodCalled(), false);
2957 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2958 QCOMPARE(object->methodCalled(), true);
2964 QQmlComponent component(&engine, testFileUrl("scriptConnect.6.qml"));
2966 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2967 QVERIFY(object != 0);
2969 QCOMPARE(object->property("test").toInt(), 0);
2970 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2971 QCOMPARE(object->property("test").toInt(), 2);
2977 void tst_qqmlecmascript::scriptDisconnect()
2980 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.1.qml"));
2982 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2983 QVERIFY(object != 0);
2985 QCOMPARE(object->property("test").toInt(), 0);
2986 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2987 QCOMPARE(object->property("test").toInt(), 1);
2988 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2989 QCOMPARE(object->property("test").toInt(), 2);
2990 emit object->basicSignal();
2991 QCOMPARE(object->property("test").toInt(), 2);
2992 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2993 QCOMPARE(object->property("test").toInt(), 2);
2999 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.2.qml"));
3001 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3002 QVERIFY(object != 0);
3004 QCOMPARE(object->property("test").toInt(), 0);
3005 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3006 QCOMPARE(object->property("test").toInt(), 1);
3007 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3008 QCOMPARE(object->property("test").toInt(), 2);
3009 emit object->basicSignal();
3010 QCOMPARE(object->property("test").toInt(), 2);
3011 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3012 QCOMPARE(object->property("test").toInt(), 2);
3018 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.3.qml"));
3020 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3021 QVERIFY(object != 0);
3023 QCOMPARE(object->property("test").toInt(), 0);
3024 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3025 QCOMPARE(object->property("test").toInt(), 1);
3026 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3027 QCOMPARE(object->property("test").toInt(), 2);
3028 emit object->basicSignal();
3029 QCOMPARE(object->property("test").toInt(), 2);
3030 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3031 QCOMPARE(object->property("test").toInt(), 3);
3036 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.4.qml"));
3038 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3039 QVERIFY(object != 0);
3041 QCOMPARE(object->property("test").toInt(), 0);
3042 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3043 QCOMPARE(object->property("test").toInt(), 1);
3044 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3045 QCOMPARE(object->property("test").toInt(), 2);
3046 emit object->basicSignal();
3047 QCOMPARE(object->property("test").toInt(), 2);
3048 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3049 QCOMPARE(object->property("test").toInt(), 3);
3055 class OwnershipObject : public QObject
3059 OwnershipObject() { object = new QObject; }
3061 QPointer<QObject> object;
3064 QObject *getObject() { return object; }
3067 void tst_qqmlecmascript::ownership()
3069 OwnershipObject own;
3070 QQmlContext *context = new QQmlContext(engine.rootContext());
3071 context->setContextObject(&own);
3074 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3076 QVERIFY(own.object != 0);
3078 QObject *object = component.create(context);
3080 engine.collectGarbage();
3082 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3083 QCoreApplication::processEvents();
3085 QVERIFY(own.object == 0);
3090 own.object = new QObject(&own);
3093 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3095 QVERIFY(own.object != 0);
3097 QObject *object = component.create(context);
3099 engine.collectGarbage();
3101 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3102 QCoreApplication::processEvents();
3104 QVERIFY(own.object != 0);
3112 class CppOwnershipReturnValue : public QObject
3116 CppOwnershipReturnValue() : value(0) {}
3117 ~CppOwnershipReturnValue() { delete value; }
3119 Q_INVOKABLE QObject *create() {
3120 value = new QObject;
3121 QQmlEngine::setObjectOwnership(value, QQmlEngine::CppOwnership);
3125 Q_INVOKABLE MyQmlObject *createQmlObject() {
3126 MyQmlObject *rv = new MyQmlObject;
3131 QPointer<QObject> value;
3135 // Test setObjectOwnership(CppOwnership) works even when there is no QQmlData
3136 void tst_qqmlecmascript::cppOwnershipReturnValue()
3138 CppOwnershipReturnValue source;
3142 engine.rootContext()->setContextProperty("source", &source);
3144 QVERIFY(source.value == 0);
3146 QQmlComponent component(&engine);
3147 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl());
3149 QObject *object = component.create();
3151 QVERIFY(object != 0);
3152 QVERIFY(source.value != 0);
3157 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3158 QCoreApplication::processEvents();
3160 QVERIFY(source.value != 0);
3164 void tst_qqmlecmascript::ownershipCustomReturnValue()
3166 CppOwnershipReturnValue source;
3170 engine.rootContext()->setContextProperty("source", &source);
3172 QVERIFY(source.value == 0);
3174 QQmlComponent component(&engine);
3175 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl());
3177 QObject *object = component.create();
3179 QVERIFY(object != 0);
3180 QVERIFY(source.value != 0);
3185 engine.collectGarbage();
3186 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3187 QCoreApplication::processEvents();
3189 QVERIFY(source.value == 0);
3192 //the return value from getObject will be JS ownership,
3193 //unless strong Cpp ownership has been set
3194 class OwnershipChangingObject : public QObject
3198 OwnershipChangingObject(): object(0) { }
3200 QPointer<QObject> object;
3203 QObject *getObject() { return object; }
3204 void setObject(QObject *obj) { object = obj; }
3207 void tst_qqmlecmascript::ownershipRootObject()
3209 OwnershipChangingObject own;
3210 QQmlContext *context = new QQmlContext(engine.rootContext());
3211 context->setContextObject(&own);
3213 QQmlComponent component(&engine, testFileUrl("ownershipRootObject.qml"));
3214 QQmlGuard<QObject> object = component.create(context);
3217 engine.collectGarbage();
3219 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3220 QCoreApplication::processEvents();
3222 QVERIFY(own.object != 0);
3228 void tst_qqmlecmascript::ownershipConsistency()
3230 OwnershipChangingObject own;
3231 QQmlContext *context = new QQmlContext(engine.rootContext());
3232 context->setContextObject(&own);
3234 QString expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":19: Error: Invalid attempt to destroy() an indestructible object");
3235 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3236 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":15: Error: Invalid attempt to destroy() an indestructible object");
3237 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3238 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":6: Error: Invalid attempt to destroy() an indestructible object");
3239 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3240 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":10: Error: Invalid attempt to destroy() an indestructible object");
3241 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3243 QQmlComponent component(&engine, testFileUrl("ownershipConsistency.qml"));
3244 QQmlGuard<QObject> object = component.create(context);
3247 engine.collectGarbage();
3249 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3250 QCoreApplication::processEvents();
3252 QVERIFY(own.object != 0);
3258 void tst_qqmlecmascript::ownershipQmlIncubated()
3260 QQmlComponent component(&engine, testFileUrl("ownershipQmlIncubated.qml"));
3261 QObject *object = component.create();
3264 QTRY_VERIFY(object->property("incubatedItem").value<QObject*>() != 0);
3266 QMetaObject::invokeMethod(object, "deleteIncubatedItem");
3268 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3269 QCoreApplication::processEvents();
3271 QVERIFY(object->property("incubatedItem").value<QObject*>() == 0);
3276 class QListQObjectMethodsObject : public QObject
3280 QListQObjectMethodsObject() {
3281 m_objects.append(new MyQmlObject());
3282 m_objects.append(new MyQmlObject());
3285 ~QListQObjectMethodsObject() {
3286 qDeleteAll(m_objects);
3290 QList<QObject *> getObjects() { return m_objects; }
3293 QList<QObject *> m_objects;
3296 // Tests that returning a QList<QObject*> from a method works
3297 void tst_qqmlecmascript::qlistqobjectMethods()
3299 QListQObjectMethodsObject obj;
3300 QQmlContext *context = new QQmlContext(engine.rootContext());
3301 context->setContextObject(&obj);
3303 QQmlComponent component(&engine, testFileUrl("qlistqobjectMethods.qml"));
3305 QObject *object = component.create(context);
3307 QCOMPARE(object->property("test").toInt(), 2);
3308 QCOMPARE(object->property("test2").toBool(), true);
3315 void tst_qqmlecmascript::strictlyEquals()
3317 QQmlComponent component(&engine, testFileUrl("strictlyEquals.qml"));
3319 QObject *object = component.create();
3320 QVERIFY(object != 0);
3322 QCOMPARE(object->property("test1").toBool(), true);
3323 QCOMPARE(object->property("test2").toBool(), true);
3324 QCOMPARE(object->property("test3").toBool(), true);
3325 QCOMPARE(object->property("test4").toBool(), true);
3326 QCOMPARE(object->property("test5").toBool(), true);
3327 QCOMPARE(object->property("test6").toBool(), true);
3328 QCOMPARE(object->property("test7").toBool(), true);
3329 QCOMPARE(object->property("test8").toBool(), true);
3334 void tst_qqmlecmascript::compiled()
3336 QQmlComponent component(&engine, testFileUrl("compiled.qml"));
3338 QObject *object = component.create();
3339 QVERIFY(object != 0);
3341 QCOMPARE(object->property("test1").toReal(), qreal(15.7));
3342 QCOMPARE(object->property("test2").toReal(), qreal(-6.7));
3343 QCOMPARE(object->property("test3").toBool(), true);
3344 QCOMPARE(object->property("test4").toBool(), false);
3345 QCOMPARE(object->property("test5").toBool(), false);
3346 QCOMPARE(object->property("test6").toBool(), true);
3348 QCOMPARE(object->property("test7").toInt(), 185);
3349 QCOMPARE(object->property("test8").toInt(), 167);
3350 QCOMPARE(object->property("test9").toBool(), true);
3351 QCOMPARE(object->property("test10").toBool(), false);
3352 QCOMPARE(object->property("test11").toBool(), false);
3353 QCOMPARE(object->property("test12").toBool(), true);
3355 QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld"));
3356 QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World"));
3357 QCOMPARE(object->property("test15").toBool(), false);
3358 QCOMPARE(object->property("test16").toBool(), true);
3360 QCOMPARE(object->property("test17").toInt(), 5);
3361 QCOMPARE(object->property("test18").toReal(), qreal(176));
3362 QCOMPARE(object->property("test19").toInt(), 7);
3363 QCOMPARE(object->property("test20").toReal(), qreal(6.7));
3364 QCOMPARE(object->property("test21").toString(), QLatin1String("6.7"));
3365 QCOMPARE(object->property("test22").toString(), QLatin1String("!"));
3366 QCOMPARE(object->property("test23").toBool(), true);
3367 QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33));
3368 QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA));
3373 // Test that numbers assigned in bindings as strings work consistently
3374 void tst_qqmlecmascript::numberAssignment()
3376 QQmlComponent component(&engine, testFileUrl("numberAssignment.qml"));
3378 QObject *object = component.create();
3379 QVERIFY(object != 0);
3381 QCOMPARE(object->property("test1"), QVariant((qreal)6.7));
3382 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3383 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3384 QCOMPARE(object->property("test3"), QVariant((qreal)6));
3385 QCOMPARE(object->property("test4"), QVariant((qreal)6));
3387 QCOMPARE(object->property("test5"), QVariant((int)7));
3388 QCOMPARE(object->property("test6"), QVariant((int)7));
3389 QCOMPARE(object->property("test7"), QVariant((int)6));
3390 QCOMPARE(object->property("test8"), QVariant((int)6));
3392 QCOMPARE(object->property("test9"), QVariant((unsigned int)7));
3393 QCOMPARE(object->property("test10"), QVariant((unsigned int)7));
3394 QCOMPARE(object->property("test11"), QVariant((unsigned int)6));
3395 QCOMPARE(object->property("test12"), QVariant((unsigned int)6));
3400 void tst_qqmlecmascript::propertySplicing()
3402 QQmlComponent component(&engine, testFileUrl("propertySplicing.qml"));
3404 QObject *object = component.create();
3405 QVERIFY(object != 0);
3407 QCOMPARE(object->property("test").toBool(), true);
3413 void tst_qqmlecmascript::signalWithUnknownTypes()
3415 QQmlComponent component(&engine, testFileUrl("signalWithUnknownTypes.qml"));
3417 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3418 QVERIFY(object != 0);
3420 MyQmlObject::MyType type;
3421 type.value = 0x8971123;
3422 emit object->signalWithUnknownType(type);
3424 MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant());
3426 QCOMPARE(result.value, type.value);
3432 void tst_qqmlecmascript::signalWithJSValueInVariant_data()
3434 QTest::addColumn<QString>("expression");
3435 QTest::addColumn<QString>("compare");
3437 QString compareStrict("(function(a, b) { return a === b; })");
3438 QTest::newRow("true") << "true" << compareStrict;
3439 QTest::newRow("undefined") << "undefined" << compareStrict;
3440 QTest::newRow("null") << "null" << compareStrict;
3441 QTest::newRow("123") << "123" << compareStrict;
3442 QTest::newRow("'ciao'") << "'ciao'" << compareStrict;
3444 QString comparePropertiesStrict(
3446 " if (typeof b != 'object')"
3448 " var props = Object.getOwnPropertyNames(b);"
3449 " for (var i = 0; i < props.length; ++i) {"
3450 " var p = props[i];"
3451 " return arguments.callee(a[p], b[p]);"
3454 QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })" << comparePropertiesStrict;
3455 QTest::newRow("[10,20,30]") << "[10,20,30]" << comparePropertiesStrict;
3458 void tst_qqmlecmascript::signalWithJSValueInVariant()
3460 QFETCH(QString, expression);
3461 QFETCH(QString, compare);
3463 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3464 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3465 QVERIFY(object != 0);
3467 QJSValue value = engine.evaluate(expression);
3468 QVERIFY(!value.isError());
3469 object->setProperty("expression", expression);
3470 object->setProperty("compare", compare);
3471 object->setProperty("pass", false);
3473 emit object->signalWithVariant(QVariant::fromValue(value));
3474 QVERIFY(object->property("pass").toBool());
3477 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines_data()
3479 signalWithJSValueInVariant_data();
3482 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines()
3484 QFETCH(QString, expression);
3485 QFETCH(QString, compare);
3487 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3488 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3489 QVERIFY(object != 0);
3492 QJSValue value = engine2.evaluate(expression);
3493 QVERIFY(!value.isError());
3494 object->setProperty("expression", expression);
3495 object->setProperty("compare", compare);
3496 object->setProperty("pass", false);
3498 QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine.");
3499 emit object->signalWithVariant(QVariant::fromValue(value));
3500 QVERIFY(!object->property("pass").toBool());
3503 void tst_qqmlecmascript::signalWithQJSValue_data()
3505 signalWithJSValueInVariant_data();
3508 void tst_qqmlecmascript::signalWithQJSValue()
3510 QFETCH(QString, expression);
3511 QFETCH(QString, compare);
3513 QQmlComponent component(&engine, testFileUrl("signalWithQJSValue.qml"));
3514 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3515 QVERIFY(object != 0);
3517 QJSValue value = engine.evaluate(expression);
3518 QVERIFY(!value.isError());
3519 object->setProperty("expression", expression);
3520 object->setProperty("compare", compare);
3521 object->setProperty("pass", false);
3523 emit object->signalWithQJSValue(value);
3525 QVERIFY(object->property("pass").toBool());
3526 QVERIFY(object->qjsvalue().strictlyEquals(value));
3529 void tst_qqmlecmascript::moduleApi_data()
3531 QTest::addColumn<QUrl>("testfile");
3532 QTest::addColumn<QString>("errorMessage");
3533 QTest::addColumn<QStringList>("warningMessages");
3534 QTest::addColumn<QStringList>("readProperties");
3535 QTest::addColumn<QVariantList>("readExpectedValues");
3536 QTest::addColumn<QStringList>("writeProperties");
3537 QTest::addColumn<QVariantList>("writeValues");
3538 QTest::addColumn<QStringList>("readBackProperties");
3539 QTest::addColumn<QVariantList>("readBackExpectedValues");
3541 QTest::newRow("qobject, register + read + method")
3542 << testFileUrl("moduleapi/qobjectModuleApi.qml")
3545 << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
3546 << "qobjectMinorVersionTest" << "qobjectMajorVersionTest" << "qobjectParentedTest")
3547 << (QVariantList() << 20 << 20 << 1 << 20 << 20 << 26)
3553 QTest::newRow("script, register + read")
3554 << testFileUrl("moduleapi/scriptModuleApi.qml")
3557 << (QStringList() << "scriptTest")
3558 << (QVariantList() << 13)
3564 QTest::newRow("qobject, caching + read")
3565 << testFileUrl("moduleapi/qobjectModuleApiCaching.qml")
3568 << (QStringList() << "existingUriTest" << "qobjectParentedTest")
3569 << (QVariantList() << 20 << 26) // 26, shouldn't have incremented to 27.
3575 QTest::newRow("script, caching + read")
3576 << testFileUrl("moduleapi/scriptModuleApiCaching.qml")
3579 << (QStringList() << "scriptTest")
3580 << (QVariantList() << 13) // 13, shouldn't have incremented to 14.
3586 QTest::newRow("qobject, writing + readonly constraints")
3587 << testFileUrl("moduleapi/qobjectModuleApiWriting.qml")
3589 << (QStringList() << QString(testFileUrl("moduleapi/qobjectModuleApiWriting.qml").toString() + QLatin1String(":15: Error: Cannot assign to read-only property \"qobjectTestProperty\"")))
3590 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3591 << (QVariantList() << 20 << 50 << 10)
3592 << (QStringList() << "firstProperty" << "secondProperty")
3593 << (QVariantList() << 30 << 30)
3594 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3595 << (QVariantList() << 20 << 30 << 30);
3597 QTest::newRow("script, writing + readonly constraints")
3598 << testFileUrl("moduleapi/scriptModuleApiWriting.qml")
3600 << (QStringList() << QString(testFileUrl("moduleapi/scriptModuleApiWriting.qml").toString() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\"")))
3601 << (QStringList() << "readBack" << "unchanged")
3602 << (QVariantList() << 13 << 42)
3603 << (QStringList() << "firstProperty" << "secondProperty")
3604 << (QVariantList() << 30 << 30)
3605 << (QStringList() << "readBack" << "unchanged")
3606 << (QVariantList() << 30 << 42);
3608 QTest::newRow("qobject module API enum values in JS")
3609 << testFileUrl("moduleapi/qobjectModuleApiEnums.qml")
3612 << (QStringList() << "enumValue" << "enumMethod")
3613 << (QVariantList() << 42 << 30)
3619 QTest::newRow("qobject, invalid major version fail")
3620 << testFileUrl("moduleapi/moduleApiMajorVersionFail.qml")
3621 << QString("QQmlComponent: Component is not ready")
3630 QTest::newRow("qobject, invalid minor version fail")
3631 << testFileUrl("moduleapi/moduleApiMinorVersionFail.qml")
3632 << QString("QQmlComponent: Component is not ready")
3642 void tst_qqmlecmascript::moduleApi()
3644 QFETCH(QUrl, testfile);
3645 QFETCH(QString, errorMessage);
3646 QFETCH(QStringList, warningMessages);
3647 QFETCH(QStringList, readProperties);
3648 QFETCH(QVariantList, readExpectedValues);
3649 QFETCH(QStringList, writeProperties);
3650 QFETCH(QVariantList, writeValues);
3651 QFETCH(QStringList, readBackProperties);
3652 QFETCH(QVariantList, readBackExpectedValues);
3654 QQmlComponent component(&engine, testfile);
3656 if (!errorMessage.isEmpty())
3657 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3659 if (warningMessages.size())
3660 foreach (const QString &warning, warningMessages)
3661 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3663 QObject *object = component.create();
3664 if (!errorMessage.isEmpty()) {
3665 QVERIFY(object == 0);
3667 QVERIFY(object != 0);
3668 for (int i = 0; i < readProperties.size(); ++i)
3669 QCOMPARE(object->property(readProperties.at(i).toLatin1().constData()), readExpectedValues.at(i));
3670 for (int i = 0; i < writeProperties.size(); ++i)
3671 QVERIFY(object->setProperty(writeProperties.at(i).toLatin1().constData(), writeValues.at(i)));
3672 for (int i = 0; i < readBackProperties.size(); ++i)
3673 QCOMPARE(object->property(readBackProperties.at(i).toLatin1().constData()), readBackExpectedValues.at(i));
3678 void tst_qqmlecmascript::importScripts_data()
3680 QTest::addColumn<QUrl>("testfile");
3681 QTest::addColumn<QString>("errorMessage");
3682 QTest::addColumn<QStringList>("warningMessages");
3683 QTest::addColumn<QStringList>("propertyNames");
3684 QTest::addColumn<QVariantList>("propertyValues");
3686 QTest::newRow("basic functionality")
3687 << testFileUrl("jsimport/testImport.qml")
3690 << (QStringList() << QLatin1String("importedScriptStringValue")
3691 << QLatin1String("importedScriptFunctionValue")
3692 << QLatin1String("importedModuleAttachedPropertyValue")
3693 << QLatin1String("importedModuleEnumValue"))
3694 << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3699 QTest::newRow("import scoping")
3700 << testFileUrl("jsimport/testImportScoping.qml")
3703 << (QStringList() << QLatin1String("componentError"))
3704 << (QVariantList() << QVariant(5));
3706 QTest::newRow("parent scope shouldn't be inherited by import with imports")
3707 << testFileUrl("jsimportfail/failOne.qml")
3709 << (QStringList() << QString(testFileUrl("jsimportfail/failOne.qml").toString() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
3710 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3711 << (QVariantList() << QVariant(QString()));
3713 QTest::newRow("javascript imports in an import should be private to the import scope")
3714 << testFileUrl("jsimportfail/failTwo.qml")
3716 << (QStringList() << QString(testFileUrl("jsimportfail/failTwo.qml").toString() + QLatin1String(":6: ReferenceError: ImportOneJs is not defined")))
3717 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3718 << (QVariantList() << QVariant(QString()));
3720 QTest::newRow("module imports in an import should be private to the import scope")
3721 << testFileUrl("jsimportfail/failThree.qml")
3723 << (QStringList() << QString(testFileUrl("jsimportfail/failThree.qml").toString() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
3724 << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
3725 << (QVariantList() << QVariant(false));
3727 QTest::newRow("typenames in an import should be private to the import scope")
3728 << testFileUrl("jsimportfail/failFour.qml")
3730 << (QStringList() << QString(testFileUrl("jsimportfail/failFour.qml").toString() + QLatin1String(":6: ReferenceError: JsQtTest is not defined")))
3731 << (QStringList() << QLatin1String("importedModuleEnumValue"))
3732 << (QVariantList() << QVariant(0));
3734 QTest::newRow("import with imports has it's own activation scope")
3735 << testFileUrl("jsimportfail/failFive.qml")
3737 << (QStringList() << QString(testFileUrl("jsimportfail/importWithImports.js").toString() + QLatin1String(":8: ReferenceError: Component is not defined")))
3738 << (QStringList() << QLatin1String("componentError"))
3739 << (QVariantList() << QVariant(0));
3741 QTest::newRow("import pragma library script")
3742 << testFileUrl("jsimport/testImportPragmaLibrary.qml")
3745 << (QStringList() << QLatin1String("testValue"))
3746 << (QVariantList() << QVariant(31));
3748 QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3749 << testFileUrl("jsimportfail/testImportPragmaLibrary.qml")
3751 << (QStringList() << QString(testFileUrl("jsimportfail/importPragmaLibrary.js").toString() + QLatin1String(":6: ReferenceError: Component is not defined")))
3752 << (QStringList() << QLatin1String("testValue"))
3753 << (QVariantList() << QVariant(0));
3755 QTest::newRow("import pragma library script which has an import")
3756 << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
3759 << (QStringList() << QLatin1String("testValue"))
3760 << (QVariantList() << QVariant(55));
3762 QTest::newRow("import pragma library script which has a pragma library import")
3763 << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3766 << (QStringList() << QLatin1String("testValue"))
3767 << (QVariantList() << QVariant(18));
3769 QTest::newRow("import module api into js import")
3770 << testFileUrl("jsimport/testImportModuleApi.qml")
3773 << (QStringList() << QLatin1String("testValue"))
3774 << (QVariantList() << QVariant(20));
3776 QTest::newRow("import module which exports a script")
3777 << testFileUrl("jsimport/testJsImport.qml")
3780 << (QStringList() << QLatin1String("importedScriptStringValue")
3781 << QLatin1String("renamedScriptStringValue")
3782 << QLatin1String("reimportedScriptStringValue"))
3783 << (QVariantList() << QVariant(QString("Hello"))
3784 << QVariant(QString("Hello"))
3785 << QVariant(QString("Hello")));
3787 QTest::newRow("import module which exports a script which imports a remote module")
3788 << testFileUrl("jsimport/testJsRemoteImport.qml")
3791 << (QStringList() << QLatin1String("importedScriptStringValue")
3792 << QLatin1String("renamedScriptStringValue")
3793 << QLatin1String("reimportedScriptStringValue"))
3794 << (QVariantList() << QVariant(QString("Hello"))
3795 << QVariant(QString("Hello"))
3796 << QVariant(QString("Hello")));
3798 QTest::newRow("malformed import statement")
3799 << testFileUrl("jsimportfail/malformedImport.qml")
3801 << (QStringList() << testFileUrl("jsimportfail/malformedImport.js").toString() + QLatin1String(":1: SyntaxError: Unexpected token ."))
3805 QTest::newRow("malformed file name")
3806 << testFileUrl("jsimportfail/malformedFile.qml")
3808 << (QStringList() << testFileUrl("jsimportfail/malformedFile.js").toString() + QLatin1String(":0:1: Imported file must be a script"))
3812 QTest::newRow("missing file qualifier")
3813 << testFileUrl("jsimportfail/missingFileQualifier.qml")
3815 << (QStringList() << testFileUrl("jsimportfail/missingFileQualifier.js").toString() + QLatin1String(":0:1: File import requires a qualifier"))
3819 QTest::newRow("malformed file qualifier")
3820 << testFileUrl("jsimportfail/malformedFileQualifier.qml")
3822 << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.js").toString() + QLatin1String(":0:1: File import requires a qualifier"))
3826 QTest::newRow("malformed module qualifier 2")
3827 << testFileUrl("jsimportfail/malformedFileQualifier.2.qml")
3829 << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.2.js").toString() + QLatin1String(":0:1: Invalid import qualifier"))
3833 QTest::newRow("malformed module uri")
3834 << testFileUrl("jsimportfail/malformedModule.qml")
3836 << (QStringList() << testFileUrl("jsimportfail/malformedModule.js").toString() + QLatin1String(":0:1: Invalid module URI"))
3840 QTest::newRow("missing module version")
3841 << testFileUrl("jsimportfail/missingModuleVersion.qml")
3843 << (QStringList() << testFileUrl("jsimportfail/missingModuleVersion.js").toString() + QLatin1String(":0:1: Module import requires a version"))
3847 QTest::newRow("malformed module version")
3848 << testFileUrl("jsimportfail/malformedModuleVersion.qml")
3850 << (QStringList() << testFileUrl("jsimportfail/malformedModuleVersion.js").toString() + QLatin1String(":0:1: Module import requires a version"))
3854 QTest::newRow("missing module qualifier")
3855 << testFileUrl("jsimportfail/missingModuleQualifier.qml")
3857 << (QStringList() << testFileUrl("jsimportfail/missingModuleQualifier.js").toString() + QLatin1String(":0:1: Module import requires a qualifier"))
3861 QTest::newRow("malformed module qualifier")
3862 << testFileUrl("jsimportfail/malformedModuleQualifier.qml")
3864 << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.js").toString() + QLatin1String(":0:1: Module import requires a qualifier"))
3868 QTest::newRow("malformed module qualifier 2")
3869 << testFileUrl("jsimportfail/malformedModuleQualifier.2.qml")
3871 << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.2.js").toString() + QLatin1String(":0:1: Invalid import qualifier"))
3876 void tst_qqmlecmascript::importScripts()
3878 QFETCH(QUrl, testfile);
3879 QFETCH(QString, errorMessage);
3880 QFETCH(QStringList, warningMessages);
3881 QFETCH(QStringList, propertyNames);
3882 QFETCH(QVariantList, propertyValues);
3884 TestHTTPServer server(8111);
3885 QVERIFY(server.isValid());
3886 server.serveDirectory(dataDirectory() + "/remote");
3888 QStringList importPathList = engine.importPathList();
3890 QString remotePath(QLatin1String("http://127.0.0.1:8111/"));
3891 engine.addImportPath(remotePath);
3893 QQmlComponent component(&engine, testfile);
3895 if (!errorMessage.isEmpty())
3896 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3898 if (warningMessages.size())
3899 foreach (const QString &warning, warningMessages)
3900 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3902 QTRY_VERIFY(component.isReady());
3904 QObject *object = component.create();
3905 if (!errorMessage.isEmpty()) {
3906 QVERIFY(object == 0);
3908 QVERIFY(object != 0);
3909 for (int i = 0; i < propertyNames.size(); ++i)
3910 QCOMPARE(object->property(propertyNames.at(i).toLatin1().constData()), propertyValues.at(i));
3914 engine.setImportPathList(importPathList);
3917 void tst_qqmlecmascript::scarceResources_other()
3919 /* These tests require knowledge of state, since we test values after
3920 performing signal or function invocation. */
3922 QPixmap origPixmap(100, 100);
3923 origPixmap.fill(Qt::blue);
3924 QString srp_name, expectedWarning;
3925 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
3926 ScarceResourceObject *eo = 0;
3928 QObject *object = 0;
3930 /* property var semantics */
3932 // test that scarce resources are handled properly in signal invocation
3933 QQmlComponent varComponentTen(&engine, testFileUrl("scarceResourceSignal.var.qml"));
3934 object = varComponentTen.create();
3935 srsc = object->findChild<QObject*>("srsc");
3937 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3938 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3939 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3940 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3941 QMetaObject::invokeMethod(srsc, "testSignal");
3942 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3943 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3944 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3945 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3946 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3947 QVERIFY(srsc->property("scarceResourceCopy").isValid());
3948 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3949 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3950 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3951 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3954 // test that scarce resources are handled properly from js functions in qml files
3955 QQmlComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml"));
3956 object = varComponentEleven.create();
3957 QVERIFY(object != 0);
3958 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3959 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3960 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3961 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3962 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3963 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3964 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3965 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3966 QMetaObject::invokeMethod(object, "releaseScarceResource");
3967 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3968 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3969 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3970 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3973 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3974 QQmlComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
3975 object = varComponentTwelve.create();
3976 QVERIFY(object != 0);
3977 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3978 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3979 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3980 srp_name = object->property("srp_name").toString();
3981 expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3982 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3983 QMetaObject::invokeMethod(object, "retrieveScarceResource");
3984 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3985 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3986 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3987 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3990 // test that if an Item which has JS ownership but has a scarce resource property is garbage collected,
3991 // that the scarce resource is removed from the engine's list of scarce resources to clean up.
3992 QQmlComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml"));
3993 object = varComponentThirteen.create();
3994 QVERIFY(object != 0);
3995 QVERIFY(!object->property("varProperty").isValid()); // not assigned yet
3996 QMetaObject::invokeMethod(object, "assignVarProperty");
3997 QVERIFY(ep->scarceResources.isEmpty()); // the scarce resource is a VME property.
3998 QMetaObject::invokeMethod(object, "deassignVarProperty");
3999 QVERIFY(ep->scarceResources.isEmpty()); // should still be empty; the resource should have been released on gc.
4002 /* property variant semantics */
4004 // test that scarce resources are handled properly in signal invocation
4005 QQmlComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml"));
4006 object = variantComponentTen.create();
4007 QVERIFY(object != 0);
4008 srsc = object->findChild<QObject*>("srsc");
4010 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
4011 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
4012 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4013 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4014 QMetaObject::invokeMethod(srsc, "testSignal");
4015 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
4016 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
4017 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4018 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
4019 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
4020 QVERIFY(srsc->property("scarceResourceCopy").isValid());
4021 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4022 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4023 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
4024 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4027 // test that scarce resources are handled properly from js functions in qml files
4028 QQmlComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml"));
4029 object = variantComponentEleven.create();
4030 QVERIFY(object != 0);
4031 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4032 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4033 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4034 QMetaObject::invokeMethod(object, "retrieveScarceResource");
4035 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
4036 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4037 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4038 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
4039 QMetaObject::invokeMethod(object, "releaseScarceResource");
4040 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
4041 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4042 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4043 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4046 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
4047 QQmlComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml"));
4048 object = variantComponentTwelve.create();
4049 QVERIFY(object != 0);
4050 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4051 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4052 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4053 srp_name = object->property("srp_name").toString();
4054 expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
4055 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
4056 QMetaObject::invokeMethod(object, "retrieveScarceResource");
4057 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
4058 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4059 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4060 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4064 void tst_qqmlecmascript::scarceResources_data()
4066 QTest::addColumn<QUrl>("qmlFile");
4067 QTest::addColumn<bool>("readDetachStatus");
4068 QTest::addColumn<bool>("expectedDetachStatus");
4069 QTest::addColumn<QStringList>("propertyNames");
4070 QTest::addColumn<QVariantList>("expectedValidity");
4071 QTest::addColumn<QVariantList>("expectedValues");
4072 QTest::addColumn<QStringList>("expectedErrors");
4074 QPixmap origPixmap(100, 100);
4075 origPixmap.fill(Qt::blue);
4077 /* property var semantics */
4079 // in the following three cases, the instance created from the component
4080 // has a property which is a copy of the scarce resource; hence, the
4081 // resource should NOT be detached prior to deletion of the object instance,
4082 // unless the resource is destroyed explicitly.
4083 QTest::newRow("var: import scarce resource copy directly")
4084 << testFileUrl("scarceResourceCopy.var.qml")
4086 << false // won't be detached, because assigned to property and not explicitly released
4087 << (QStringList() << QLatin1String("scarceResourceCopy"))
4088 << (QList<QVariant>() << true)
4089 << (QList<QVariant>() << origPixmap)
4092 QTest::newRow("var: import scarce resource copy from JS")
4093 << testFileUrl("scarceResourceCopyFromJs.var.qml")
4095 << false // won't be detached, because assigned to property and not explicitly released
4096 << (QStringList() << QLatin1String("scarceResourceCopy"))
4097 << (QList<QVariant>() << true)
4098 << (QList<QVariant>() << origPixmap)
4101 QTest::newRow("var: import released scarce resource copy from JS")
4102 << testFileUrl("scarceResourceDestroyedCopy.var.qml")
4104 << true // explicitly released, so it will be detached
4105 << (QStringList() << QLatin1String("scarceResourceCopy"))
4106 << (QList<QVariant>() << false)
4107 << (QList<QVariant>() << QVariant())
4110 // in the following three cases, no other copy should exist in memory,
4111 // and so it should be detached (unless explicitly preserved).
4112 QTest::newRow("var: import auto-release SR from JS in binding side-effect")
4113 << testFileUrl("scarceResourceTest.var.qml")
4115 << true // auto released, so it will be detached
4116 << (QStringList() << QLatin1String("scarceResourceTest"))
4117 << (QList<QVariant>() << true)
4118 << (QList<QVariant>() << QVariant(100))
4120 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4121 << testFileUrl("scarceResourceTestPreserve.var.qml")
4123 << false // won't be detached because we explicitly preserve it
4124 << (QStringList() << QLatin1String("scarceResourceTest"))
4125 << (QList<QVariant>() << true)
4126 << (QList<QVariant>() << QVariant(100))
4128 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4129 << testFileUrl("scarceResourceTestMultiple.var.qml")
4131 << true // will be detached because all resources were released manually or automatically.
4132 << (QStringList() << QLatin1String("scarceResourceTest"))
4133 << (QList<QVariant>() << true)
4134 << (QList<QVariant>() << QVariant(100))
4137 // In the following three cases, test that scarce resources are handled
4138 // correctly for imports.
4139 QTest::newRow("var: import with no binding")
4140 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4141 << false // cannot check detach status.
4144 << QList<QVariant>()
4145 << QList<QVariant>()
4147 QTest::newRow("var: import with binding without explicit preserve")
4148 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4151 << (QStringList() << QLatin1String("scarceResourceCopy"))
4152 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4153 << (QList<QVariant>() << QVariant())
4155 QTest::newRow("var: import with explicit release after binding evaluation")
4156 << testFileUrl("scarceResourceCopyImport.var.qml")
4159 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4160 << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated.
4161 << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true))
4163 QTest::newRow("var: import with different js objects")
4164 << testFileUrl("scarceResourceCopyImportDifferent.var.qml")
4167 << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4168 << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4169 << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false))
4171 QTest::newRow("var: import with different js objects and explicit release")
4172 << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml")
4175 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4176 << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4177 << (QList<QVariant>() << QVariant(origPixmap) << QVariant())
4179 QTest::newRow("var: import with same js objects and explicit release")
4180 << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml")
4183 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4184 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4185 << (QList<QVariant>() << QVariant() << QVariant())
4187 QTest::newRow("var: binding with same js objects and explicit release")
4188 << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml")
4191 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4192 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4193 << (QList<QVariant>() << QVariant() << QVariant())
4197 /* property variant semantics */
4199 // in the following three cases, the instance created from the component
4200 // has a property which is a copy of the scarce resource; hence, the
4201 // resource should NOT be detached prior to deletion of the object instance,
4202 // unless the resource is destroyed explicitly.
4203 QTest::newRow("variant: import scarce resource copy directly")
4204 << testFileUrl("scarceResourceCopy.variant.qml")
4206 << false // won't be detached, because assigned to property and not explicitly released
4207 << (QStringList() << QLatin1String("scarceResourceCopy"))
4208 << (QList<QVariant>() << true)
4209 << (QList<QVariant>() << origPixmap)
4212 QTest::newRow("variant: import scarce resource copy from JS")
4213 << testFileUrl("scarceResourceCopyFromJs.variant.qml")
4215 << false // won't be detached, because assigned to property and not explicitly released
4216 << (QStringList() << QLatin1String("scarceResourceCopy"))
4217 << (QList<QVariant>() << true)
4218 << (QList<QVariant>() << origPixmap)
4221 QTest::newRow("variant: import released scarce resource copy from JS")
4222 << testFileUrl("scarceResourceDestroyedCopy.variant.qml")
4224 << true // explicitly released, so it will be detached
4225 << (QStringList() << QLatin1String("scarceResourceCopy"))
4226 << (QList<QVariant>() << false)
4227 << (QList<QVariant>() << QVariant())
4230 // in the following three cases, no other copy should exist in memory,
4231 // and so it should be detached (unless explicitly preserved).
4232 QTest::newRow("variant: import auto-release SR from JS in binding side-effect")
4233 << testFileUrl("scarceResourceTest.variant.qml")
4235 << true // auto released, so it will be detached
4236 << (QStringList() << QLatin1String("scarceResourceTest"))
4237 << (QList<QVariant>() << true)
4238 << (QList<QVariant>() << QVariant(100))
4240 QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect")
4241 << testFileUrl("scarceResourceTestPreserve.variant.qml")
4243 << false // won't be detached because we explicitly preserve it
4244 << (QStringList() << QLatin1String("scarceResourceTest"))
4245 << (QList<QVariant>() << true)
4246 << (QList<QVariant>() << QVariant(100))
4248 QTest::newRow("variant: import multiple scarce resources")
4249 << testFileUrl("scarceResourceTestMultiple.variant.qml")
4251 << true // will be detached because all resources were released manually or automatically.
4252 << (QStringList() << QLatin1String("scarceResourceTest"))
4253 << (QList<QVariant>() << true)
4254 << (QList<QVariant>() << QVariant(100))
4257 // In the following three cases, test that scarce resources are handled
4258 // correctly for imports.
4259 QTest::newRow("variant: import with no binding")
4260 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4261 << false // cannot check detach status.
4264 << QList<QVariant>()
4265 << QList<QVariant>()
4267 QTest::newRow("variant: import with binding without explicit preserve")
4268 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4271 << (QStringList() << QLatin1String("scarceResourceCopy"))
4272 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4273 << (QList<QVariant>() << QVariant())
4275 QTest::newRow("variant: import with explicit release after binding evaluation")
4276 << testFileUrl("scarceResourceCopyImport.variant.qml")
4279 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo"))
4280 << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies.
4281 << (QList<QVariant>() << origPixmap << origPixmap << QVariant())
4285 void tst_qqmlecmascript::scarceResources()
4287 QFETCH(QUrl, qmlFile);
4288 QFETCH(bool, readDetachStatus);
4289 QFETCH(bool, expectedDetachStatus);
4290 QFETCH(QStringList, propertyNames);
4291 QFETCH(QVariantList, expectedValidity);
4292 QFETCH(QVariantList, expectedValues);
4293 QFETCH(QStringList, expectedErrors);
4295 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4296 ScarceResourceObject *eo = 0;
4297 QObject *object = 0;
4299 QQmlComponent c(&engine, qmlFile);
4300 object = c.create();
4301 QVERIFY(object != 0);
4302 for (int i = 0; i < propertyNames.size(); ++i) {
4303 QString prop = propertyNames.at(i);
4304 bool validity = expectedValidity.at(i).toBool();
4305 QVariant value = expectedValues.at(i);
4307 QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity);
4308 if (value.type() == QVariant::Int) {
4309 QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt());
4310 } else if (value.type() == QVariant::Pixmap) {
4311 QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>());
4315 if (readDetachStatus) {
4316 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4317 QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
4320 QVERIFY(ep->scarceResources.isEmpty());
4324 void tst_qqmlecmascript::propertyChangeSlots()
4326 // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
4327 QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml"));
4328 QObject *object = component.create();
4329 QVERIFY(object != 0);
4332 // ensure that invalid property names fail properly.
4333 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4334 QQmlComponent e1(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.1.qml"));
4335 QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
4336 QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
4337 object = e1.create();
4338 QVERIFY(object == 0);
4341 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4342 QQmlComponent e2(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.2.qml"));
4343 expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
4344 QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
4345 object = e2.create();
4346 QVERIFY(object == 0);
4349 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4350 QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml"));
4351 expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
4352 QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
4353 object = e3.create();
4354 QVERIFY(object == 0);
4357 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4358 QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml"));
4359 expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
4360 QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
4361 object = e4.create();
4362 QVERIFY(object == 0);
4366 void tst_qqmlecmascript::propertyVar_data()
4368 QTest::addColumn<QUrl>("qmlFile");
4371 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyVar.1.qml");
4372 QTest::newRow("non-bindable object changed") << testFileUrl("propertyVar.2.qml");
4373 QTest::newRow("primitive changed") << testFileUrl("propertyVar.3.qml");
4374 QTest::newRow("javascript array modification") << testFileUrl("propertyVar.4.qml");
4375 QTest::newRow("javascript map modification") << testFileUrl("propertyVar.5.qml");
4376 QTest::newRow("javascript array assignment") << testFileUrl("propertyVar.6.qml");
4377 QTest::newRow("javascript map assignment") << testFileUrl("propertyVar.7.qml");
4378 QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml");
4379 QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml");
4380 QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml");
4381 QTest::newRow("javascript function assignment") << testFileUrl("propertyVar.11.qml");
4382 QTest::newRow("javascript special assignment") << testFileUrl("propertyVar.12.qml");
4383 QTest::newRow("declarative binding assignment") << testFileUrl("propertyVar.13.qml");
4384 QTest::newRow("imperative binding assignment") << testFileUrl("propertyVar.14.qml");
4385 QTest::newRow("stored binding assignment") << testFileUrl("propertyVar.15.qml");
4386 QTest::newRow("function expression binding assignment") << testFileUrl("propertyVar.16.qml");
4389 void tst_qqmlecmascript::propertyVar()
4391 QFETCH(QUrl, qmlFile);
4393 QQmlComponent component(&engine, qmlFile);
4394 QObject *object = component.create();
4395 QVERIFY(object != 0);
4397 QCOMPARE(object->property("test").toBool(), true);
4402 void tst_qqmlecmascript::propertyQJSValue_data()
4404 QTest::addColumn<QUrl>("qmlFile");
4407 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyQJSValue.1.qml");
4408 QTest::newRow("non-bindable object changed") << testFileUrl("propertyQJSValue.2.qml");
4409 QTest::newRow("primitive changed") << testFileUrl("propertyQJSValue.3.qml");
4410 QTest::newRow("javascript array modification") << testFileUrl("propertyQJSValue.4.qml");
4411 QTest::newRow("javascript map modification") << testFileUrl("propertyQJSValue.5.qml");
4412 QTest::newRow("javascript array assignment") << testFileUrl("propertyQJSValue.6.qml");
4413 QTest::newRow("javascript map assignment") << testFileUrl("propertyQJSValue.7.qml");
4414 QTest::newRow("literal property assignment") << testFileUrl("propertyQJSValue.8.qml");
4415 QTest::newRow("qobject property assignment") << testFileUrl("propertyQJSValue.9.qml");
4416 QTest::newRow("base class var property assignment") << testFileUrl("propertyQJSValue.10.qml");
4417 QTest::newRow("javascript function assignment") << testFileUrl("propertyQJSValue.11.qml");
4418 QTest::newRow("javascript special assignment") << testFileUrl("propertyQJSValue.12.qml");
4419 QTest::newRow("declarative binding assignment") << testFileUrl("propertyQJSValue.13.qml");
4420 QTest::newRow("imperative binding assignment") << testFileUrl("propertyQJSValue.14.qml");
4421 QTest::newRow("stored binding assignment") << testFileUrl("propertyQJSValue.15.qml");
4422 QTest::newRow("javascript function binding") << testFileUrl("propertyQJSValue.16.qml");
4424 QTest::newRow("reset property") << testFileUrl("propertyQJSValue.reset.qml");
4425 QTest::newRow("reset property in binding") << testFileUrl("propertyQJSValue.bindingreset.qml");
4428 void tst_qqmlecmascript::propertyQJSValue()
4430 QFETCH(QUrl, qmlFile);
4432 QQmlComponent component(&engine, qmlFile);
4433 QObject *object = component.create();
4434 QVERIFY(object != 0);
4436 QCOMPARE(object->property("test").toBool(), true);
4441 // Tests that we can write QVariant values to var properties from C++
4442 void tst_qqmlecmascript::propertyVarCpp()
4444 QObject *object = 0;
4446 // ensure that writing to and reading from a var property from cpp works as required.
4447 // Literal values stored in var properties can be read and written as QVariants
4448 // of a specific type, whereas object values are read as QVariantMaps.
4449 QQmlComponent component(&engine, testFileUrl("propertyVarCpp.qml"));
4450 object = component.create();
4451 QVERIFY(object != 0);
4452 // assign int to property var that currently has int assigned
4453 QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
4454 QCOMPARE(object->property("varBound"), QVariant(15));
4455 QCOMPARE(object->property("intBound"), QVariant(15));
4456 QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
4457 QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
4458 // assign string to property var that current has bool assigned
4459 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
4460 QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
4461 QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
4462 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
4463 // now enforce behaviour when accessing JavaScript objects from cpp.
4464 QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
4468 static void gc(QQmlEngine &engine)
4470 engine.collectGarbage();
4471 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4472 QCoreApplication::processEvents();
4475 void tst_qqmlecmascript::propertyVarOwnership()
4477 // Referenced JS objects are not collected
4479 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.qml"));
4480 QObject *object = component.create();
4481 QVERIFY(object != 0);
4482 QCOMPARE(object->property("test").toBool(), false);
4483 QMetaObject::invokeMethod(object, "runTest");
4484 QCOMPARE(object->property("test").toBool(), true);
4487 // Referenced JS objects are not collected
4489 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml"));
4490 QObject *object = component.create();
4491 QVERIFY(object != 0);
4492 QCOMPARE(object->property("test").toBool(), false);
4493 QMetaObject::invokeMethod(object, "runTest");
4494 QCOMPARE(object->property("test").toBool(), true);
4497 // Qt objects are not collected until they've been dereferenced
4499 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml"));
4500 QObject *object = component.create();
4501 QVERIFY(object != 0);
4503 QCOMPARE(object->property("test2").toBool(), false);
4504 QCOMPARE(object->property("test2").toBool(), false);
4506 QMetaObject::invokeMethod(object, "runTest");
4507 QCOMPARE(object->property("test1").toBool(), true);
4509 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4510 QVERIFY(!referencedObject.isNull());
4512 QVERIFY(!referencedObject.isNull());
4514 QMetaObject::invokeMethod(object, "runTest2");
4515 QCOMPARE(object->property("test2").toBool(), true);
4517 QVERIFY(referencedObject.isNull());
4521 // Self reference does not prevent Qt object collection
4523 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml"));
4524 QObject *object = component.create();
4525 QVERIFY(object != 0);
4527 QCOMPARE(object->property("test").toBool(), true);
4529 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4530 QVERIFY(!referencedObject.isNull());
4532 QVERIFY(!referencedObject.isNull());
4534 QMetaObject::invokeMethod(object, "runTest");
4536 QVERIFY(referencedObject.isNull());
4540 // Garbage collection cannot result in attempted dereference of empty handle
4542 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.5.qml"));
4543 QObject *object = component.create();
4544 QVERIFY(object != 0);
4545 QMetaObject::invokeMethod(object, "runTest");
4546 QCOMPARE(object->property("test").toBool(), true);
4551 void tst_qqmlecmascript::propertyVarImplicitOwnership()
4553 // The childObject has a reference to a different QObject. We want to ensure
4554 // that the different item will not be cleaned up until required. IE, the childObject
4555 // has implicit ownership of the constructed QObject.
4556 QQmlComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml"));
4557 QObject *object = component.create();
4558 QVERIFY(object != 0);
4559 QMetaObject::invokeMethod(object, "assignCircular");
4560 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4561 QCoreApplication::processEvents();
4562 QObject *rootObject = object->property("vp").value<QObject*>();
4563 QVERIFY(rootObject != 0);
4564 QObject *childObject = rootObject->findChild<QObject*>("text");
4565 QVERIFY(childObject != 0);
4566 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4567 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4568 QMetaObject::invokeMethod(childObject, "constructQObject"); // creates a reference to a constructed QObject.
4569 QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
4570 QVERIFY(!qobjectGuard.isNull());
4571 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4572 QCoreApplication::processEvents();
4573 QVERIFY(!qobjectGuard.isNull());
4574 QMetaObject::invokeMethod(object, "deassignCircular");
4575 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4576 QCoreApplication::processEvents();
4577 QVERIFY(qobjectGuard.isNull()); // should have been collected now.
4581 void tst_qqmlecmascript::propertyVarReparent()
4583 // ensure that nothing breaks if we re-parent objects
4584 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4585 QObject *object = component.create();
4586 QVERIFY(object != 0);
4587 QMetaObject::invokeMethod(object, "assignVarProp");
4588 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4589 QCoreApplication::processEvents();
4590 QObject *rect = object->property("vp").value<QObject*>();
4591 QObject *text = rect->findChild<QObject*>("textOne");
4592 QObject *text2 = rect->findChild<QObject*>("textTwo");
4593 QWeakPointer<QObject> rectGuard(rect);
4594 QWeakPointer<QObject> textGuard(text);
4595 QWeakPointer<QObject> text2Guard(text2);
4596 QVERIFY(!rectGuard.isNull());
4597 QVERIFY(!textGuard.isNull());
4598 QVERIFY(!text2Guard.isNull());
4599 QCOMPARE(text->property("textCanary").toInt(), 11);
4600 QCOMPARE(text2->property("textCanary").toInt(), 12);
4601 // now construct an image which we will reparent.
4602 QMetaObject::invokeMethod(text2, "constructQObject");
4603 QObject *image = text2->property("vp").value<QObject*>();
4604 QWeakPointer<QObject> imageGuard(image);
4605 QVERIFY(!imageGuard.isNull());
4606 QCOMPARE(image->property("imageCanary").toInt(), 13);
4607 // now reparent the "Image" object (currently, it has JS ownership)
4608 image->setParent(text); // shouldn't be collected after deassignVp now, since has a parent.
4609 QMetaObject::invokeMethod(text2, "deassignVp");
4610 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4611 QCoreApplication::processEvents();
4612 QCOMPARE(text->property("textCanary").toInt(), 11);
4613 QCOMPARE(text2->property("textCanary").toInt(), 22);
4614 QVERIFY(!imageGuard.isNull()); // should still be alive.
4615 QCOMPARE(image->property("imageCanary").toInt(), 13); // still able to access var properties
4616 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4617 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4618 QCoreApplication::processEvents();
4619 QVERIFY(imageGuard.isNull()); // should now have been deleted, due to parent being deleted.
4623 void tst_qqmlecmascript::propertyVarReparentNullContext()
4625 // sometimes reparenting can cause problems
4626 // (eg, if the ctxt is collected, varproperties are no longer available)
4627 // this test ensures that no crash occurs in that situation.
4628 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4629 QObject *object = component.create();
4630 QVERIFY(object != 0);
4631 QMetaObject::invokeMethod(object, "assignVarProp");
4632 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4633 QCoreApplication::processEvents();
4634 QObject *rect = object->property("vp").value<QObject*>();
4635 QObject *text = rect->findChild<QObject*>("textOne");
4636 QObject *text2 = rect->findChild<QObject*>("textTwo");
4637 QWeakPointer<QObject> rectGuard(rect);
4638 QWeakPointer<QObject> textGuard(text);
4639 QWeakPointer<QObject> text2Guard(text2);
4640 QVERIFY(!rectGuard.isNull());
4641 QVERIFY(!textGuard.isNull());
4642 QVERIFY(!text2Guard.isNull());
4643 QCOMPARE(text->property("textCanary").toInt(), 11);
4644 QCOMPARE(text2->property("textCanary").toInt(), 12);
4645 // now construct an image which we will reparent.
4646 QMetaObject::invokeMethod(text2, "constructQObject");
4647 QObject *image = text2->property("vp").value<QObject*>();
4648 QWeakPointer<QObject> imageGuard(image);
4649 QVERIFY(!imageGuard.isNull());
4650 QCOMPARE(image->property("imageCanary").toInt(), 13);
4651 // now reparent the "Image" object (currently, it has JS ownership)
4652 image->setParent(object); // reparented to base object. after deassignVarProp, the ctxt will be invalid.
4653 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4654 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4655 QCoreApplication::processEvents();
4656 QVERIFY(!imageGuard.isNull()); // should still be alive.
4657 QVERIFY(!image->property("imageCanary").isValid()); // but varProperties won't be available (null context).
4659 QVERIFY(imageGuard.isNull()); // should now be dead.
4662 void tst_qqmlecmascript::propertyVarCircular()
4664 // enforce behaviour regarding circular references - ensure qdvmemo deletion.
4665 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.qml"));
4666 QObject *object = component.create();
4667 QVERIFY(object != 0);
4668 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4669 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4670 QCoreApplication::processEvents();
4671 QCOMPARE(object->property("canaryInt"), QVariant(5));
4672 QVariant canaryResourceVariant = object->property("canaryResource");
4673 QVERIFY(canaryResourceVariant.isValid());
4674 QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
4675 canaryResourceVariant = QVariant(); // invalidate it to remove one copy of the pixmap from memory.
4676 QMetaObject::invokeMethod(object, "deassignCanaryResource"); // remove one copy of the pixmap from memory
4677 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4678 QCoreApplication::processEvents();
4679 QVERIFY(!canaryResourcePixmap.isDetached()); // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
4680 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4681 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4682 QCoreApplication::processEvents();
4683 QCOMPARE(object->property("canaryInt"), QVariant(2));
4684 QCOMPARE(object->property("canaryResource"), QVariant(1));
4685 QVERIFY(canaryResourcePixmap.isDetached()); // now detached, since orig copy was member of qdvmemo which was deleted.
4689 void tst_qqmlecmascript::propertyVarCircular2()
4691 // track deletion of JS-owned parent item with Cpp-owned child
4692 // where the child has a var property referencing its parent.
4693 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4694 QObject *object = component.create();
4695 QVERIFY(object != 0);
4696 QMetaObject::invokeMethod(object, "assignCircular");
4697 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4698 QCoreApplication::processEvents();
4699 QObject *rootObject = object->property("vp").value<QObject*>();
4700 QVERIFY(rootObject != 0);
4701 QObject *childObject = rootObject->findChild<QObject*>("text");
4702 QVERIFY(childObject != 0);
4703 QWeakPointer<QObject> rootObjectTracker(rootObject);
4704 QVERIFY(!rootObjectTracker.isNull());
4705 QWeakPointer<QObject> childObjectTracker(childObject);
4706 QVERIFY(!childObjectTracker.isNull());
4708 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4709 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4710 QMetaObject::invokeMethod(object, "deassignCircular");
4711 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4712 QCoreApplication::processEvents();
4713 QVERIFY(rootObjectTracker.isNull()); // should have been collected
4714 QVERIFY(childObjectTracker.isNull()); // should have been collected
4718 void tst_qqmlecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
4720 *(int*)(parameter) += 1;
4721 qPersistentDispose(object);
4724 void tst_qqmlecmascript::propertyVarInheritance()
4726 int propertyVarWeakRefCallbackCount = 0;
4728 // enforce behaviour regarding element inheritance - ensure handle disposal.
4729 // The particular component under test here has a chain of references.
4730 QQmlComponent component(&engine, testFileUrl("propertyVar.inherit.qml"));
4731 QObject *object = component.create();
4732 QVERIFY(object != 0);
4733 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4734 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4735 QCoreApplication::processEvents();
4736 // we want to be able to track when the varProperties array of the last metaobject is disposed
4737 QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
4738 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*>();
4739 QQmlVMEMetaObject *icovmemo = QQmlVMEMetaObject::get(ico5);
4740 QQmlVMEMetaObject *ccovmemo = QQmlVMEMetaObject::get(cco5);
4741 v8::Persistent<v8::Value> icoCanaryHandle;
4742 v8::Persistent<v8::Value> ccoCanaryHandle;
4745 // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only
4746 // public function which can return us a handle to something in the varProperties array.
4747 icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ")));
4748 ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")));
4749 // we make them weak and invoke the gc, but we should not hit the weak-callback yet
4750 // as the varproperties array of each vmemo still references the resource.
4751 icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4752 ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4754 QVERIFY(propertyVarWeakRefCallbackCount == 0);
4756 // now we deassign the var prop, which should trigger collection of item subtrees.
4757 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4758 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4759 QCoreApplication::processEvents();
4760 // ensure that there are only weak handles to the underlying varProperties array remaining.
4762 QCOMPARE(propertyVarWeakRefCallbackCount, 2); // should have been called for both, since all refs should be weak.
4764 // since there are no parent vmemo's to keep implicit references alive, and the only handles
4765 // to what remains are weak, all varProperties arrays must have been collected.
4768 void tst_qqmlecmascript::propertyVarInheritance2()
4770 int propertyVarWeakRefCallbackCount = 0;
4772 // The particular component under test here does NOT have a chain of references; the
4773 // only link between rootObject and childObject is that rootObject is the parent of childObject.
4774 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4775 QObject *object = component.create();
4776 QVERIFY(object != 0);
4777 QMetaObject::invokeMethod(object, "assignCircular");
4778 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4779 QCoreApplication::processEvents();
4780 QObject *rootObject = object->property("vp").value<QObject*>();
4781 QVERIFY(rootObject != 0);
4782 QObject *childObject = rootObject->findChild<QObject*>("text");
4783 QVERIFY(childObject != 0);
4784 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4785 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4786 v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
4789 propertyVarWeakRefCallbackCount = 0; // reset callback count.
4790 childObjectVarArrayValueHandle = qPersistentNew(QQmlVMEMetaObject::get(childObject)->vmeProperty(childObject->metaObject()->indexOfProperty("vp")));
4791 childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4793 QVERIFY(propertyVarWeakRefCallbackCount == 0); // should not have been collected yet.
4794 QCOMPARE(childObject->property("vp").value<QObject*>(), rootObject);
4795 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4797 QMetaObject::invokeMethod(object, "deassignCircular");
4798 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4799 QCoreApplication::processEvents();
4800 QVERIFY(propertyVarWeakRefCallbackCount == 1); // should have been collected now.
4804 // Ensure that QObject type conversion works on binding assignment
4805 void tst_qqmlecmascript::elementAssign()
4807 QQmlComponent component(&engine, testFileUrl("elementAssign.qml"));
4809 QObject *object = component.create();
4810 QVERIFY(object != 0);
4812 QCOMPARE(object->property("test").toBool(), true);
4818 void tst_qqmlecmascript::objectPassThroughSignals()
4820 QQmlComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml"));
4822 QObject *object = component.create();
4823 QVERIFY(object != 0);
4825 QCOMPARE(object->property("test").toBool(), true);
4831 void tst_qqmlecmascript::objectConversion()
4833 QQmlComponent component(&engine, testFileUrl("objectConversion.qml"));
4835 QObject *object = component.create();
4836 QVERIFY(object != 0);
4838 QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
4839 QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
4846 void tst_qqmlecmascript::booleanConversion()
4848 QQmlComponent component(&engine, testFileUrl("booleanConversion.qml"));
4850 QObject *object = component.create();
4851 QVERIFY(object != 0);
4853 QCOMPARE(object->property("test_true1").toBool(), true);
4854 QCOMPARE(object->property("test_true2").toBool(), true);
4855 QCOMPARE(object->property("test_true3").toBool(), true);
4856 QCOMPARE(object->property("test_true4").toBool(), true);
4857 QCOMPARE(object->property("test_true5").toBool(), true);
4859 QCOMPARE(object->property("test_false1").toBool(), false);
4860 QCOMPARE(object->property("test_false2").toBool(), false);
4861 QCOMPARE(object->property("test_false3").toBool(), false);
4866 void tst_qqmlecmascript::handleReferenceManagement()
4871 // Linear QObject reference
4872 QQmlEngine hrmEngine;
4873 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml"));
4874 QObject *object = component.create();
4875 QVERIFY(object != 0);
4876 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4877 cro->setEngine(&hrmEngine);
4878 cro->setDtorCount(&dtorCount);
4879 QMetaObject::invokeMethod(object, "createReference");
4881 QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
4883 hrmEngine.collectGarbage();
4884 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4885 QCoreApplication::processEvents();
4886 QCOMPARE(dtorCount, 3);
4891 // Circular QObject reference
4892 QQmlEngine hrmEngine;
4893 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml"));
4894 QObject *object = component.create();
4895 QVERIFY(object != 0);
4896 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4897 cro->setEngine(&hrmEngine);
4898 cro->setDtorCount(&dtorCount);
4899 QMetaObject::invokeMethod(object, "circularReference");
4901 QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
4903 hrmEngine.collectGarbage();
4904 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4905 QCoreApplication::processEvents();
4906 QCOMPARE(dtorCount, 3);
4911 // Linear handle reference
4912 QQmlEngine hrmEngine;
4913 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.1.qml"));
4914 QObject *object = component.create();
4915 QVERIFY(object != 0);
4916 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4918 crh->setEngine(&hrmEngine);
4919 crh->setDtorCount(&dtorCount);
4920 QMetaObject::invokeMethod(object, "createReference");
4921 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4922 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4923 QVERIFY(first != 0);
4924 QVERIFY(second != 0);
4925 first->addReference(QQmlData::get(second)->v8object); // create reference
4926 // now we have to reparent second and make second owned by JS.
4927 second->setParent(0);
4928 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
4930 QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
4932 hrmEngine.collectGarbage();
4933 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4934 QCoreApplication::processEvents();
4935 QCOMPARE(dtorCount, 3);
4940 // Circular handle reference
4941 QQmlEngine hrmEngine;
4942 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.2.qml"));
4943 QObject *object = component.create();
4944 QVERIFY(object != 0);
4945 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4947 crh->setEngine(&hrmEngine);
4948 crh->setDtorCount(&dtorCount);
4949 QMetaObject::invokeMethod(object, "circularReference");
4950 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4951 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4952 QVERIFY(first != 0);
4953 QVERIFY(second != 0);
4954 first->addReference(QQmlData::get(second)->v8object); // create circular reference
4955 second->addReference(QQmlData::get(first)->v8object); // note: must be weak.
4956 // now we have to reparent and change ownership, and unset the property references.
4957 first->setParent(0);
4958 second->setParent(0);
4959 QQmlEngine::setObjectOwnership(first, QQmlEngine::JavaScriptOwnership);
4960 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
4961 object->setProperty("first", QVariant::fromValue<QObject*>(0));
4962 object->setProperty("second", QVariant::fromValue<QObject*>(0));
4964 QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
4966 hrmEngine.collectGarbage();
4967 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4968 QCoreApplication::processEvents();
4969 QCOMPARE(dtorCount, 3);
4974 // multiple engine interaction - linear reference
4975 QQmlEngine hrmEngine1;
4976 QQmlEngine hrmEngine2;
4977 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4978 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4979 QObject *object1 = component1.create();
4980 QObject *object2 = component2.create();
4981 QVERIFY(object1 != 0);
4982 QVERIFY(object2 != 0);
4983 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4984 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4987 crh1->setEngine(&hrmEngine1);
4988 crh2->setEngine(&hrmEngine2);
4989 crh1->setDtorCount(&dtorCount);
4990 crh2->setDtorCount(&dtorCount);
4991 QMetaObject::invokeMethod(object1, "createReference");
4992 QMetaObject::invokeMethod(object2, "createReference");
4993 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4994 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4995 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4996 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4997 QVERIFY(first1 != 0);
4998 QVERIFY(second1 != 0);
4999 QVERIFY(first2 != 0);
5000 QVERIFY(second2 != 0);
5001 first1->addReference(QQmlData::get(second2)->v8object); // create reference across engines
5002 // now we have to reparent second2 and make second2 owned by JS.
5003 second2->setParent(0);
5004 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5006 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5007 QCoreApplication::processEvents();
5008 QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
5011 hrmEngine1.collectGarbage();
5012 hrmEngine2.collectGarbage();
5013 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5014 QCoreApplication::processEvents();
5015 QCOMPARE(dtorCount, 6);
5020 // multiple engine interaction - circular reference
5021 QQmlEngine hrmEngine1;
5022 QQmlEngine hrmEngine2;
5023 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
5024 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
5025 QObject *object1 = component1.create();
5026 QObject *object2 = component2.create();
5027 QVERIFY(object1 != 0);
5028 QVERIFY(object2 != 0);
5029 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
5030 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
5033 crh1->setEngine(&hrmEngine1);
5034 crh2->setEngine(&hrmEngine2);
5035 crh1->setDtorCount(&dtorCount);
5036 crh2->setDtorCount(&dtorCount);
5037 QMetaObject::invokeMethod(object1, "createReference");
5038 QMetaObject::invokeMethod(object2, "createReference");
5039 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
5040 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
5041 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
5042 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
5043 QVERIFY(first1 != 0);
5044 QVERIFY(second1 != 0);
5045 QVERIFY(first2 != 0);
5046 QVERIFY(second2 != 0);
5047 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
5048 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
5049 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
5050 first2->addReference(QQmlData::get(first1)->v8object); // close the loop - circular ref across engines
5051 // now we have to reparent and change ownership to JS, and remove property references.
5052 first1->setParent(0);
5053 second1->setParent(0);
5054 first2->setParent(0);
5055 second2->setParent(0);
5056 QQmlEngine::setObjectOwnership(first1, QQmlEngine::JavaScriptOwnership);
5057 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
5058 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
5059 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5060 object1->setProperty("first", QVariant::fromValue<QObject*>(0));
5061 object1->setProperty("second", QVariant::fromValue<QObject*>(0));
5062 object2->setProperty("first", QVariant::fromValue<QObject*>(0));
5063 object2->setProperty("second", QVariant::fromValue<QObject*>(0));
5065 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5066 QCoreApplication::processEvents();
5067 QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
5070 hrmEngine1.collectGarbage();
5071 hrmEngine2.collectGarbage();
5072 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5073 QCoreApplication::processEvents();
5074 QCOMPARE(dtorCount, 6);
5079 // multiple engine interaction - linear reference with engine deletion
5080 QQmlEngine *hrmEngine1 = new QQmlEngine;
5081 QQmlEngine *hrmEngine2 = new QQmlEngine;
5082 QQmlComponent component1(hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
5083 QQmlComponent component2(hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
5084 QObject *object1 = component1.create();
5085 QObject *object2 = component2.create();
5086 QVERIFY(object1 != 0);
5087 QVERIFY(object2 != 0);
5088 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
5089 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
5092 crh1->setEngine(hrmEngine1);
5093 crh2->setEngine(hrmEngine2);
5094 crh1->setDtorCount(&dtorCount);
5095 crh2->setDtorCount(&dtorCount);
5096 QMetaObject::invokeMethod(object1, "createReference");
5097 QMetaObject::invokeMethod(object2, "createReference");
5098 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
5099 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
5100 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
5101 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
5102 QVERIFY(first1 != 0);
5103 QVERIFY(second1 != 0);
5104 QVERIFY(first2 != 0);
5105 QVERIFY(second2 != 0);
5106 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
5107 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
5108 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
5109 // now we have to reparent and change ownership to JS.
5110 first1->setParent(crh1);
5111 second1->setParent(0);
5112 first2->setParent(0);
5113 second2->setParent(0);
5114 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
5115 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
5116 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5119 QCOMPARE(dtorCount, 0);
5120 delete hrmEngine2; // should trigger deletion of objects with JS ownership tracked by this engine
5122 QCOMPARE(dtorCount, 2); // first2 and second2 should have been deleted.
5126 QCOMPARE(dtorCount, 6); // deleting object1 and object2 should trigger deletion of first1 and first2.
5128 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5129 QCoreApplication::processEvents();
5130 QCOMPARE(dtorCount, 6); // all objects should have been cleaned up prior to deleting hrmEngine1.
5134 // Dynamic variant property reference keeps target alive
5135 QQmlEngine hrmEngine;
5136 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.qml"));
5137 QObject *object = component.create();
5138 QVERIFY(object != 0);
5139 QMetaObject::invokeMethod(object, "createReference");
5141 QMetaObject::invokeMethod(object, "ensureReference");
5143 QMetaObject::invokeMethod(object, "removeReference");
5145 QMetaObject::invokeMethod(object, "ensureDeletion");
5146 QCOMPARE(object->property("success").toBool(), true);
5151 // Dynamic Item property reference keeps target alive
5152 QQmlEngine hrmEngine;
5153 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.2.qml"));
5154 QObject *object = component.create();
5155 QVERIFY(object != 0);
5156 QMetaObject::invokeMethod(object, "createReference");
5158 QMetaObject::invokeMethod(object, "ensureReference");
5160 QMetaObject::invokeMethod(object, "removeReference");
5162 QMetaObject::invokeMethod(object, "ensureDeletion");
5163 QCOMPARE(object->property("success").toBool(), true);
5168 // Item property reference to deleted item doesn't crash
5169 QQmlEngine hrmEngine;
5170 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.3.qml"));
5171 QObject *object = component.create();
5172 QVERIFY(object != 0);
5173 QMetaObject::invokeMethod(object, "createReference");
5175 QMetaObject::invokeMethod(object, "ensureReference");
5177 QMetaObject::invokeMethod(object, "manuallyDelete");
5179 QMetaObject::invokeMethod(object, "ensureDeleted");
5180 QCOMPARE(object->property("success").toBool(), true);
5185 void tst_qqmlecmascript::stringArg()
5187 QQmlComponent component(&engine, testFileUrl("stringArg.qml"));
5188 QObject *object = component.create();
5189 QVERIFY(object != 0);
5190 QMetaObject::invokeMethod(object, "success");
5191 QVERIFY(object->property("returnValue").toBool());
5193 QString w1 = testFileUrl("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
5194 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5195 QMetaObject::invokeMethod(object, "failure");
5196 QVERIFY(object->property("returnValue").toBool());
5201 void tst_qqmlecmascript::readonlyDeclaration()
5203 QQmlComponent component(&engine, testFileUrl("readonlyDeclaration.qml"));
5205 QObject *object = component.create();
5206 QVERIFY(object != 0);
5208 QCOMPARE(object->property("test").toBool(), true);
5213 Q_DECLARE_METATYPE(QList<int>)
5214 Q_DECLARE_METATYPE(QList<qreal>)
5215 Q_DECLARE_METATYPE(QList<bool>)
5216 Q_DECLARE_METATYPE(QList<QString>)
5217 Q_DECLARE_METATYPE(QList<QUrl>)
5218 void tst_qqmlecmascript::sequenceConversionRead()
5221 QUrl qmlFile = testFileUrl("sequenceConversion.read.qml");
5222 QQmlComponent component(&engine, qmlFile);
5223 QObject *object = component.create();
5224 QVERIFY(object != 0);
5225 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5228 QMetaObject::invokeMethod(object, "readSequences");
5229 QList<int> intList; intList << 1 << 2 << 3 << 4;
5230 QCOMPARE(object->property("intListLength").toInt(), intList.length());
5231 QCOMPARE(object->property("intList").value<QList<int> >(), intList);
5232 QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
5233 QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
5234 QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
5235 QList<bool> boolList; boolList << true << false << true << false;
5236 QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
5237 QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
5238 QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5239 QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
5240 QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
5241 QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
5242 QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
5243 QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
5244 QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5245 QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
5246 QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
5248 QMetaObject::invokeMethod(object, "readSequenceElements");
5249 QCOMPARE(object->property("intVal").toInt(), 2);
5250 QCOMPARE(object->property("qrealVal").toReal(), 2.2);
5251 QCOMPARE(object->property("boolVal").toBool(), false);
5252 QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
5253 QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
5254 QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
5256 QMetaObject::invokeMethod(object, "enumerateSequenceElements");
5257 QCOMPARE(object->property("enumerationMatches").toBool(), true);
5259 intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
5260 QQmlProperty seqProp(seq, "intListProperty");
5261 QCOMPARE(seqProp.read().value<QList<int> >(), intList);
5262 QQmlProperty seqProp2(seq, "intListProperty", &engine);
5263 QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
5265 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5266 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5272 QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml");
5273 QQmlComponent component(&engine, qmlFile);
5274 QObject *object = component.create();
5275 QVERIFY(object != 0);
5276 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5279 // we haven't registered QList<QPoint> as a sequence type.
5280 QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5281 QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
5282 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5283 QTest::ignoreMessage(QtWarningMsg, warningTwo.toLatin1().constData());
5285 QMetaObject::invokeMethod(object, "performTest");
5287 // QList<QPoint> has not been registered as a sequence type.
5288 QCOMPARE(object->property("pointListLength").toInt(), 0);
5289 QVERIFY(!object->property("pointList").isValid());
5290 QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5291 QQmlProperty seqProp(seq, "pointListProperty", &engine);
5292 QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
5298 void tst_qqmlecmascript::sequenceConversionWrite()
5301 QUrl qmlFile = testFileUrl("sequenceConversion.write.qml");
5302 QQmlComponent component(&engine, qmlFile);
5303 QObject *object = component.create();
5304 QVERIFY(object != 0);
5305 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5308 QMetaObject::invokeMethod(object, "writeSequences");
5309 QCOMPARE(object->property("success").toBool(), true);
5311 QMetaObject::invokeMethod(object, "writeSequenceElements");
5312 QCOMPARE(object->property("success").toBool(), true);
5314 QMetaObject::invokeMethod(object, "writeOtherElements");
5315 QCOMPARE(object->property("success").toBool(), true);
5317 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5318 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5324 QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml");
5325 QQmlComponent component(&engine, qmlFile);
5326 QObject *object = component.create();
5327 QVERIFY(object != 0);
5328 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5331 // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
5332 QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to an unregistered type");
5333 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5335 QMetaObject::invokeMethod(object, "performTest");
5337 QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
5338 QCOMPARE(seq->pointListProperty(), pointList);
5344 void tst_qqmlecmascript::sequenceConversionArray()
5346 // ensure that in JS the returned sequences act just like normal JS Arrays.
5347 QUrl qmlFile = testFileUrl("sequenceConversion.array.qml");
5348 QQmlComponent component(&engine, qmlFile);
5349 QObject *object = component.create();
5350 QVERIFY(object != 0);
5351 QMetaObject::invokeMethod(object, "indexedAccess");
5352 QVERIFY(object->property("success").toBool());
5353 QMetaObject::invokeMethod(object, "arrayOperations");
5354 QVERIFY(object->property("success").toBool());
5355 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5356 QVERIFY(object->property("success").toBool());
5357 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5358 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5363 void tst_qqmlecmascript::sequenceConversionIndexes()
5365 // ensure that we gracefully fail if unsupported index values are specified.
5366 // Qt container classes only support non-negative, signed integer index values.
5367 QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
5368 QQmlComponent component(&engine, qmlFile);
5369 QObject *object = component.create();
5370 QVERIFY(object != 0);
5371 QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
5372 QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
5373 QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
5374 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
5375 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
5376 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
5377 QMetaObject::invokeMethod(object, "indexedAccess");
5378 QVERIFY(object->property("success").toBool());
5382 void tst_qqmlecmascript::sequenceConversionThreads()
5384 // ensure that sequence conversion operations work correctly in a worker thread
5385 // and that serialisation between the main and worker thread succeeds.
5386 QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml");
5387 QQmlComponent component(&engine, qmlFile);
5388 QObject *object = component.create();
5389 QVERIFY(object != 0);
5391 QMetaObject::invokeMethod(object, "testIntSequence");
5392 QTRY_VERIFY(object->property("finished").toBool());
5393 QVERIFY(object->property("success").toBool());
5395 QMetaObject::invokeMethod(object, "testQrealSequence");
5396 QTRY_VERIFY(object->property("finished").toBool());
5397 QVERIFY(object->property("success").toBool());
5399 QMetaObject::invokeMethod(object, "testBoolSequence");
5400 QTRY_VERIFY(object->property("finished").toBool());
5401 QVERIFY(object->property("success").toBool());
5403 QMetaObject::invokeMethod(object, "testStringSequence");
5404 QTRY_VERIFY(object->property("finished").toBool());
5405 QVERIFY(object->property("success").toBool());
5407 QMetaObject::invokeMethod(object, "testQStringSequence");
5408 QTRY_VERIFY(object->property("finished").toBool());
5409 QVERIFY(object->property("success").toBool());
5411 QMetaObject::invokeMethod(object, "testUrlSequence");
5412 QTRY_VERIFY(object->property("finished").toBool());
5413 QVERIFY(object->property("success").toBool());
5415 QMetaObject::invokeMethod(object, "testVariantSequence");
5416 QTRY_VERIFY(object->property("finished").toBool());
5417 QVERIFY(object->property("success").toBool());
5422 void tst_qqmlecmascript::sequenceConversionBindings()
5425 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml");
5426 QQmlComponent component(&engine, qmlFile);
5427 QObject *object = component.create();
5428 QVERIFY(object != 0);
5429 QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
5430 QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
5431 QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
5432 QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
5433 QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
5438 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml");
5439 QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
5440 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5441 QQmlComponent component(&engine, qmlFile);
5442 QObject *object = component.create();
5443 QVERIFY(object != 0);
5448 void tst_qqmlecmascript::sequenceConversionCopy()
5450 QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml");
5451 QQmlComponent component(&engine, qmlFile);
5452 QObject *object = component.create();
5453 QVERIFY(object != 0);
5454 QMetaObject::invokeMethod(object, "testCopySequences");
5455 QCOMPARE(object->property("success").toBool(), true);
5456 QMetaObject::invokeMethod(object, "readSequenceCopyElements");
5457 QCOMPARE(object->property("success").toBool(), true);
5458 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5459 QCOMPARE(object->property("success").toBool(), true);
5463 void tst_qqmlecmascript::assignSequenceTypes()
5465 // test binding array to sequence type property
5467 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml"));
5468 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5469 QVERIFY(object != 0);
5470 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5471 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5472 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5473 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5474 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5475 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5479 // test binding literal to sequence type property
5481 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml"));
5482 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5483 QVERIFY(object != 0);
5484 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5485 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5486 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5487 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5488 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5489 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5493 // test binding single value to sequence type property
5495 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml"));
5496 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5497 QVERIFY(object != 0);
5498 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5499 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5500 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5501 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5505 // test assigning array to sequence type property in js function
5507 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml"));
5508 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5509 QVERIFY(object != 0);
5510 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5511 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5512 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5513 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5514 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5515 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5519 // test assigning literal to sequence type property in js function
5521 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml"));
5522 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5523 QVERIFY(object != 0);
5524 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5525 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5526 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5527 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5528 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5529 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5533 // test assigning single value to sequence type property in js function
5535 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml"));
5536 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5537 QVERIFY(object != 0);
5538 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5539 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5540 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5541 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5545 // test QList<QUrl> literal assignment and binding assignment causes url resolution when required
5547 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml"));
5548 QObject *object = component.create();
5549 QVERIFY(object != 0);
5550 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
5551 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
5552 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
5553 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
5554 MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco5"));
5555 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0);
5556 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5557 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5558 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5559 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5560 QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5565 // Test that assigning a null object works
5566 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
5567 void tst_qqmlecmascript::nullObjectBinding()
5569 QQmlComponent component(&engine, testFileUrl("nullObjectBinding.qml"));
5571 QObject *object = component.create();
5572 QVERIFY(object != 0);
5574 QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
5579 // Test that bindings don't evaluate once the engine has been destroyed
5580 void tst_qqmlecmascript::deletedEngine()
5582 QQmlEngine *engine = new QQmlEngine;
5583 QQmlComponent component(engine, testFileUrl("deletedEngine.qml"));
5585 QObject *object = component.create();
5586 QVERIFY(object != 0);
5588 QCOMPARE(object->property("a").toInt(), 39);
5589 object->setProperty("b", QVariant(9));
5590 QCOMPARE(object->property("a").toInt(), 117);
5594 QCOMPARE(object->property("a").toInt(), 117);
5595 object->setProperty("b", QVariant(10));
5596 QCOMPARE(object->property("a").toInt(), 117);
5601 // Test the crashing part of QTBUG-9705
5602 void tst_qqmlecmascript::libraryScriptAssert()
5604 QQmlComponent component(&engine, testFileUrl("libraryScriptAssert.qml"));
5606 QObject *object = component.create();
5607 QVERIFY(object != 0);
5612 void tst_qqmlecmascript::variantsAssignedUndefined()
5614 QQmlComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml"));
5616 QObject *object = component.create();
5617 QVERIFY(object != 0);
5619 QCOMPARE(object->property("test1").toInt(), 10);
5620 QCOMPARE(object->property("test2").toInt(), 11);
5622 object->setProperty("runTest", true);
5624 QCOMPARE(object->property("test1"), QVariant());
5625 QCOMPARE(object->property("test2"), QVariant());
5631 void tst_qqmlecmascript::qtbug_9792()
5633 QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml"));
5635 QQmlContext *context = new QQmlContext(engine.rootContext());
5637 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
5638 QVERIFY(object != 0);
5640 QTest::ignoreMessage(QtDebugMsg, "Hello world!");
5641 object->basicSignal();
5645 transientErrorsMsgCount = 0;
5646 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5648 object->basicSignal();
5650 qInstallMsgHandler(old);
5652 QCOMPARE(transientErrorsMsgCount, 0);
5657 // Verifies that QQmlGuard<>s used in the vmemetaobject are cleaned correctly
5658 void tst_qqmlecmascript::qtcreatorbug_1289()
5660 QQmlComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml"));
5662 QObject *o = component.create();
5665 QObject *nested = qvariant_cast<QObject *>(o->property("object"));
5666 QVERIFY(nested != 0);
5668 QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
5671 nested = qvariant_cast<QObject *>(o->property("object"));
5672 QVERIFY(nested == 0);
5674 // If the bug is present, the next line will crash
5678 // Test that we shut down without stupid warnings
5679 void tst_qqmlecmascript::noSpuriousWarningsAtShutdown()
5682 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml"));
5684 QObject *o = component.create();
5686 transientErrorsMsgCount = 0;
5687 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5691 qInstallMsgHandler(old);
5693 QCOMPARE(transientErrorsMsgCount, 0);
5698 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml"));
5700 QObject *o = component.create();
5702 transientErrorsMsgCount = 0;
5703 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5707 qInstallMsgHandler(old);
5709 QCOMPARE(transientErrorsMsgCount, 0);
5713 void tst_qqmlecmascript::canAssignNullToQObject()
5716 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml"));
5718 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5721 QVERIFY(o->objectProperty() != 0);
5723 o->setProperty("runTest", true);
5725 QVERIFY(o->objectProperty() == 0);
5731 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml"));
5733 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5736 QVERIFY(o->objectProperty() == 0);
5742 void tst_qqmlecmascript::functionAssignment_fromBinding()
5744 QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml"));
5746 QString url = component.url().toString();
5747 QString w1 = url + ":4:25: Unable to assign a function to a property of any type other than var.";
5748 QString w2 = url + ":5:25: Invalid use of Qt.binding() in a binding declaration.";
5749 QString w3 = url + ":6:21: Invalid use of Qt.binding() in a binding declaration.";
5750 QString w4 = url + ":7:15: Invalid use of Qt.binding() in a binding declaration.";
5751 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5752 QTest::ignoreMessage(QtWarningMsg, w2.toLatin1().constData());
5753 QTest::ignoreMessage(QtWarningMsg, w3.toLatin1().constData());
5754 QTest::ignoreMessage(QtWarningMsg, w4.toLatin1().constData());
5756 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5759 QVERIFY(!o->property("a").isValid());
5764 void tst_qqmlecmascript::functionAssignment_fromJS()
5766 QFETCH(QString, triggerProperty);
5768 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5769 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5771 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5773 QVERIFY(!o->property("a").isValid());
5775 o->setProperty("aNumber", QVariant(5));
5776 o->setProperty(triggerProperty.toUtf8().constData(), true);
5777 QCOMPARE(o->property("a"), QVariant(50));
5779 o->setProperty("aNumber", QVariant(10));
5780 QCOMPARE(o->property("a"), QVariant(100));
5785 void tst_qqmlecmascript::functionAssignment_fromJS_data()
5787 QTest::addColumn<QString>("triggerProperty");
5789 QTest::newRow("assign to property") << "assignToProperty";
5790 QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
5792 QTest::newRow("assign to value type") << "assignToValueType";
5794 QTest::newRow("use 'this'") << "assignWithThis";
5795 QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
5798 void tst_qqmlecmascript::functionAssignmentfromJS_invalid()
5800 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5801 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5803 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5805 QVERIFY(!o->property("a").isValid());
5807 o->setProperty("assignFuncWithoutReturn", true);
5808 QVERIFY(!o->property("a").isValid());
5810 QString url = component.url().toString();
5811 QString warning = url + ":67:17: Unable to assign QString to int";
5812 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5813 o->setProperty("assignWrongType", true);
5815 warning = url + ":71:29: Unable to assign QString to int";
5816 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5817 o->setProperty("assignWrongTypeToValueType", true);
5822 void tst_qqmlecmascript::functionAssignment_afterBinding()
5824 QQmlComponent component(&engine, testFileUrl("functionAssignment.3.qml"));
5826 QString url = component.url().toString();
5827 QString w1 = url + ":16: Error: Cannot assign JavaScript function to int";
5828 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5830 QObject *o = component.create();
5832 QCOMPARE(o->property("t1"), QVariant::fromValue<int>(4)); // should have bound
5833 QCOMPARE(o->property("t2"), QVariant::fromValue<int>(2)); // should not have changed
5838 void tst_qqmlecmascript::eval()
5840 QQmlComponent component(&engine, testFileUrl("eval.qml"));
5842 QObject *o = component.create();
5845 QCOMPARE(o->property("test1").toBool(), true);
5846 QCOMPARE(o->property("test2").toBool(), true);
5847 QCOMPARE(o->property("test3").toBool(), true);
5848 QCOMPARE(o->property("test4").toBool(), true);
5849 QCOMPARE(o->property("test5").toBool(), true);
5854 void tst_qqmlecmascript::function()
5856 QQmlComponent component(&engine, testFileUrl("function.qml"));
5858 QObject *o = component.create();
5861 QCOMPARE(o->property("test1").toBool(), true);
5862 QCOMPARE(o->property("test2").toBool(), true);
5863 QCOMPARE(o->property("test3").toBool(), true);
5868 void tst_qqmlecmascript::functionException()
5870 // QTBUG-24037 - shouldn't crash.
5871 QString errstr = testFileUrl("v8functionException.qml").toString() + QLatin1String(":13: SyntaxError: Unexpected token ILLEGAL");
5872 QTest::ignoreMessage(QtWarningMsg, qPrintable(errstr));
5873 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Exception occurred during compilation of function: dynamicSlot()");
5874 QQmlComponent component(&engine, testFileUrl("v8functionException.qml"));
5875 QObject *o = component.create();
5877 QMetaObject::invokeMethod(o, "dynamicSlot");
5881 // Test the "Qt.include" method
5882 void tst_qqmlecmascript::include()
5884 // Non-library relative include
5886 QQmlComponent component(&engine, testFileUrl("include.qml"));
5887 QObject *o = component.create();
5890 QCOMPARE(o->property("test0").toInt(), 99);
5891 QCOMPARE(o->property("test1").toBool(), true);
5892 QCOMPARE(o->property("test2").toBool(), true);
5893 QCOMPARE(o->property("test2_1").toBool(), true);
5894 QCOMPARE(o->property("test3").toBool(), true);
5895 QCOMPARE(o->property("test3_1").toBool(), true);
5900 // Library relative include
5902 QQmlComponent component(&engine, testFileUrl("include_shared.qml"));
5903 QObject *o = component.create();
5906 QCOMPARE(o->property("test0").toInt(), 99);
5907 QCOMPARE(o->property("test1").toBool(), true);
5908 QCOMPARE(o->property("test2").toBool(), true);
5909 QCOMPARE(o->property("test2_1").toBool(), true);
5910 QCOMPARE(o->property("test3").toBool(), true);
5911 QCOMPARE(o->property("test3_1").toBool(), true);
5918 QQmlComponent component(&engine, testFileUrl("include_callback.qml"));
5919 QObject *o = component.create();
5922 QCOMPARE(o->property("test1").toBool(), true);
5923 QCOMPARE(o->property("test2").toBool(), true);
5924 QCOMPARE(o->property("test3").toBool(), true);
5925 QCOMPARE(o->property("test4").toBool(), true);
5926 QCOMPARE(o->property("test5").toBool(), true);
5927 QCOMPARE(o->property("test6").toBool(), true);
5932 // Including file with ".pragma library"
5934 QQmlComponent component(&engine, testFileUrl("include_pragma.qml"));
5935 QObject *o = component.create();
5937 QCOMPARE(o->property("test1").toInt(), 100);
5944 TestHTTPServer server(8111);
5945 QVERIFY(server.isValid());
5946 server.serveDirectory(dataDirectory());
5948 QQmlComponent component(&engine, testFileUrl("include_remote.qml"));
5949 QObject *o = component.create();
5952 QTRY_VERIFY(o->property("done").toBool() == true);
5953 QTRY_VERIFY(o->property("done2").toBool() == true);
5955 QCOMPARE(o->property("test1").toBool(), true);
5956 QCOMPARE(o->property("test2").toBool(), true);
5957 QCOMPARE(o->property("test3").toBool(), true);
5958 QCOMPARE(o->property("test4").toBool(), true);
5959 QCOMPARE(o->property("test5").toBool(), true);
5961 QCOMPARE(o->property("test6").toBool(), true);
5962 QCOMPARE(o->property("test7").toBool(), true);
5963 QCOMPARE(o->property("test8").toBool(), true);
5964 QCOMPARE(o->property("test9").toBool(), true);
5965 QCOMPARE(o->property("test10").toBool(), true);
5972 TestHTTPServer server(8111);
5973 QVERIFY(server.isValid());
5974 server.serveDirectory(dataDirectory());
5976 QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml"));
5977 QObject *o = component.create();
5980 QTRY_VERIFY(o->property("done").toBool() == true);
5982 QCOMPARE(o->property("test1").toBool(), true);
5983 QCOMPARE(o->property("test2").toBool(), true);
5984 QCOMPARE(o->property("test3").toBool(), true);
5990 void tst_qqmlecmascript::signalHandlers()
5992 QQmlComponent component(&engine, testFileUrl("signalHandlers.qml"));
5993 QObject *o = component.create();
5996 QVERIFY(o->property("count").toInt() == 0);
5997 QMetaObject::invokeMethod(o, "testSignalCall");
5998 QCOMPARE(o->property("count").toInt(), 1);
6000 QMetaObject::invokeMethod(o, "testSignalHandlerCall");
6001 QCOMPARE(o->property("count").toInt(), 1);
6002 QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
6004 QVERIFY(o->property("funcCount").toInt() == 0);
6005 QMetaObject::invokeMethod(o, "testSignalConnection");
6006 QCOMPARE(o->property("funcCount").toInt(), 1);
6008 QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
6009 QCOMPARE(o->property("funcCount").toInt(), 2);
6011 QMetaObject::invokeMethod(o, "testSignalDefined");
6012 QCOMPARE(o->property("definedResult").toBool(), true);
6014 QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
6015 QCOMPARE(o->property("definedHandlerResult").toBool(), true);
6020 void tst_qqmlecmascript::qtbug_10696()
6022 QQmlComponent component(&engine, testFileUrl("qtbug_10696.qml"));
6023 QObject *o = component.create();
6028 void tst_qqmlecmascript::qtbug_11606()
6030 QQmlComponent component(&engine, testFileUrl("qtbug_11606.qml"));
6031 QObject *o = component.create();
6033 QCOMPARE(o->property("test").toBool(), true);
6037 void tst_qqmlecmascript::qtbug_11600()
6039 QQmlComponent component(&engine, testFileUrl("qtbug_11600.qml"));
6040 QObject *o = component.create();
6042 QCOMPARE(o->property("test").toBool(), true);
6046 void tst_qqmlecmascript::qtbug_21864()
6048 QQmlComponent component(&engine, testFileUrl("qtbug_21864.qml"));
6049 QObject *o = component.create();
6051 QCOMPARE(o->property("test").toBool(), true);
6055 void tst_qqmlecmascript::rewriteMultiLineStrings()
6059 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml"));
6060 QObject *o = component.create();
6062 QTRY_COMPARE(o->property("test").toBool(), true);
6067 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings_crlf.1.qml"));
6068 QObject *o = component.create();
6074 void tst_qqmlecmascript::qobjectConnectionListExceptionHandling()
6077 QQmlComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml"));
6078 QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined");
6079 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6080 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6081 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6082 QObject *o = component.create();
6084 QCOMPARE(o->property("test").toBool(), true);
6088 // Reading and writing non-scriptable properties should fail
6089 void tst_qqmlecmascript::nonscriptable()
6091 QQmlComponent component(&engine, testFileUrl("nonscriptable.qml"));
6092 QObject *o = component.create();
6094 QCOMPARE(o->property("readOk").toBool(), true);
6095 QCOMPARE(o->property("writeOk").toBool(), true);
6099 // deleteLater() should not be callable from QML
6100 void tst_qqmlecmascript::deleteLater()
6102 QQmlComponent component(&engine, testFileUrl("deleteLater.qml"));
6103 QObject *o = component.create();
6105 QCOMPARE(o->property("test").toBool(), true);
6109 // objectNameChanged() should be usable from QML
6110 void tst_qqmlecmascript::objectNameChangedSignal()
6112 QQmlComponent component(&engine, testFileUrl("objectNameChangedSignal.qml"));
6113 QObject *o = component.create();
6115 QCOMPARE(o->property("test").toBool(), false);
6116 o->setObjectName("obj");
6117 QCOMPARE(o->property("test").toBool(), true);
6121 // destroyed() should not be usable from QML
6122 void tst_qqmlecmascript::destroyedSignal()
6124 QQmlComponent component(&engine, testFileUrl("destroyedSignal.qml"));
6125 QVERIFY(component.isError());
6127 QString expectedErrorString = component.url().toString() + QLatin1String(":5:5: Cannot assign to non-existent property \"onDestroyed\"");
6128 QCOMPARE(component.errors().at(0).toString(), expectedErrorString);
6131 void tst_qqmlecmascript::in()
6133 QQmlComponent component(&engine, testFileUrl("in.qml"));
6134 QObject *o = component.create();
6136 QCOMPARE(o->property("test1").toBool(), true);
6137 QCOMPARE(o->property("test2").toBool(), true);
6141 void tst_qqmlecmascript::typeOf()
6143 QQmlComponent component(&engine, testFileUrl("typeOf.qml"));
6145 QObject *o = component.create();
6148 QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
6149 QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
6150 QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
6151 QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
6152 QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
6153 QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
6154 QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
6155 QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
6156 QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
6161 void tst_qqmlecmascript::qtbug_24448()
6163 QQmlComponent component(&engine, testFileUrl("qtbug_24448.qml"));
6164 QScopedPointer<QObject> o(component.create());
6166 QVERIFY(o->property("test").toBool());
6169 void tst_qqmlecmascript::sharedAttachedObject()
6171 QQmlComponent component(&engine, testFileUrl("sharedAttachedObject.qml"));
6172 QObject *o = component.create();
6174 QCOMPARE(o->property("test1").toBool(), true);
6175 QCOMPARE(o->property("test2").toBool(), true);
6180 void tst_qqmlecmascript::objectName()
6182 QQmlComponent component(&engine, testFileUrl("objectName.qml"));
6183 QObject *o = component.create();
6186 QCOMPARE(o->property("test1").toString(), QString("hello"));
6187 QCOMPARE(o->property("test2").toString(), QString("ell"));
6189 o->setObjectName("world");
6191 QCOMPARE(o->property("test1").toString(), QString("world"));
6192 QCOMPARE(o->property("test2").toString(), QString("orl"));
6197 void tst_qqmlecmascript::writeRemovesBinding()
6199 QQmlComponent component(&engine, testFileUrl("writeRemovesBinding.qml"));
6200 QObject *o = component.create();
6203 QCOMPARE(o->property("test").toBool(), true);
6208 // Test bindings assigned to alias properties actually assign to the alias' target
6209 void tst_qqmlecmascript::aliasBindingsAssignCorrectly()
6211 QQmlComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml"));
6212 QObject *o = component.create();
6215 QCOMPARE(o->property("test").toBool(), true);
6220 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
6221 void tst_qqmlecmascript::aliasBindingsOverrideTarget()
6224 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml"));
6225 QObject *o = component.create();
6228 QCOMPARE(o->property("test").toBool(), true);
6234 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml"));
6235 QObject *o = component.create();
6238 QCOMPARE(o->property("test").toBool(), true);
6244 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml"));
6245 QObject *o = component.create();
6248 QCOMPARE(o->property("test").toBool(), true);
6254 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
6255 void tst_qqmlecmascript::aliasWritesOverrideBindings()
6258 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml"));
6259 QObject *o = component.create();
6262 QCOMPARE(o->property("test").toBool(), true);
6268 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml"));
6269 QObject *o = component.create();
6272 QCOMPARE(o->property("test").toBool(), true);
6278 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml"));
6279 QObject *o = component.create();
6282 QCOMPARE(o->property("test").toBool(), true);
6288 // Allow an alais to a composite element
6290 void tst_qqmlecmascript::aliasToCompositeElement()
6292 QQmlComponent component(&engine, testFileUrl("aliasToCompositeElement.qml"));
6294 QObject *object = component.create();
6295 QVERIFY(object != 0);
6300 void tst_qqmlecmascript::qtbug_20344()
6302 QQmlComponent component(&engine, testFileUrl("qtbug_20344.qml"));
6304 QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
6305 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6307 QObject *object = component.create();
6308 QVERIFY(object != 0);
6313 void tst_qqmlecmascript::revisionErrors()
6316 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml"));
6317 QString url = component.url().toString();
6319 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6320 QString warning2 = url + ":11: ReferenceError: prop2 is not defined";
6321 QString warning3 = url + ":13: ReferenceError: method2 is not defined";
6323 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6324 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6325 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6326 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6327 QVERIFY(object != 0);
6331 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml"));
6332 QString url = component.url().toString();
6334 // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
6335 // method2, prop2 from MyRevisionedClass not available
6336 // method4, prop4 from MyRevisionedSubclass not available
6337 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6338 QString warning2 = url + ":14: ReferenceError: prop2 is not defined";
6339 QString warning3 = url + ":10: ReferenceError: prop4 is not defined";
6340 QString warning4 = url + ":16: ReferenceError: prop4 is not defined";
6341 QString warning5 = url + ":20: ReferenceError: method2 is not defined";
6343 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6344 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6345 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6346 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
6347 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
6348 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6349 QVERIFY(object != 0);
6353 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml"));
6354 QString url = component.url().toString();
6356 // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
6357 // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
6358 QString warning1 = url + ":30: ReferenceError: methodD is not defined";
6359 QString warning2 = url + ":10: ReferenceError: propD is not defined";
6360 QString warning3 = url + ":20: ReferenceError: propD is not defined";
6361 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6362 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6363 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6364 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6365 QVERIFY(object != 0);
6370 void tst_qqmlecmascript::revision()
6373 QQmlComponent component(&engine, testFileUrl("metaobjectRevision.qml"));
6374 QString url = component.url().toString();
6376 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6377 QVERIFY(object != 0);
6381 QQmlComponent component(&engine, testFileUrl("metaobjectRevision2.qml"));
6382 QString url = component.url().toString();
6384 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6385 QVERIFY(object != 0);
6389 QQmlComponent component(&engine, testFileUrl("metaobjectRevision3.qml"));
6390 QString url = component.url().toString();
6392 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6393 QVERIFY(object != 0);
6396 // Test that non-root classes can resolve revisioned methods
6398 QQmlComponent component(&engine, testFileUrl("metaobjectRevision4.qml"));
6400 QObject *object = component.create();
6401 QVERIFY(object != 0);
6402 QCOMPARE(object->property("test").toReal(), 11.);
6407 void tst_qqmlecmascript::realToInt()
6409 QQmlComponent component(&engine, testFileUrl("realToInt.qml"));
6410 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6411 QVERIFY(object != 0);
6413 QMetaObject::invokeMethod(object, "test1");
6414 QCOMPARE(object->value(), int(4));
6415 QMetaObject::invokeMethod(object, "test2");
6416 QCOMPARE(object->value(), int(8));
6419 void tst_qqmlecmascript::urlProperty()
6422 QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml"));
6423 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6424 QVERIFY(object != 0);
6425 object->setStringProperty("http://qt-project.org");
6426 QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
6427 QCOMPARE(object->intProperty(), 123);
6428 QCOMPARE(object->value(), 1);
6429 QCOMPARE(object->property("result").toBool(), true);
6433 void tst_qqmlecmascript::urlPropertyWithEncoding()
6436 QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml"));
6437 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6438 QVERIFY(object != 0);
6439 object->setStringProperty("http://qt-project.org");
6441 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6442 QCOMPARE(object->urlProperty(), encoded);
6443 QCOMPARE(object->value(), 0); // Interpreting URL as string yields canonicalised version
6444 QCOMPARE(object->property("result").toBool(), true);
6448 void tst_qqmlecmascript::urlListPropertyWithEncoding()
6451 QQmlComponent component(&engine, testFileUrl("urlListProperty.qml"));
6452 QObject *object = component.create();
6453 QVERIFY(object != 0);
6454 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
6455 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
6456 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
6457 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
6458 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0);
6460 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6461 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded));
6462 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded));
6463 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6464 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6469 void tst_qqmlecmascript::dynamicString()
6471 QQmlComponent component(&engine, testFileUrl("dynamicString.qml"));
6472 QObject *object = component.create();
6473 QVERIFY(object != 0);
6474 QCOMPARE(object->property("stringProperty").toString(),
6475 QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
6478 void tst_qqmlecmascript::deleteLaterObjectMethodCall()
6480 QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml"));
6481 QObject *object = component.create();
6482 QVERIFY(object != 0);
6485 void tst_qqmlecmascript::automaticSemicolon()
6487 QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml"));
6488 QObject *object = component.create();
6489 QVERIFY(object != 0);
6492 void tst_qqmlecmascript::unaryExpression()
6494 QQmlComponent component(&engine, testFileUrl("unaryExpression.qml"));
6495 QObject *object = component.create();
6496 QVERIFY(object != 0);
6499 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
6500 void tst_qqmlecmascript::doubleEvaluate()
6502 QQmlComponent component(&engine, testFileUrl("doubleEvaluate.qml"));
6503 QObject *object = component.create();
6504 QVERIFY(object != 0);
6505 WriteCounter *wc = qobject_cast<WriteCounter *>(object);
6507 QCOMPARE(wc->count(), 1);
6509 wc->setProperty("x", 9);
6511 QCOMPARE(wc->count(), 2);
6516 static QStringList messages;
6517 static void captureMsgHandler(QtMsgType, const char *msg)
6519 messages.append(QLatin1String(msg));
6522 void tst_qqmlecmascript::nonNotifyable()
6524 QV4Compiler::enableV4(false);
6525 QQmlComponent component(&engine, testFileUrl("nonNotifyable.qml"));
6526 QV4Compiler::enableV4(true);
6528 QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
6530 QObject *object = component.create();
6531 qInstallMsgHandler(old);
6533 QVERIFY(object != 0);
6535 QString expected1 = QLatin1String("QQmlExpression: Expression ") +
6536 component.url().toString() +
6537 QLatin1String(":5 depends on non-NOTIFYable properties:");
6538 QString expected2 = QLatin1String(" ") +
6539 QLatin1String(object->metaObject()->className()) +
6540 QLatin1String("::value");
6542 QCOMPARE(messages.length(), 2);
6543 QCOMPARE(messages.at(0), expected1);
6544 QCOMPARE(messages.at(1), expected2);
6549 void tst_qqmlecmascript::forInLoop()
6551 QQmlComponent component(&engine, testFileUrl("forInLoop.qml"));
6552 QObject *object = component.create();
6553 QVERIFY(object != 0);
6555 QMetaObject::invokeMethod(object, "listProperty");
6557 QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
6558 QCOMPARE(r.size(), 3);
6559 QCOMPARE(r[0],QLatin1String("0=obj1"));
6560 QCOMPARE(r[1],QLatin1String("1=obj2"));
6561 QCOMPARE(r[2],QLatin1String("2=obj3"));
6563 //TODO: should test for in loop for other objects (such as QObjects) as well.
6568 // An object the binding depends on is deleted while the binding is still running
6569 void tst_qqmlecmascript::deleteWhileBindingRunning()
6571 QQmlComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml"));
6572 QObject *object = component.create();
6573 QVERIFY(object != 0);
6577 void tst_qqmlecmascript::qtbug_22679()
6580 object.setStringProperty(QLatin1String("Please work correctly"));
6581 engine.rootContext()->setContextProperty("contextProp", &object);
6583 QQmlComponent component(&engine, testFileUrl("qtbug_22679.qml"));
6584 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6585 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6587 QObject *o = component.create();
6589 QCOMPARE(warningsSpy.count(), 0);
6593 void tst_qqmlecmascript::qtbug_22843_data()
6595 QTest::addColumn<bool>("library");
6597 QTest::newRow("without .pragma library") << false;
6598 QTest::newRow("with .pragma library") << true;
6601 void tst_qqmlecmascript::qtbug_22843()
6603 QFETCH(bool, library);
6605 QString fileName("qtbug_22843");
6607 fileName += QLatin1String(".library");
6608 fileName += QLatin1String(".qml");
6610 QQmlComponent component(&engine, testFileUrl(fileName));
6611 QString url = component.url().toString();
6612 QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )");
6613 QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'");
6615 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6616 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6617 for (int x = 0; x < 3; ++x) {
6618 warningsSpy.clear();
6619 // For libraries, only the first import attempt should produce a
6620 // SyntaxError warning; subsequent component creation should not
6621 // attempt to reload the script.
6622 bool expectSyntaxError = !library || (x == 0);
6623 if (expectSyntaxError)
6624 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
6625 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
6626 QObject *object = component.create();
6627 QVERIFY(object != 0);
6628 QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
6634 void tst_qqmlecmascript::switchStatement()
6637 QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml"));
6638 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6639 QVERIFY(object != 0);
6641 // `object->value()' is the number of executed statements
6643 object->setStringProperty("A");
6644 QCOMPARE(object->value(), 5);
6646 object->setStringProperty("S");
6647 QCOMPARE(object->value(), 3);
6649 object->setStringProperty("D");
6650 QCOMPARE(object->value(), 3);
6652 object->setStringProperty("F");
6653 QCOMPARE(object->value(), 4);
6655 object->setStringProperty("something else");
6656 QCOMPARE(object->value(), 1);
6660 QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml"));
6661 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6662 QVERIFY(object != 0);
6664 // `object->value()' is the number of executed statements
6666 object->setStringProperty("A");
6667 QCOMPARE(object->value(), 5);
6669 object->setStringProperty("S");
6670 QCOMPARE(object->value(), 3);
6672 object->setStringProperty("D");
6673 QCOMPARE(object->value(), 3);
6675 object->setStringProperty("F");
6676 QCOMPARE(object->value(), 3);
6678 object->setStringProperty("something else");
6679 QCOMPARE(object->value(), 4);
6683 QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml"));
6684 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6685 QVERIFY(object != 0);
6687 // `object->value()' is the number of executed statements
6689 object->setStringProperty("A");
6690 QCOMPARE(object->value(), 5);
6692 object->setStringProperty("S");
6693 QCOMPARE(object->value(), 3);
6695 object->setStringProperty("D");
6696 QCOMPARE(object->value(), 3);
6698 object->setStringProperty("F");
6699 QCOMPARE(object->value(), 3);
6701 object->setStringProperty("something else");
6702 QCOMPARE(object->value(), 6);
6706 QQmlComponent component(&engine, testFileUrl("switchStatement.4.qml"));
6708 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
6709 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6711 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6712 QVERIFY(object != 0);
6714 // `object->value()' is the number of executed statements
6716 object->setStringProperty("A");
6717 QCOMPARE(object->value(), 5);
6719 object->setStringProperty("S");
6720 QCOMPARE(object->value(), 3);
6722 object->setStringProperty("D");
6723 QCOMPARE(object->value(), 3);
6725 object->setStringProperty("F");
6726 QCOMPARE(object->value(), 3);
6728 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6730 object->setStringProperty("something else");
6734 QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml"));
6735 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6736 QVERIFY(object != 0);
6738 // `object->value()' is the number of executed statements
6740 object->setStringProperty("A");
6741 QCOMPARE(object->value(), 1);
6743 object->setStringProperty("S");
6744 QCOMPARE(object->value(), 1);
6746 object->setStringProperty("D");
6747 QCOMPARE(object->value(), 1);
6749 object->setStringProperty("F");
6750 QCOMPARE(object->value(), 1);
6752 object->setStringProperty("something else");
6753 QCOMPARE(object->value(), 1);
6757 QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml"));
6758 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6759 QVERIFY(object != 0);
6761 // `object->value()' is the number of executed statements
6763 object->setStringProperty("A");
6764 QCOMPARE(object->value(), 123);
6766 object->setStringProperty("S");
6767 QCOMPARE(object->value(), 123);
6769 object->setStringProperty("D");
6770 QCOMPARE(object->value(), 321);
6772 object->setStringProperty("F");
6773 QCOMPARE(object->value(), 321);
6775 object->setStringProperty("something else");
6776 QCOMPARE(object->value(), 0);
6780 void tst_qqmlecmascript::withStatement()
6783 QQmlComponent component(&engine, testFileUrl("withStatement.1.qml"));
6784 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6785 QVERIFY(object != 0);
6787 QCOMPARE(object->value(), 123);
6791 void tst_qqmlecmascript::tryStatement()
6794 QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml"));
6795 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6796 QVERIFY(object != 0);
6798 QCOMPARE(object->value(), 123);
6802 QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml"));
6803 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6804 QVERIFY(object != 0);
6806 QCOMPARE(object->value(), 321);
6810 QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml"));
6811 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6812 QVERIFY(object != 0);
6814 QCOMPARE(object->value(), 1);
6818 QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml"));
6819 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6820 QVERIFY(object != 0);
6822 QCOMPARE(object->value(), 1);
6826 class CppInvokableWithQObjectDerived : public QObject
6830 CppInvokableWithQObjectDerived() {}
6831 ~CppInvokableWithQObjectDerived() {}
6833 Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data)
6835 MyQmlObject *obj = new MyQmlObject();
6836 obj->setStringProperty(data);
6840 Q_INVOKABLE QString getStringProperty(MyQmlObject *obj)
6842 return obj->stringProperty();
6846 void tst_qqmlecmascript::invokableWithQObjectDerived()
6848 CppInvokableWithQObjectDerived invokable;
6852 engine.rootContext()->setContextProperty("invokable", &invokable);
6854 QQmlComponent component(&engine, testFileUrl("qobjectDerivedArgument.qml"));
6856 QObject *object = component.create();
6858 QVERIFY(object != 0);
6859 QVERIFY(object->property("result").value<bool>() == true);
6865 void tst_qqmlecmascript::realTypePrecision()
6867 // Properties and signal parameters of type real should have double precision.
6868 QQmlComponent component(&engine, testFileUrl("realTypePrecision.qml"));
6869 QScopedPointer<QObject> object(component.create());
6870 QVERIFY(object != 0);
6871 QCOMPARE(object->property("test").toDouble(), 1234567890.);
6872 QCOMPARE(object->property("test2").toDouble(), 1234567890.);
6873 QCOMPARE(object->property("test3").toDouble(), 1234567890.);
6874 QCOMPARE(object->property("test4").toDouble(), 1234567890.);
6875 QCOMPARE(object->property("test5").toDouble(), 1234567890.);
6876 QCOMPARE(object->property("test6").toDouble(), 1234567890.*2);
6879 void tst_qqmlecmascript::registeredFlagMethod()
6882 QQmlComponent component(&engine, testFileUrl("registeredFlagMethod.qml"));
6883 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6884 QVERIFY(object != 0);
6886 QCOMPARE(object->buttons(), 0);
6887 emit object->basicSignal();
6888 QCOMPARE(object->buttons(), Qt::RightButton);
6894 void tst_qqmlecmascript::replaceBinding()
6897 QQmlComponent c(&engine, testFileUrl("replaceBinding.qml"));
6898 QObject *obj = c.create();
6901 QVERIFY(obj->property("success").toBool());
6905 void tst_qqmlecmascript::deleteRootObjectInCreation()
6909 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.qml"));
6910 QObject *obj = c.create();
6912 QVERIFY(obj->property("rootIndestructible").toBool());
6913 QVERIFY(!obj->property("childDestructible").toBool());
6915 QVERIFY(obj->property("childDestructible").toBool());
6920 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.2.qml"));
6921 QObject *object = c.create();
6922 QVERIFY(object != 0);
6923 QVERIFY(object->property("testConditionsMet").toBool());
6928 void tst_qqmlecmascript::onDestruction()
6931 // Delete object manually to invoke the associated handlers,
6932 // prior to engine destruction.
6934 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
6935 QObject *obj = c.create();
6941 // In this case, the teardown of the engine causes deletion
6942 // of contexts and child items. This triggers the
6943 // onDestruction handler of a (previously .destroy()ed)
6944 // component instance. This shouldn't crash.
6946 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
6947 QObject *obj = c.create();
6952 struct EventProcessor : public QObject
6956 Q_INVOKABLE void process()
6958 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
6959 QCoreApplication::processEvents();
6963 void tst_qqmlecmascript::bindingSuppression()
6966 EventProcessor processor;
6967 engine.rootContext()->setContextProperty("pendingEvents", &processor);
6969 transientErrorsMsgCount = 0;
6970 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
6972 QQmlComponent c(&engine, testFileUrl("bindingSuppression.qml"));
6973 QObject *obj = c.create();
6977 qInstallMsgHandler(old);
6978 QCOMPARE(transientErrorsMsgCount, 0);
6981 void tst_qqmlecmascript::signalEmitted()
6984 // calling destroy on the sibling.
6986 QQmlComponent c(&engine, testFileUrl("signalEmitted.2.qml"));
6987 QObject *obj = c.create();
6989 QTRY_VERIFY(obj->property("success").toBool());
6994 // allowing gc to clean up the sibling.
6996 QQmlComponent c(&engine, testFileUrl("signalEmitted.3.qml"));
6997 QObject *obj = c.create();
6999 gc(engine); // should collect c1.
7000 QTRY_VERIFY(obj->property("success").toBool());
7005 // allowing gc to clean up the sibling after manually destroying target.
7007 QQmlComponent c(&engine, testFileUrl("signalEmitted.4.qml"));
7008 QObject *obj = c.create();
7010 gc(engine); // should collect c1.
7011 QMetaObject::invokeMethod(obj, "destroyC2");
7012 QTRY_VERIFY(obj->property("success").toBool()); // handles events (incl. delete later).
7018 void tst_qqmlecmascript::threadSignal()
7021 QQmlComponent c(&engine, testFileUrl("threadSignal.qml"));
7022 QObject *object = c.create();
7023 QVERIFY(object != 0);
7024 QTRY_VERIFY(object->property("passed").toBool());
7028 QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml"));
7029 QObject *object = c.create();
7030 QVERIFY(object != 0);
7031 QSignalSpy doneSpy(object, SIGNAL(done(const QString &)));
7032 QMetaObject::invokeMethod(object, "doIt");
7033 QTRY_VERIFY(object->property("passed").toBool());
7034 QCOMPARE(doneSpy.count(), 1);
7039 // ensure that the qqmldata::destroyed() handler doesn't cause problems
7040 void tst_qqmlecmascript::qqmldataDestroyed()
7042 // gc cleans up a qobject, later the qqmldata destroyed handler will run.
7044 QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.qml"));
7045 QObject *object = c.create();
7046 QVERIFY(object != 0);
7047 // now gc causing the collection of the dynamically constructed object.
7048 engine.collectGarbage();
7049 engine.collectGarbage();
7050 // now process events to allow deletion (calling qqmldata::destroyed())
7051 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
7052 QCoreApplication::processEvents();
7057 // in this case, the object has CPP ownership, and the gc will
7058 // be triggered during its beginCreate stage.
7060 QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.2.qml"));
7061 QObject *object = c.create();
7062 QVERIFY(object != 0);
7063 QVERIFY(object->property("testConditionsMet").toBool());
7064 // the gc() within the handler will have triggered the weak
7065 // qobject reference callback. If that incorrectly disposes
7066 // the handle, when the qqmldata::destroyed() handler is
7067 // called due to object deletion we will see a crash.
7069 // shouldn't have crashed.
7073 void tst_qqmlecmascript::secondAlias()
7075 QQmlComponent c(&engine, testFileUrl("secondAlias.qml"));
7076 QObject *object = c.create();
7077 QVERIFY(object != 0);
7078 QCOMPARE(object->property("test").toInt(), 200);
7082 // An alias to a var property works
7083 void tst_qqmlecmascript::varAlias()
7085 QQmlComponent c(&engine, testFileUrl("varAlias.qml"));
7086 QObject *object = c.create();
7087 QVERIFY(object != 0);
7088 QCOMPARE(object->property("test").toInt(), 192);
7092 // Used to trigger an assert in the lazy meta object creation stage
7093 void tst_qqmlecmascript::overrideDataAssert()
7095 QQmlComponent c(&engine, testFileUrl("overrideDataAssert.qml"));
7096 QObject *object = c.create();
7097 QVERIFY(object != 0);
7098 object->metaObject();
7102 void tst_qqmlecmascript::fallbackBindings_data()
7104 QTest::addColumn<QString>("source");
7106 QTest::newRow("Property without fallback") << "fallbackBindings.1.qml";
7107 QTest::newRow("Property fallback") << "fallbackBindings.2.qml";
7108 QTest::newRow("ModuleAPI without fallback") << "fallbackBindings.3.qml";
7109 QTest::newRow("ModuleAPI fallback") << "fallbackBindings.4.qml";
7110 QTest::newRow("Attached without fallback") << "fallbackBindings.5.qml";
7111 QTest::newRow("Attached fallback") << "fallbackBindings.6.qml";
7114 void tst_qqmlecmascript::fallbackBindings()
7116 QFETCH(QString, source);
7118 QQmlComponent component(&engine, testFileUrl(source));
7119 QScopedPointer<QObject> object(component.create());
7120 QVERIFY(object != 0);
7122 QCOMPARE(object->property("success").toBool(), true);
7125 QTEST_MAIN(tst_qqmlecmascript)
7127 #include "tst_qqmlecmascript.moc"