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 singletonType_data();
154 void singletonType();
155 void singletonTypeImportOrder();
156 void singletonTypeResolution();
157 void singletonTypeConflicts1();
158 void singletonTypeConflicts2();
159 void singletonTypeConflicts3();
160 void importScripts_data();
161 void importScripts();
162 void scarceResources();
163 void scarceResources_data();
164 void scarceResources_other();
165 void propertyChangeSlots();
166 void propertyVar_data();
168 void propertyQJSValue_data();
169 void propertyQJSValue();
170 void propertyVarCpp();
171 void propertyVarOwnership();
172 void propertyVarImplicitOwnership();
173 void propertyVarReparent();
174 void propertyVarReparentNullContext();
175 void propertyVarCircular();
176 void propertyVarCircular2();
177 void propertyVarInheritance();
178 void propertyVarInheritance2();
179 void elementAssign();
180 void objectPassThroughSignals();
181 void objectConversion();
182 void booleanConversion();
183 void handleReferenceManagement();
185 void readonlyDeclaration();
186 void sequenceConversionRead();
187 void sequenceConversionWrite();
188 void sequenceConversionArray();
189 void sequenceConversionIndexes();
190 void sequenceConversionThreads();
191 void sequenceConversionBindings();
192 void sequenceConversionCopy();
193 void assignSequenceTypes();
194 void sequenceSort_data();
198 void singleV8BindingDestroyedDuringEvaluation();
200 #ifndef QT_NO_WIDGETS
203 void dynamicCreationCrash();
204 void dynamicCreationOwnership();
206 void nullObjectBinding();
207 void deletedEngine();
208 void libraryScriptAssert();
209 void variantsAssignedUndefined();
211 void qtcreatorbug_1289();
212 void noSpuriousWarningsAtShutdown();
213 void canAssignNullToQObject();
214 void functionAssignment_fromBinding();
215 void functionAssignment_fromJS();
216 void functionAssignment_fromJS_data();
217 void functionAssignmentfromJS_invalid();
218 void functionAssignment_afterBinding();
221 void functionException();
226 void qobjectConnectionListExceptionHandling();
227 void nonscriptable();
229 void objectNameChangedSignal();
230 void destroyedSignal();
234 void sharedAttachedObject();
236 void writeRemovesBinding();
237 void aliasBindingsAssignCorrectly();
238 void aliasBindingsOverrideTarget();
239 void aliasWritesOverrideBindings();
240 void aliasToCompositeElement();
243 void urlPropertyWithEncoding();
244 void urlListPropertyWithEncoding();
245 void dynamicString();
247 void signalHandlers();
248 void doubleEvaluate();
250 void nonNotifyable();
251 void deleteWhileBindingRunning();
252 void callQtInvokables();
253 void invokableObjectArg();
254 void invokableObjectRet();
255 void invokableEnumRet();
258 void qtbug_22843_data();
260 void rewriteMultiLineStrings();
261 void revisionErrors();
263 void invokableWithQObjectDerived();
264 void realTypePrecision();
265 void registeredFlagMethod();
266 void deleteLaterObjectMethodCall();
267 void automaticSemicolon();
268 void unaryExpression();
269 void switchStatement();
270 void withStatement();
272 void replaceBinding();
273 void deleteRootObjectInCreation();
274 void onDestruction();
275 void bindingSuppression();
276 void signalEmitted();
278 void qqmldataDestroyed();
281 void overrideDataAssert();
282 void fallbackBindings_data();
283 void fallbackBindings();
286 static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
290 void tst_qqmlecmascript::initTestCase()
292 QQmlDataTest::initTestCase();
295 QString dataDir(dataDirectory() + QLatin1Char('/') + QLatin1String("lib"));
296 engine.addImportPath(dataDir);
299 void tst_qqmlecmascript::assignBasicTypes()
302 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.qml"));
303 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
304 QVERIFY(object != 0);
305 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
306 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
307 QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue);
308 QCOMPARE(object->stringProperty(), QString("Hello World!"));
309 QCOMPARE(object->uintProperty(), uint(10));
310 QCOMPARE(object->intProperty(), -19);
311 QCOMPARE((float)object->realProperty(), float(23.2));
312 QCOMPARE((float)object->doubleProperty(), float(-19.75));
313 QCOMPARE((float)object->floatProperty(), float(8.5));
314 QCOMPARE(object->colorProperty(), QColor("red"));
315 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
316 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
317 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
318 QCOMPARE(object->pointProperty(), QPoint(99,13));
319 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
320 QCOMPARE(object->sizeProperty(), QSize(99, 13));
321 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
322 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
323 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
324 QCOMPARE(object->boolProperty(), true);
325 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
326 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
327 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
331 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.2.qml"));
332 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
333 QVERIFY(object != 0);
334 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
335 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
336 QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue);
337 QCOMPARE(object->stringProperty(), QString("Hello World!"));
338 QCOMPARE(object->uintProperty(), uint(10));
339 QCOMPARE(object->intProperty(), -19);
340 QCOMPARE((float)object->realProperty(), float(23.2));
341 QCOMPARE((float)object->doubleProperty(), float(-19.75));
342 QCOMPARE((float)object->floatProperty(), float(8.5));
343 QCOMPARE(object->colorProperty(), QColor("red"));
344 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
345 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
346 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
347 QCOMPARE(object->pointProperty(), QPoint(99,13));
348 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
349 QCOMPARE(object->sizeProperty(), QSize(99, 13));
350 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
351 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
352 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
353 QCOMPARE(object->boolProperty(), true);
354 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
355 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
356 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
361 void tst_qqmlecmascript::assignDate_data()
363 QTest::addColumn<QUrl>("source");
365 QTest::newRow("Component.onComplete JS Parse") << testFileUrl("assignDate.qml");
366 QTest::newRow("Component.onComplete JS") << testFileUrl("assignDate.1.qml");
367 QTest::newRow("Binding JS") << testFileUrl("assignDate.2.qml");
368 QTest::newRow("Binding UTC") << testFileUrl("assignDate.3.qml");
369 QTest::newRow("Binding JS UTC") << testFileUrl("assignDate.4.qml");
370 QTest::newRow("Binding UTC+2") << testFileUrl("assignDate.5.qml");
371 QTest::newRow("Binding JS UTC+2 ") << testFileUrl("assignDate.6.qml");
374 void tst_qqmlecmascript::assignDate()
376 QFETCH(QUrl, source);
378 QQmlComponent component(&engine, source);
379 QScopedPointer<QObject> obj(component.create());
380 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
381 QVERIFY(object != 0);
383 // Dates received from JS are automatically converted to local time
384 QDate expectedDate(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 0), Qt::UTC).toLocalTime().date());
385 QDateTime expectedDateTime(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 1), Qt::UTC).toLocalTime());
386 QDateTime expectedDateTime2(QDateTime(QDate(2009, 5, 12), QTime(23, 59, 59), Qt::UTC).toLocalTime());
388 QCOMPARE(object->dateProperty(), expectedDate);
389 QCOMPARE(object->dateTimeProperty(), expectedDateTime);
390 QCOMPARE(object->dateTimeProperty2(), expectedDateTime2);
391 QCOMPARE(object->boolProperty(), true);
394 void tst_qqmlecmascript::exportDate_data()
396 QTest::addColumn<QUrl>("source");
397 QTest::addColumn<QDateTime>("datetime");
399 // Verify that we can export datetime information to QML and that consumers can access
400 // the data correctly provided they know the TZ info associated with the value
402 const QDate date(2009, 5, 12);
403 const QTime early(0, 0, 1);
404 const QTime late(23, 59, 59);
405 const int offset(((11 * 60) + 30) * 60);
407 QTest::newRow("Localtime early") << testFileUrl("exportDate.qml") << QDateTime(date, early, Qt::LocalTime);
408 QTest::newRow("Localtime late") << testFileUrl("exportDate.2.qml") << QDateTime(date, late, Qt::LocalTime);
409 QTest::newRow("UTC early") << testFileUrl("exportDate.3.qml") << QDateTime(date, early, Qt::UTC);
410 QTest::newRow("UTC late") << testFileUrl("exportDate.4.qml") << QDateTime(date, late, Qt::UTC);
412 QDateTime dt(date, early, Qt::OffsetFromUTC);
413 dt.setUtcOffset(offset);
414 QTest::newRow("+11:30 early") << testFileUrl("exportDate.5.qml") << dt;
417 QDateTime dt(date, late, Qt::OffsetFromUTC);
418 dt.setUtcOffset(offset);
419 QTest::newRow("+11:30 late") << testFileUrl("exportDate.6.qml") << dt;
422 QDateTime dt(date, early, Qt::OffsetFromUTC);
423 dt.setUtcOffset(-offset);
424 QTest::newRow("-11:30 early") << testFileUrl("exportDate.7.qml") << dt;
427 QDateTime dt(date, late, Qt::OffsetFromUTC);
428 dt.setUtcOffset(-offset);
429 QTest::newRow("-11:30 late") << testFileUrl("exportDate.8.qml") << dt;
433 void tst_qqmlecmascript::exportDate()
435 QFETCH(QUrl, source);
436 QFETCH(QDateTime, datetime);
438 DateTimeExporter exporter(datetime);
441 e.rootContext()->setContextProperty("datetimeExporter", &exporter);
443 QQmlComponent component(&e, source);
444 QScopedPointer<QObject> obj(component.create());
445 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
446 QVERIFY(object != 0);
447 QCOMPARE(object->boolProperty(), true);
450 void tst_qqmlecmascript::idShortcutInvalidates()
453 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.qml"));
454 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
455 QVERIFY(object != 0);
456 QVERIFY(object->objectProperty() != 0);
457 delete object->objectProperty();
458 QVERIFY(object->objectProperty() == 0);
463 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.1.qml"));
464 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
465 QVERIFY(object != 0);
466 QVERIFY(object->objectProperty() != 0);
467 delete object->objectProperty();
468 QVERIFY(object->objectProperty() == 0);
473 void tst_qqmlecmascript::boolPropertiesEvaluateAsBool()
476 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.1.qml"));
477 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
478 QVERIFY(object != 0);
479 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
483 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.2.qml"));
484 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
485 QVERIFY(object != 0);
486 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
491 void tst_qqmlecmascript::signalAssignment()
494 QQmlComponent component(&engine, testFileUrl("signalAssignment.1.qml"));
495 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
496 QVERIFY(object != 0);
497 QCOMPARE(object->string(), QString());
498 emit object->basicSignal();
499 QCOMPARE(object->string(), QString("pass"));
504 QQmlComponent component(&engine, testFileUrl("signalAssignment.2.qml"));
505 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
506 QVERIFY(object != 0);
507 QCOMPARE(object->string(), QString());
508 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
509 QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
514 QQmlComponent component(&engine, testFileUrl("signalAssignment.3.qml"));
515 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
516 QVERIFY(object != 0);
517 QCOMPARE(object->string(), QString());
518 emit object->unnamedArgumentSignal(19, 10.25, "Hello world!");
519 QCOMPARE(object->string(), QString("pass 19 Hello world!"));
524 QQmlComponent component(&engine, testFileUrl("signalAssignment.4.qml"));
525 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
526 QVERIFY(object != 0);
527 QCOMPARE(object->string(), QString());
528 emit object->signalWithGlobalName(19);
529 QCOMPARE(object->string(), QString("pass 5"));
534 void tst_qqmlecmascript::methods()
537 QQmlComponent component(&engine, testFileUrl("methods.1.qml"));
538 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
539 QVERIFY(object != 0);
540 QCOMPARE(object->methodCalled(), false);
541 QCOMPARE(object->methodIntCalled(), false);
542 emit object->basicSignal();
543 QCOMPARE(object->methodCalled(), true);
544 QCOMPARE(object->methodIntCalled(), false);
549 QQmlComponent component(&engine, testFileUrl("methods.2.qml"));
550 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
551 QVERIFY(object != 0);
552 QCOMPARE(object->methodCalled(), false);
553 QCOMPARE(object->methodIntCalled(), false);
554 emit object->basicSignal();
555 QCOMPARE(object->methodCalled(), false);
556 QCOMPARE(object->methodIntCalled(), true);
561 QQmlComponent component(&engine, testFileUrl("methods.3.qml"));
562 QObject *object = component.create();
563 QVERIFY(object != 0);
564 QCOMPARE(object->property("test").toInt(), 19);
569 QQmlComponent component(&engine, testFileUrl("methods.4.qml"));
570 QObject *object = component.create();
571 QVERIFY(object != 0);
572 QCOMPARE(object->property("test").toInt(), 19);
573 QCOMPARE(object->property("test2").toInt(), 17);
574 QCOMPARE(object->property("test3").toInt(), 16);
579 QQmlComponent component(&engine, testFileUrl("methods.5.qml"));
580 QObject *object = component.create();
581 QVERIFY(object != 0);
582 QCOMPARE(object->property("test").toInt(), 9);
587 void tst_qqmlecmascript::bindingLoop()
589 QQmlComponent component(&engine, testFileUrl("bindingLoop.qml"));
590 QString warning = component.url().toString() + ":5:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\"";
591 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
592 QObject *object = component.create();
593 QVERIFY(object != 0);
597 void tst_qqmlecmascript::basicExpressions_data()
599 QTest::addColumn<QString>("expression");
600 QTest::addColumn<QVariant>("result");
601 QTest::addColumn<bool>("nest");
603 QTest::newRow("Syntax error (self test)") << "{console.log({'a':1'}.a)}" << QVariant() << false;
604 QTest::newRow("Context property") << "a" << QVariant(1944) << false;
605 QTest::newRow("Context property") << "a" << QVariant(1944) << true;
606 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << false;
607 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << true;
608 QTest::newRow("Overridden context property") << "b" << QVariant("Milk") << false;
609 QTest::newRow("Overridden context property") << "b" << QVariant("Cow") << true;
610 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << false;
611 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << true;
612 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object2") << false;
613 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object3") << true;
614 QTest::newRow("Default object property") << "horseLegs" << QVariant(4) << false;
615 QTest::newRow("Default object property") << "antLegs" << QVariant(6) << false;
616 QTest::newRow("Default object property") << "emuLegs" << QVariant(2) << false;
617 QTest::newRow("Nested default object property") << "horseLegs" << QVariant(4) << true;
618 QTest::newRow("Nested default object property") << "antLegs" << QVariant(7) << true;
619 QTest::newRow("Nested default object property") << "emuLegs" << QVariant(2) << true;
620 QTest::newRow("Nested default object property") << "humanLegs" << QVariant(2) << true;
621 QTest::newRow("Context property override default object property") << "millipedeLegs" << QVariant(100) << true;
624 void tst_qqmlecmascript::basicExpressions()
626 QFETCH(QString, expression);
627 QFETCH(QVariant, result);
633 MyDefaultObject1 default1;
634 MyDefaultObject3 default3;
635 object1.setStringProperty("Object1");
636 object2.setStringProperty("Object2");
637 object3.setStringProperty("Object3");
639 QQmlContext context(engine.rootContext());
640 QQmlContext nestedContext(&context);
642 context.setContextObject(&default1);
643 context.setContextProperty("a", QVariant(1944));
644 context.setContextProperty("b", QVariant("Milk"));
645 context.setContextProperty("object", &object1);
646 context.setContextProperty("objectOverride", &object2);
647 nestedContext.setContextObject(&default3);
648 nestedContext.setContextProperty("b", QVariant("Cow"));
649 nestedContext.setContextProperty("objectOverride", &object3);
650 nestedContext.setContextProperty("millipedeLegs", QVariant(100));
652 MyExpression expr(nest?&nestedContext:&context, expression);
653 QCOMPARE(expr.evaluate(), result);
656 void tst_qqmlecmascript::arrayExpressions()
662 QQmlContext context(engine.rootContext());
663 context.setContextProperty("a", &obj1);
664 context.setContextProperty("b", &obj2);
665 context.setContextProperty("c", &obj3);
667 MyExpression expr(&context, "[a, b, c, 10]");
668 QVariant result = expr.evaluate();
669 QCOMPARE(result.userType(), qMetaTypeId<QVariantList>());
670 QVariantList list = qvariant_cast<QVariantList>(result);
671 QCOMPARE(list.count(), 4);
672 QCOMPARE(list.at(0).value<QObject*>(), &obj1);
673 QCOMPARE(list.at(1).value<QObject*>(), &obj2);
674 QCOMPARE(list.at(2).value<QObject*>(), &obj3);
675 QCOMPARE(list.at(3).value<int>(), 10);
678 // Tests that modifying a context property will reevaluate expressions
679 void tst_qqmlecmascript::contextPropertiesTriggerReeval()
681 QQmlContext context(engine.rootContext());
684 MyQmlObject *object3 = new MyQmlObject;
686 object1.setStringProperty("Hello");
687 object2.setStringProperty("World");
689 context.setContextProperty("testProp", QVariant(1));
690 context.setContextProperty("testObj", &object1);
691 context.setContextProperty("testObj2", object3);
694 MyExpression expr(&context, "testProp + 1");
695 QCOMPARE(expr.changed, false);
696 QCOMPARE(expr.evaluate(), QVariant(2));
698 context.setContextProperty("testProp", QVariant(2));
699 QCOMPARE(expr.changed, true);
700 QCOMPARE(expr.evaluate(), QVariant(3));
704 MyExpression expr(&context, "testProp + testProp + testProp");
705 QCOMPARE(expr.changed, false);
706 QCOMPARE(expr.evaluate(), QVariant(6));
708 context.setContextProperty("testProp", QVariant(4));
709 QCOMPARE(expr.changed, true);
710 QCOMPARE(expr.evaluate(), QVariant(12));
714 MyExpression expr(&context, "testObj.stringProperty");
715 QCOMPARE(expr.changed, false);
716 QCOMPARE(expr.evaluate(), QVariant("Hello"));
718 context.setContextProperty("testObj", &object2);
719 QCOMPARE(expr.changed, true);
720 QCOMPARE(expr.evaluate(), QVariant("World"));
724 MyExpression expr(&context, "testObj.stringProperty /**/");
725 QCOMPARE(expr.changed, false);
726 QCOMPARE(expr.evaluate(), QVariant("World"));
728 context.setContextProperty("testObj", &object1);
729 QCOMPARE(expr.changed, true);
730 QCOMPARE(expr.evaluate(), QVariant("Hello"));
734 MyExpression expr(&context, "testObj2");
735 QCOMPARE(expr.changed, false);
736 QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3));
742 void tst_qqmlecmascript::objectPropertiesTriggerReeval()
744 QQmlContext context(engine.rootContext());
748 context.setContextProperty("testObj", &object1);
750 object1.setStringProperty(QLatin1String("Hello"));
751 object2.setStringProperty(QLatin1String("Dog"));
752 object3.setStringProperty(QLatin1String("Cat"));
755 MyExpression expr(&context, "testObj.stringProperty");
756 QCOMPARE(expr.changed, false);
757 QCOMPARE(expr.evaluate(), QVariant("Hello"));
759 object1.setStringProperty(QLatin1String("World"));
760 QCOMPARE(expr.changed, true);
761 QCOMPARE(expr.evaluate(), QVariant("World"));
765 MyExpression expr(&context, "testObj.objectProperty.stringProperty");
766 QCOMPARE(expr.changed, false);
767 QCOMPARE(expr.evaluate(), QVariant());
769 object1.setObjectProperty(&object2);
770 QCOMPARE(expr.changed, true);
771 expr.changed = false;
772 QCOMPARE(expr.evaluate(), QVariant("Dog"));
774 object1.setObjectProperty(&object3);
775 QCOMPARE(expr.changed, true);
776 expr.changed = false;
777 QCOMPARE(expr.evaluate(), QVariant("Cat"));
779 object1.setObjectProperty(0);
780 QCOMPARE(expr.changed, true);
781 expr.changed = false;
782 QCOMPARE(expr.evaluate(), QVariant());
784 object1.setObjectProperty(&object3);
785 QCOMPARE(expr.changed, true);
786 expr.changed = false;
787 QCOMPARE(expr.evaluate(), QVariant("Cat"));
789 object3.setStringProperty("Donkey");
790 QCOMPARE(expr.changed, true);
791 expr.changed = false;
792 QCOMPARE(expr.evaluate(), QVariant("Donkey"));
796 void tst_qqmlecmascript::deferredProperties()
798 QQmlComponent component(&engine, testFileUrl("deferredProperties.qml"));
799 MyDeferredObject *object =
800 qobject_cast<MyDeferredObject *>(component.create());
801 QVERIFY(object != 0);
802 QCOMPARE(object->value(), 0);
803 QVERIFY(object->objectProperty() == 0);
804 QVERIFY(object->objectProperty2() != 0);
805 qmlExecuteDeferred(object);
806 QCOMPARE(object->value(), 10);
807 QVERIFY(object->objectProperty() != 0);
808 MyQmlObject *qmlObject =
809 qobject_cast<MyQmlObject *>(object->objectProperty());
810 QVERIFY(qmlObject != 0);
811 QCOMPARE(qmlObject->value(), 10);
812 object->setValue(19);
813 QCOMPARE(qmlObject->value(), 19);
818 // Check errors on deferred properties are correctly emitted
819 void tst_qqmlecmascript::deferredPropertiesErrors()
821 QQmlComponent component(&engine, testFileUrl("deferredPropertiesErrors.qml"));
822 MyDeferredObject *object =
823 qobject_cast<MyDeferredObject *>(component.create());
824 QVERIFY(object != 0);
825 QCOMPARE(object->value(), 0);
826 QVERIFY(object->objectProperty() == 0);
827 QVERIFY(object->objectProperty2() == 0);
829 QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject*";
830 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
832 qmlExecuteDeferred(object);
837 void tst_qqmlecmascript::extensionObjects()
839 QQmlComponent component(&engine, testFileUrl("extensionObjects.qml"));
840 MyExtendedObject *object =
841 qobject_cast<MyExtendedObject *>(component.create());
842 QVERIFY(object != 0);
843 QCOMPARE(object->baseProperty(), 13);
844 QCOMPARE(object->coreProperty(), 9);
845 object->setProperty("extendedProperty", QVariant(11));
846 object->setProperty("baseExtendedProperty", QVariant(92));
847 QCOMPARE(object->coreProperty(), 11);
848 QCOMPARE(object->baseProperty(), 92);
850 MyExtendedObject *nested = qobject_cast<MyExtendedObject*>(qvariant_cast<QObject *>(object->property("nested")));
852 QCOMPARE(nested->baseProperty(), 13);
853 QCOMPARE(nested->coreProperty(), 9);
854 nested->setProperty("extendedProperty", QVariant(11));
855 nested->setProperty("baseExtendedProperty", QVariant(92));
856 QCOMPARE(nested->coreProperty(), 11);
857 QCOMPARE(nested->baseProperty(), 92);
862 void tst_qqmlecmascript::overrideExtensionProperties()
864 QQmlComponent component(&engine, testFileUrl("extensionObjectsPropertyOverride.qml"));
865 OverrideDefaultPropertyObject *object =
866 qobject_cast<OverrideDefaultPropertyObject *>(component.create());
867 QVERIFY(object != 0);
868 QVERIFY(object->secondProperty() != 0);
869 QVERIFY(object->firstProperty() == 0);
874 void tst_qqmlecmascript::attachedProperties()
877 QQmlComponent component(&engine, testFileUrl("attachedProperty.qml"));
878 QObject *object = component.create();
879 QVERIFY(object != 0);
880 QCOMPARE(object->property("a").toInt(), 19);
881 QCOMPARE(object->property("b").toInt(), 19);
882 QCOMPARE(object->property("c").toInt(), 19);
883 QCOMPARE(object->property("d").toInt(), 19);
888 QQmlComponent component(&engine, testFileUrl("attachedProperty.2.qml"));
889 QObject *object = component.create();
890 QVERIFY(object != 0);
891 QCOMPARE(object->property("a").toInt(), 26);
892 QCOMPARE(object->property("b").toInt(), 26);
893 QCOMPARE(object->property("c").toInt(), 26);
894 QCOMPARE(object->property("d").toInt(), 26);
900 QQmlComponent component(&engine, testFileUrl("writeAttachedProperty.qml"));
901 QObject *object = component.create();
902 QVERIFY(object != 0);
904 QMetaObject::invokeMethod(object, "writeValue2");
906 MyQmlAttachedObject *attached =
907 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
908 QVERIFY(attached != 0);
910 QCOMPARE(attached->value2(), 9);
915 void tst_qqmlecmascript::enums()
919 QQmlComponent component(&engine, testFileUrl("enums.1.qml"));
920 QObject *object = component.create();
921 QVERIFY(object != 0);
923 QCOMPARE(object->property("enumProperty").toInt(), (int)MyQmlObject::EnumValue2);
924 QCOMPARE(object->property("relatedEnumProperty").toInt(), (int)MyEnumContainer::RelatedValue);
925 QCOMPARE(object->property("unrelatedEnumProperty").toInt(), (int)MyEnumContainer::RelatedValue);
926 QCOMPARE(object->property("qtEnumProperty").toInt(), (int)Qt::CaseInsensitive);
927 QCOMPARE(object->property("a").toInt(), 0);
928 QCOMPARE(object->property("b").toInt(), 1);
929 QCOMPARE(object->property("c").toInt(), 2);
930 QCOMPARE(object->property("d").toInt(), 3);
931 QCOMPARE(object->property("e").toInt(), 0);
932 QCOMPARE(object->property("f").toInt(), 1);
933 QCOMPARE(object->property("g").toInt(), 2);
934 QCOMPARE(object->property("h").toInt(), 3);
935 QCOMPARE(object->property("i").toInt(), 19);
936 QCOMPARE(object->property("j").toInt(), 19);
937 QCOMPARE(object->property("k").toInt(), 42);
938 QCOMPARE(object->property("l").toInt(), 333);
942 // Non-existent enums
944 QUrl file = testFileUrl("enums.2.qml");
945 QString w1 = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'MyEnum' for property 'MyUnregisteredEnumTypeObject::enumProperty'");
946 QString w2 = QLatin1String("QQmlExpression: Expression ") + testFileUrl("enums.2.qml").toString() + QLatin1String(":9 depends on non-NOTIFYable properties:");
947 QString w3 = QLatin1String(" MyUnregisteredEnumTypeObject::enumProperty");
948 QString w4 = file.toString() + ":7: Unable to assign [undefined] to int";
949 QString w5 = file.toString() + ":8: Unable to assign [undefined] to int";
950 QString w6 = file.toString() + ":9: Unable to assign [undefined] to int";
951 QString w7 = file.toString() + ":13: Unable to assign [undefined] to [unknown property type]";
952 QString w8 = file.toString() + ":31: Unable to assign int to [unknown property type]";
953 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
954 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
955 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
956 QTest::ignoreMessage(QtWarningMsg, qPrintable(w4));
957 QTest::ignoreMessage(QtWarningMsg, qPrintable(w5));
958 QTest::ignoreMessage(QtWarningMsg, qPrintable(w6));
959 QTest::ignoreMessage(QtWarningMsg, qPrintable(w7));
960 QTest::ignoreMessage(QtWarningMsg, qPrintable(w8));
962 QQmlComponent component(&engine, testFileUrl("enums.2.qml"));
963 QObject *object = component.create();
964 QVERIFY(object != 0);
965 QCOMPARE(object->property("a").toInt(), 0);
966 QCOMPARE(object->property("b").toInt(), 0);
967 QCOMPARE(object->property("c").toInt(), 0);
969 QString w9 = file.toString() + ":18: Error: Cannot assign JavaScript function to [unknown property type]";
970 QTest::ignoreMessage(QtWarningMsg, qPrintable(w9));
971 QMetaObject::invokeMethod(object, "testAssignmentOne");
973 QString w10 = file.toString() + ":21: Error: Cannot assign [undefined] to [unknown property type]";
974 QTest::ignoreMessage(QtWarningMsg, qPrintable(w10));
975 QMetaObject::invokeMethod(object, "testAssignmentTwo");
977 QString w11 = file.toString() + ":24: Error: Cannot assign [undefined] to [unknown property type]";
978 QTest::ignoreMessage(QtWarningMsg, qPrintable(w11));
979 QMetaObject::invokeMethod(object, "testAssignmentThree");
981 QString w12 = file.toString() + ":34: Error: Cannot assign int to an unregistered type";
982 QTest::ignoreMessage(QtWarningMsg, qPrintable(w12));
983 QMetaObject::invokeMethod(object, "testAssignmentFour");
989 QQmlComponent component(&engine, testFileUrl("enums.3.qml"));
990 QObject *object = component.create();
991 QVERIFY(object != 0);
993 // check the values are what we expect
994 QCOMPARE(object->property("a").toInt(), 4);
995 QCOMPARE(object->property("b").toInt(), 5);
996 QCOMPARE(object->property("c").toInt(), 9);
997 QCOMPARE(object->property("d").toInt(), 13);
998 QCOMPARE(object->property("e").toInt(), 2);
999 QCOMPARE(object->property("f").toInt(), 3);
1000 QCOMPARE(object->property("h").toInt(), 2);
1001 QCOMPARE(object->property("i").toInt(), 3);
1002 QCOMPARE(object->property("j").toInt(), -1);
1003 QCOMPARE(object->property("k").toInt(), 42);
1005 // count of change signals
1006 QCOMPARE(object->property("ac").toInt(), 0);
1007 QCOMPARE(object->property("bc").toInt(), 0);
1008 QCOMPARE(object->property("cc").toInt(), 0);
1009 QCOMPARE(object->property("dc").toInt(), 0);
1010 QCOMPARE(object->property("ec").toInt(), 0);
1011 QCOMPARE(object->property("fc").toInt(), 0);
1012 QCOMPARE(object->property("hc").toInt(), 1); // namespace -> binding
1013 QCOMPARE(object->property("ic").toInt(), 1); // namespace -> binding
1014 QCOMPARE(object->property("jc").toInt(), 0);
1015 QCOMPARE(object->property("kc").toInt(), 0);
1021 void tst_qqmlecmascript::valueTypeFunctions()
1023 QQmlComponent component(&engine, testFileUrl("valueTypeFunctions.qml"));
1024 MyTypeObject *obj = qobject_cast<MyTypeObject*>(component.create());
1026 QCOMPARE(obj->rectProperty(), QRect(0,0,100,100));
1027 QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5));
1033 Tests that writing a constant to a property with a binding on it disables the
1036 void tst_qqmlecmascript::constantsOverrideBindings()
1040 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.1.qml"));
1041 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1042 QVERIFY(object != 0);
1044 QCOMPARE(object->property("c2").toInt(), 0);
1045 object->setProperty("c1", QVariant(9));
1046 QCOMPARE(object->property("c2").toInt(), 9);
1048 emit object->basicSignal();
1050 QCOMPARE(object->property("c2").toInt(), 13);
1051 object->setProperty("c1", QVariant(8));
1052 QCOMPARE(object->property("c2").toInt(), 13);
1057 // During construction
1059 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.2.qml"));
1060 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1061 QVERIFY(object != 0);
1063 QCOMPARE(object->property("c1").toInt(), 0);
1064 QCOMPARE(object->property("c2").toInt(), 10);
1065 object->setProperty("c1", QVariant(9));
1066 QCOMPARE(object->property("c1").toInt(), 9);
1067 QCOMPARE(object->property("c2").toInt(), 10);
1075 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.3.qml"));
1076 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1077 QVERIFY(object != 0);
1079 QCOMPARE(object->property("c2").toInt(), 0);
1080 object->setProperty("c1", QVariant(9));
1081 QCOMPARE(object->property("c2").toInt(), 9);
1083 object->setProperty("c2", QVariant(13));
1084 QCOMPARE(object->property("c2").toInt(), 13);
1085 object->setProperty("c1", QVariant(7));
1086 QCOMPARE(object->property("c1").toInt(), 7);
1087 QCOMPARE(object->property("c2").toInt(), 13);
1095 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.4.qml"));
1096 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1097 QVERIFY(object != 0);
1099 QCOMPARE(object->property("c1").toInt(), 0);
1100 QCOMPARE(object->property("c3").toInt(), 10);
1101 object->setProperty("c1", QVariant(9));
1102 QCOMPARE(object->property("c1").toInt(), 9);
1103 QCOMPARE(object->property("c3").toInt(), 10);
1110 Tests that assigning a binding to a property that already has a binding causes
1111 the original binding to be disabled.
1113 void tst_qqmlecmascript::outerBindingOverridesInnerBinding()
1115 QQmlComponent component(&engine,
1116 testFileUrl("outerBindingOverridesInnerBinding.qml"));
1117 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1118 QVERIFY(object != 0);
1120 QCOMPARE(object->property("c1").toInt(), 0);
1121 QCOMPARE(object->property("c2").toInt(), 0);
1122 QCOMPARE(object->property("c3").toInt(), 0);
1124 object->setProperty("c1", QVariant(9));
1125 QCOMPARE(object->property("c1").toInt(), 9);
1126 QCOMPARE(object->property("c2").toInt(), 0);
1127 QCOMPARE(object->property("c3").toInt(), 0);
1129 object->setProperty("c3", QVariant(8));
1130 QCOMPARE(object->property("c1").toInt(), 9);
1131 QCOMPARE(object->property("c2").toInt(), 8);
1132 QCOMPARE(object->property("c3").toInt(), 8);
1138 Access a non-existent attached object.
1140 Tests for a regression where this used to crash.
1142 void tst_qqmlecmascript::nonExistentAttachedObject()
1144 QQmlComponent component(&engine, testFileUrl("nonExistentAttachedObject.qml"));
1146 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString";
1147 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
1149 QObject *object = component.create();
1150 QVERIFY(object != 0);
1155 void tst_qqmlecmascript::scope()
1158 QQmlComponent component(&engine, testFileUrl("scope.qml"));
1159 QObject *object = component.create();
1160 QVERIFY(object != 0);
1162 QCOMPARE(object->property("test1").toInt(), 1);
1163 QCOMPARE(object->property("test2").toInt(), 2);
1164 QCOMPARE(object->property("test3").toString(), QString("1Test"));
1165 QCOMPARE(object->property("test4").toString(), QString("2Test"));
1166 QCOMPARE(object->property("test5").toInt(), 1);
1167 QCOMPARE(object->property("test6").toInt(), 1);
1168 QCOMPARE(object->property("test7").toInt(), 2);
1169 QCOMPARE(object->property("test8").toInt(), 2);
1170 QCOMPARE(object->property("test9").toInt(), 1);
1171 QCOMPARE(object->property("test10").toInt(), 3);
1177 QQmlComponent component(&engine, testFileUrl("scope.2.qml"));
1178 QObject *object = component.create();
1179 QVERIFY(object != 0);
1181 QCOMPARE(object->property("test1").toInt(), 19);
1182 QCOMPARE(object->property("test2").toInt(), 19);
1183 QCOMPARE(object->property("test3").toInt(), 14);
1184 QCOMPARE(object->property("test4").toInt(), 14);
1185 QCOMPARE(object->property("test5").toInt(), 24);
1186 QCOMPARE(object->property("test6").toInt(), 24);
1192 QQmlComponent component(&engine, testFileUrl("scope.3.qml"));
1193 QObject *object = component.create();
1194 QVERIFY(object != 0);
1196 QCOMPARE(object->property("test1").toBool(), true);
1197 QCOMPARE(object->property("test2").toBool(), true);
1198 QCOMPARE(object->property("test3").toBool(), true);
1203 // Signal argument scope
1205 QQmlComponent component(&engine, testFileUrl("scope.4.qml"));
1206 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1207 QVERIFY(object != 0);
1209 QCOMPARE(object->property("test").toInt(), 0);
1210 QCOMPARE(object->property("test2").toString(), QString());
1212 emit object->argumentSignal(13, "Argument Scope", 9, MyQmlObject::EnumValue4, Qt::RightButton);
1214 QCOMPARE(object->property("test").toInt(), 13);
1215 QCOMPARE(object->property("test2").toString(), QString("Argument Scope"));
1221 QQmlComponent component(&engine, testFileUrl("scope.5.qml"));
1222 QObject *object = component.create();
1223 QVERIFY(object != 0);
1225 QCOMPARE(object->property("test1").toBool(), true);
1226 QCOMPARE(object->property("test2").toBool(), true);
1232 QQmlComponent component(&engine, testFileUrl("scope.6.qml"));
1233 QObject *object = component.create();
1234 QVERIFY(object != 0);
1236 QCOMPARE(object->property("test").toBool(), true);
1242 // In 4.7, non-library javascript files that had no imports shared the imports of their
1243 // importing context
1244 void tst_qqmlecmascript::importScope()
1246 QQmlComponent component(&engine, testFileUrl("importScope.qml"));
1247 QObject *o = component.create();
1250 QCOMPARE(o->property("test").toInt(), 240);
1256 Tests that "any" type passes through a synthesized signal parameter. This
1257 is essentially a test of QQmlMetaType::copy()
1259 void tst_qqmlecmascript::signalParameterTypes()
1261 QQmlComponent component(&engine, testFileUrl("signalParameterTypes.qml"));
1262 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1263 QVERIFY(object != 0);
1265 emit object->basicSignal();
1267 QCOMPARE(object->property("intProperty").toInt(), 10);
1268 QCOMPARE(object->property("realProperty").toReal(), 19.2);
1269 QVERIFY(object->property("colorProperty").value<QColor>() == QColor(255, 255, 0, 255));
1270 QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255)));
1271 QVERIFY(object->property("enumProperty") == MyQmlObject::EnumValue3);
1272 QVERIFY(object->property("qtEnumProperty") == Qt::LeftButton);
1278 Test that two JS objects for the same QObject compare as equal.
1280 void tst_qqmlecmascript::objectsCompareAsEqual()
1282 QQmlComponent component(&engine, testFileUrl("objectsCompareAsEqual.qml"));
1283 QObject *object = component.create();
1284 QVERIFY(object != 0);
1286 QCOMPARE(object->property("test1").toBool(), true);
1287 QCOMPARE(object->property("test2").toBool(), true);
1288 QCOMPARE(object->property("test3").toBool(), true);
1289 QCOMPARE(object->property("test4").toBool(), true);
1290 QCOMPARE(object->property("test5").toBool(), true);
1296 Confirm bindings and alias properties can coexist.
1298 Tests for a regression where the binding would not reevaluate.
1300 void tst_qqmlecmascript::aliasPropertyAndBinding()
1302 QQmlComponent component(&engine, testFileUrl("aliasPropertyAndBinding.qml"));
1303 QObject *object = component.create();
1304 QVERIFY(object != 0);
1306 QCOMPARE(object->property("c2").toInt(), 3);
1307 QCOMPARE(object->property("c3").toInt(), 3);
1309 object->setProperty("c2", QVariant(19));
1311 QCOMPARE(object->property("c2").toInt(), 19);
1312 QCOMPARE(object->property("c3").toInt(), 19);
1318 Ensure that we can write undefined value to an alias property,
1319 and that the aliased property is reset correctly if possible.
1321 void tst_qqmlecmascript::aliasPropertyReset()
1323 QObject *object = 0;
1325 // test that a manual write (of undefined) to a resettable aliased property succeeds
1326 QQmlComponent c1(&engine, testFileUrl("aliasreset/aliasPropertyReset.1.qml"));
1327 object = c1.create();
1328 QVERIFY(object != 0);
1329 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1330 QCOMPARE(object->property("aliasIsUndefined"), QVariant(false));
1331 QMetaObject::invokeMethod(object, "resetAliased");
1332 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1333 QCOMPARE(object->property("aliasIsUndefined"), QVariant(true));
1336 // test that a manual write (of undefined) to a resettable alias property succeeds
1337 QQmlComponent c2(&engine, testFileUrl("aliasreset/aliasPropertyReset.2.qml"));
1338 object = c2.create();
1339 QVERIFY(object != 0);
1340 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1341 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false));
1342 QMetaObject::invokeMethod(object, "resetAlias");
1343 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1344 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(true));
1347 // test that an alias to a bound property works correctly
1348 QQmlComponent c3(&engine, testFileUrl("aliasreset/aliasPropertyReset.3.qml"));
1349 object = c3.create();
1350 QVERIFY(object != 0);
1351 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1352 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false));
1353 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1354 QMetaObject::invokeMethod(object, "resetAlias");
1355 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1356 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(true));
1357 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1360 // test that a manual write (of undefined) to a resettable alias property
1361 // whose aliased property's object has been deleted, does not crash.
1362 QQmlComponent c4(&engine, testFileUrl("aliasreset/aliasPropertyReset.4.qml"));
1363 object = c4.create();
1364 QVERIFY(object != 0);
1365 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1366 QObject *loader = object->findChild<QObject*>("loader");
1367 QVERIFY(loader != 0);
1369 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // deletion should have caused value unset.
1370 QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash.
1371 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1372 QMetaObject::invokeMethod(object, "setAlias"); // shouldn't crash, and shouldn't change value (since it's no longer referencing anything).
1373 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1376 // test that binding an alias property to an undefined value works correctly
1377 QQmlComponent c5(&engine, testFileUrl("aliasreset/aliasPropertyReset.5.qml"));
1378 object = c5.create();
1379 QVERIFY(object != 0);
1380 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // bound to undefined value.
1383 // test that a manual write (of undefined) to a non-resettable property fails properly
1384 QUrl url = testFileUrl("aliasreset/aliasPropertyReset.error.1.qml");
1385 QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int");
1386 QQmlComponent e1(&engine, url);
1387 object = e1.create();
1388 QVERIFY(object != 0);
1389 QCOMPARE(object->property("intAlias").value<int>(), 12);
1390 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1391 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1392 QMetaObject::invokeMethod(object, "resetAlias");
1393 QCOMPARE(object->property("intAlias").value<int>(), 12);
1394 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1398 void tst_qqmlecmascript::componentCreation_data()
1400 QTest::addColumn<QString>("method");
1401 QTest::addColumn<QString>("creationError");
1402 QTest::addColumn<QString>("createdParent");
1404 QTest::newRow("url")
1408 QTest::newRow("urlMode")
1412 QTest::newRow("urlParent")
1416 QTest::newRow("urlNullParent")
1420 QTest::newRow("urlModeParent")
1424 QTest::newRow("urlModeNullParent")
1425 << "urlModeNullParent"
1428 QTest::newRow("invalidSecondArg")
1429 << "invalidSecondArg"
1430 << ":40: Error: Qt.createComponent(): Invalid arguments"
1432 QTest::newRow("invalidThirdArg")
1433 << "invalidThirdArg"
1434 << ":45: Error: Qt.createComponent(): Invalid parent object"
1436 QTest::newRow("invalidMode")
1438 << ":50: Error: Qt.createComponent(): Invalid arguments"
1443 Test using createComponent to dynamically generate a component.
1445 void tst_qqmlecmascript::componentCreation()
1447 QFETCH(QString, method);
1448 QFETCH(QString, creationError);
1449 QFETCH(QString, createdParent);
1451 QUrl testUrl(testFileUrl("componentCreation.qml"));
1453 if (!creationError.isEmpty()) {
1454 QString warning = testUrl.toString() + creationError;
1455 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1458 QQmlComponent component(&engine, testUrl);
1459 MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create());
1460 QVERIFY(object != 0);
1462 QMetaObject::invokeMethod(object, method.toUtf8());
1463 QQmlComponent *created = object->componentProperty();
1465 if (creationError.isEmpty()) {
1468 QObject *expectedParent;
1469 if (createdParent == QLatin1String("obj")) {
1470 expectedParent = object;
1471 } else if ((createdParent == QLatin1String("null")) || createdParent.isEmpty()) {
1474 QCOMPARE(created->parent(), expectedParent);
1478 void tst_qqmlecmascript::dynamicCreation_data()
1480 QTest::addColumn<QString>("method");
1481 QTest::addColumn<QString>("createdName");
1483 QTest::newRow("One") << "createOne" << "objectOne";
1484 QTest::newRow("Two") << "createTwo" << "objectTwo";
1485 QTest::newRow("Three") << "createThree" << "objectThree";
1489 Test using createQmlObject to dynamically generate an item
1490 Also using createComponent is tested.
1492 void tst_qqmlecmascript::dynamicCreation()
1494 QFETCH(QString, method);
1495 QFETCH(QString, createdName);
1497 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
1498 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1499 QVERIFY(object != 0);
1501 QMetaObject::invokeMethod(object, method.toUtf8());
1502 QObject *created = object->objectProperty();
1504 QCOMPARE(created->objectName(), createdName);
1510 Tests the destroy function
1512 void tst_qqmlecmascript::dynamicDestruction()
1515 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.qml"));
1516 QQmlGuard<MyQmlObject> object = qobject_cast<MyQmlObject*>(component.create());
1517 QVERIFY(object != 0);
1518 QQmlGuard<QObject> createdQmlObject = 0;
1520 QMetaObject::invokeMethod(object, "create");
1521 createdQmlObject = object->objectProperty();
1522 QVERIFY(createdQmlObject);
1523 QCOMPARE(createdQmlObject->objectName(), QString("emptyObject"));
1525 QMetaObject::invokeMethod(object, "killOther");
1526 QVERIFY(createdQmlObject);
1528 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1529 QCoreApplication::processEvents();
1530 QVERIFY(createdQmlObject);
1531 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1532 if (createdQmlObject) {
1534 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1535 QCoreApplication::processEvents();
1538 QVERIFY(!createdQmlObject);
1540 QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
1541 QMetaObject::invokeMethod(object, "killMe");
1543 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1544 QCoreApplication::processEvents();
1549 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.2.qml"));
1550 QObject *o = component.create();
1553 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1555 QMetaObject::invokeMethod(o, "create");
1557 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) != 0);
1559 QMetaObject::invokeMethod(o, "destroy");
1561 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1562 QCoreApplication::processEvents();
1564 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1571 QQmlGuard<QObject> createdQmlObject = 0;
1572 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.3.qml"));
1573 QObject *o = component.create();
1575 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1576 QMetaObject::invokeMethod(o, "create");
1577 createdQmlObject = qvariant_cast<QObject*>(o->property("objectProperty"));
1578 QVERIFY(createdQmlObject);
1579 QMetaObject::invokeMethod(o, "destroy");
1580 QVERIFY(qvariant_cast<bool>(o->property("test")) == false);
1581 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1583 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1584 QCoreApplication::processEvents();
1586 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1587 QVERIFY(qvariant_cast<bool>(o->property("test")) == true);
1593 tests that id.toString() works
1595 void tst_qqmlecmascript::objectToString()
1597 QQmlComponent component(&engine, testFileUrl("qmlToString.qml"));
1598 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1599 QVERIFY(object != 0);
1600 QMetaObject::invokeMethod(object, "testToString");
1601 QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_"));
1602 QVERIFY(object->stringProperty().endsWith(", \"objName\")"));
1608 tests that id.hasOwnProperty() works
1610 void tst_qqmlecmascript::objectHasOwnProperty()
1612 QUrl url = testFileUrl("qmlHasOwnProperty.qml");
1613 QString warning1 = url.toString() + ":59: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1614 QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1615 QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1617 QQmlComponent component(&engine, url);
1618 QObject *object = component.create();
1619 QVERIFY(object != 0);
1621 // test QObjects in QML
1622 QMetaObject::invokeMethod(object, "testHasOwnPropertySuccess");
1623 QVERIFY(object->property("result").value<bool>() == true);
1624 QMetaObject::invokeMethod(object, "testHasOwnPropertyFailure");
1625 QVERIFY(object->property("result").value<bool>() == false);
1627 // now test other types in QML
1628 QObject *child = object->findChild<QObject*>("typeObj");
1629 QVERIFY(child != 0);
1630 QMetaObject::invokeMethod(child, "testHasOwnPropertySuccess");
1631 QCOMPARE(child->property("valueTypeHasOwnProperty").toBool(), true);
1632 QCOMPARE(child->property("valueTypeHasOwnProperty2").toBool(), true);
1633 QCOMPARE(child->property("variantTypeHasOwnProperty").toBool(), true);
1634 QCOMPARE(child->property("stringTypeHasOwnProperty").toBool(), true);
1635 QCOMPARE(child->property("listTypeHasOwnProperty").toBool(), true);
1636 QCOMPARE(child->property("emptyListTypeHasOwnProperty").toBool(), true);
1637 QCOMPARE(child->property("enumTypeHasOwnProperty").toBool(), true);
1638 QCOMPARE(child->property("typenameHasOwnProperty").toBool(), true);
1639 QCOMPARE(child->property("typenameHasOwnProperty2").toBool(), true);
1640 QCOMPARE(child->property("singletonTypeTypeHasOwnProperty").toBool(), true);
1641 QCOMPARE(child->property("singletonTypePropertyTypeHasOwnProperty").toBool(), true);
1643 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1644 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureOne");
1645 QCOMPARE(child->property("enumNonValueHasOwnProperty").toBool(), false);
1646 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1647 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureTwo");
1648 QCOMPARE(child->property("singletonTypeNonPropertyHasOwnProperty").toBool(), false);
1649 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1650 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureThree");
1651 QCOMPARE(child->property("listAtInvalidHasOwnProperty").toBool(), false);
1657 Tests bindings that indirectly cause their own deletion work.
1659 This test is best run under valgrind to ensure no invalid memory access occur.
1661 void tst_qqmlecmascript::selfDeletingBinding()
1664 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.qml"));
1665 QObject *object = component.create();
1666 QVERIFY(object != 0);
1667 object->setProperty("triggerDelete", true);
1672 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.2.qml"));
1673 QObject *object = component.create();
1674 QVERIFY(object != 0);
1675 object->setProperty("triggerDelete", true);
1681 Test that extended object properties can be accessed.
1683 This test a regression where this used to crash. The issue was specificially
1684 for extended objects that did not include a synthesized meta object (so non-root
1685 and no synthesiszed properties).
1687 void tst_qqmlecmascript::extendedObjectPropertyLookup()
1689 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup.qml"));
1690 QObject *object = component.create();
1691 QVERIFY(object != 0);
1696 Test that extended object properties can be accessed correctly.
1698 void tst_qqmlecmascript::extendedObjectPropertyLookup2()
1700 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup2.qml"));
1701 QObject *object = component.create();
1702 QVERIFY(object != 0);
1704 QVariant returnValue;
1705 QVERIFY(QMetaObject::invokeMethod(object, "getValue", Q_RETURN_ARG(QVariant, returnValue)));
1706 QCOMPARE(returnValue.toInt(), 42);
1711 Test file/lineNumbers for binding/Script errors.
1713 void tst_qqmlecmascript::scriptErrors()
1715 QQmlComponent component(&engine, testFileUrl("scriptErrors.qml"));
1716 QString url = component.url().toString();
1718 QString warning1 = url.left(url.length() - 3) + "js:2: Error: Invalid write to global property \"a\"";
1719 QString warning2 = url + ":5: ReferenceError: a is not defined";
1720 QString warning3 = url.left(url.length() - 3) + "js:4: Error: Invalid write to global property \"a\"";
1721 QString warning4 = url + ":13: ReferenceError: a is not defined";
1722 QString warning5 = url + ":11: ReferenceError: a is not defined";
1723 QString warning6 = url + ":10: Unable to assign [undefined] to int";
1724 QString warning7 = url + ":15: Error: Cannot assign to read-only property \"trueProperty\"";
1725 QString warning8 = url + ":16: Error: Cannot assign to non-existent property \"fakeProperty\"";
1727 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1728 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1729 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1730 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
1731 QTest::ignoreMessage(QtWarningMsg, warning6.toLatin1().constData());
1732 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1733 QVERIFY(object != 0);
1735 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
1736 emit object->basicSignal();
1738 QTest::ignoreMessage(QtWarningMsg, warning7.toLatin1().constData());
1739 emit object->anotherBasicSignal();
1741 QTest::ignoreMessage(QtWarningMsg, warning8.toLatin1().constData());
1742 emit object->thirdBasicSignal();
1748 Test file/lineNumbers for inline functions.
1750 void tst_qqmlecmascript::functionErrors()
1752 QQmlComponent component(&engine, testFileUrl("functionErrors.qml"));
1753 QString url = component.url().toString();
1755 QString warning = url + ":5: Error: Invalid write to global property \"a\"";
1757 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1759 QObject *object = component.create();
1760 QVERIFY(object != 0);
1763 // test that if an exception occurs while invoking js function from cpp, it is reported as expected.
1764 QQmlComponent componentTwo(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
1765 url = componentTwo.url().toString();
1766 object = componentTwo.create();
1767 QVERIFY(object != 0);
1769 QString srpname = object->property("srp_name").toString();
1771 warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srpname
1772 + QLatin1String(" is not a function");
1773 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed.
1774 QMetaObject::invokeMethod(object, "retrieveScarceResource");
1779 Test various errors that can occur when assigning a property from script
1781 void tst_qqmlecmascript::propertyAssignmentErrors()
1783 QQmlComponent component(&engine, testFileUrl("propertyAssignmentErrors.qml"));
1785 QString url = component.url().toString();
1787 QObject *object = component.create();
1788 QVERIFY(object != 0);
1790 QCOMPARE(object->property("test1").toBool(), true);
1791 QCOMPARE(object->property("test2").toBool(), true);
1797 Test bindings still work when the reeval is triggered from within
1800 void tst_qqmlecmascript::signalTriggeredBindings()
1802 QQmlComponent component(&engine, testFileUrl("signalTriggeredBindings.qml"));
1803 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1804 QVERIFY(object != 0);
1806 QCOMPARE(object->property("base").toReal(), 50.);
1807 QCOMPARE(object->property("test1").toReal(), 50.);
1808 QCOMPARE(object->property("test2").toReal(), 50.);
1810 object->basicSignal();
1812 QCOMPARE(object->property("base").toReal(), 200.);
1813 QCOMPARE(object->property("test1").toReal(), 200.);
1814 QCOMPARE(object->property("test2").toReal(), 200.);
1816 object->argumentSignal(10, QString(), 10, MyQmlObject::EnumValue4, Qt::RightButton);
1818 QCOMPARE(object->property("base").toReal(), 400.);
1819 QCOMPARE(object->property("test1").toReal(), 400.);
1820 QCOMPARE(object->property("test2").toReal(), 400.);
1826 Test that list properties can be iterated from ECMAScript
1828 void tst_qqmlecmascript::listProperties()
1830 QQmlComponent component(&engine, testFileUrl("listProperties.qml"));
1831 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1832 QVERIFY(object != 0);
1834 QCOMPARE(object->property("test1").toInt(), 21);
1835 QCOMPARE(object->property("test2").toInt(), 2);
1836 QCOMPARE(object->property("test3").toBool(), true);
1837 QCOMPARE(object->property("test4").toBool(), true);
1842 void tst_qqmlecmascript::exceptionClearsOnReeval()
1844 QQmlComponent component(&engine, testFileUrl("exceptionClearsOnReeval.qml"));
1845 QString url = component.url().toString();
1847 QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null";
1849 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1850 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1851 QVERIFY(object != 0);
1853 QCOMPARE(object->property("test").toBool(), false);
1855 MyQmlObject object2;
1856 MyQmlObject object3;
1857 object2.setObjectProperty(&object3);
1858 object->setObjectProperty(&object2);
1860 QCOMPARE(object->property("test").toBool(), true);
1865 void tst_qqmlecmascript::exceptionSlotProducesWarning()
1867 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning.qml"));
1868 QString url = component.url().toString();
1870 QString warning = component.url().toString() + ":6: Error: JS exception";
1872 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1873 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1874 QVERIFY(object != 0);
1878 void tst_qqmlecmascript::exceptionBindingProducesWarning()
1880 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning2.qml"));
1881 QString url = component.url().toString();
1883 QString warning = component.url().toString() + ":5: Error: JS exception";
1885 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1886 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1887 QVERIFY(object != 0);
1891 void tst_qqmlecmascript::compileInvalidBinding()
1893 // QTBUG-23387: ensure that invalid bindings don't cause a crash.
1894 QQmlComponent component(&engine, testFileUrl("v8bindingException.qml"));
1895 QObject *object = component.create();
1896 QVERIFY(object != 0);
1900 static int transientErrorsMsgCount = 0;
1901 static void transientErrorsMsgHandler(QtMsgType, const char *)
1903 ++transientErrorsMsgCount;
1906 // Check that transient binding errors are not displayed
1907 void tst_qqmlecmascript::transientErrors()
1910 QQmlComponent component(&engine, testFileUrl("transientErrors.qml"));
1912 transientErrorsMsgCount = 0;
1913 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1915 QObject *object = component.create();
1916 QVERIFY(object != 0);
1918 qInstallMsgHandler(old);
1920 QCOMPARE(transientErrorsMsgCount, 0);
1925 // One binding erroring multiple times, but then resolving
1927 QQmlComponent component(&engine, testFileUrl("transientErrors.2.qml"));
1929 transientErrorsMsgCount = 0;
1930 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1932 QObject *object = component.create();
1933 QVERIFY(object != 0);
1935 qInstallMsgHandler(old);
1937 QCOMPARE(transientErrorsMsgCount, 0);
1943 // Check that errors during shutdown are minimized
1944 void tst_qqmlecmascript::shutdownErrors()
1946 QQmlComponent component(&engine, testFileUrl("shutdownErrors.qml"));
1947 QObject *object = component.create();
1948 QVERIFY(object != 0);
1950 transientErrorsMsgCount = 0;
1951 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1955 qInstallMsgHandler(old);
1956 QCOMPARE(transientErrorsMsgCount, 0);
1959 void tst_qqmlecmascript::compositePropertyType()
1961 QQmlComponent component(&engine, testFileUrl("compositePropertyType.qml"));
1963 QTest::ignoreMessage(QtDebugMsg, "hello world");
1964 QObject *object = qobject_cast<QObject *>(component.create());
1969 void tst_qqmlecmascript::jsObject()
1971 QQmlComponent component(&engine, testFileUrl("jsObject.qml"));
1972 QObject *object = component.create();
1973 QVERIFY(object != 0);
1975 QCOMPARE(object->property("test").toInt(), 92);
1980 void tst_qqmlecmascript::undefinedResetsProperty()
1983 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.qml"));
1984 QObject *object = component.create();
1985 QVERIFY(object != 0);
1987 QCOMPARE(object->property("resettableProperty").toInt(), 92);
1989 object->setProperty("setUndefined", true);
1991 QCOMPARE(object->property("resettableProperty").toInt(), 13);
1993 object->setProperty("setUndefined", false);
1995 QCOMPARE(object->property("resettableProperty").toInt(), 92);
2000 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.2.qml"));
2001 QObject *object = component.create();
2002 QVERIFY(object != 0);
2004 QCOMPARE(object->property("resettableProperty").toInt(), 19);
2006 QMetaObject::invokeMethod(object, "doReset");
2008 QCOMPARE(object->property("resettableProperty").toInt(), 13);
2014 // Aliases to variant properties should work
2015 void tst_qqmlecmascript::qtbug_22464()
2017 QQmlComponent component(&engine, testFileUrl("qtbug_22464.qml"));
2018 QObject *object = component.create();
2019 QVERIFY(object != 0);
2021 QCOMPARE(object->property("test").toBool(), true);
2026 void tst_qqmlecmascript::qtbug_21580()
2028 QQmlComponent component(&engine, testFileUrl("qtbug_21580.qml"));
2030 QObject *object = component.create();
2031 QVERIFY(object != 0);
2033 QCOMPARE(object->property("test").toBool(), true);
2038 // Causes a v8 binding, but not all v8 bindings to be destroyed during evaluation
2039 void tst_qqmlecmascript::singleV8BindingDestroyedDuringEvaluation()
2041 QQmlComponent component(&engine, testFileUrl("singleV8BindingDestroyedDuringEvaluation.qml"));
2043 QObject *object = component.create();
2044 QVERIFY(object != 0);
2049 void tst_qqmlecmascript::bug1()
2051 QQmlComponent component(&engine, testFileUrl("bug.1.qml"));
2052 QObject *object = component.create();
2053 QVERIFY(object != 0);
2055 QCOMPARE(object->property("test").toInt(), 14);
2057 object->setProperty("a", 11);
2059 QCOMPARE(object->property("test").toInt(), 3);
2061 object->setProperty("b", true);
2063 QCOMPARE(object->property("test").toInt(), 9);
2068 #ifndef QT_NO_WIDGETS
2069 void tst_qqmlecmascript::bug2()
2071 QQmlComponent component(&engine);
2072 component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl());
2074 QObject *object = component.create();
2075 QVERIFY(object != 0);
2081 // Don't crash in createObject when the component has errors.
2082 void tst_qqmlecmascript::dynamicCreationCrash()
2084 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
2085 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2086 QVERIFY(object != 0);
2088 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2089 QMetaObject::invokeMethod(object, "dontCrash");
2090 QObject *created = object->objectProperty();
2091 QVERIFY(created == 0);
2096 // ownership transferred to JS, ensure that GC runs the dtor
2097 void tst_qqmlecmascript::dynamicCreationOwnership()
2100 int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
2102 // allow the engine to go out of scope too.
2104 QQmlEngine dcoEngine;
2105 QQmlComponent component(&dcoEngine, testFileUrl("dynamicCreationOwnership.qml"));
2106 QObject *object = component.create();
2107 QVERIFY(object != 0);
2108 MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo");
2109 QVERIFY(mdcdo != 0);
2110 mdcdo->setDtorCount(&dtorCount);
2112 for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
2113 QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
2115 // we do this once manually, but it should be done automatically
2116 // when the engine goes out of scope (since it should gc in dtor)
2117 QMetaObject::invokeMethod(object, "performGc");
2120 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2121 QCoreApplication::processEvents();
2127 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2128 QCoreApplication::processEvents();
2129 QCOMPARE(dtorCount, expectedDtorCount);
2132 void tst_qqmlecmascript::regExpBug()
2136 QQmlComponent component(&engine, testFileUrl("regExp.qml"));
2137 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2138 QVERIFY(object != 0);
2139 QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]"));
2145 QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString());
2146 QQmlComponent component(&engine, testFileUrl("regExp.2.qml"));
2147 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2148 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2150 QCOMPARE(component.errorString(), err);
2154 static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source)
2156 QString functionSource = QLatin1String("(function(object) { return ") +
2157 QLatin1String(source) + QLatin1String(" })");
2159 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2162 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2163 if (function.IsEmpty())
2165 v8::Handle<v8::Value> args[] = { o };
2166 function->Call(engine->global(), 1, args);
2167 return tc.HasCaught();
2170 static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o,
2171 const char *source, v8::Handle<v8::Value> result)
2173 QString functionSource = QLatin1String("(function(object) { return ") +
2174 QLatin1String(source) + QLatin1String(" })");
2176 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2179 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2180 if (function.IsEmpty())
2182 v8::Handle<v8::Value> args[] = { o };
2184 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2189 return value->StrictEquals(result);
2192 static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o,
2195 QString functionSource = QLatin1String("(function(object) { return ") +
2196 QLatin1String(source) + QLatin1String(" })");
2198 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2200 return v8::Handle<v8::Value>();
2201 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2202 if (function.IsEmpty())
2203 return v8::Handle<v8::Value>();
2204 v8::Handle<v8::Value> args[] = { o };
2206 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2209 return v8::Handle<v8::Value>();
2213 #define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
2214 #define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result)
2215 #define EVALUATE(source) evaluate(engine, object, source)
2217 void tst_qqmlecmascript::callQtInvokables()
2219 MyInvokableObject o;
2221 QQmlEngine qmlengine;
2222 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine);
2224 QV8Engine *engine = ep->v8engine();
2226 v8::HandleScope handle_scope;
2227 v8::Context::Scope scope(engine->context());
2229 v8::Local<v8::Object> object = engine->newQObject(&o)->ToObject();
2231 // Non-existent methods
2233 QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
2234 QCOMPARE(o.error(), false);
2235 QCOMPARE(o.invoked(), -1);
2236 QCOMPARE(o.actuals().count(), 0);
2239 QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)"));
2240 QCOMPARE(o.error(), false);
2241 QCOMPARE(o.invoked(), -1);
2242 QCOMPARE(o.actuals().count(), 0);
2244 // Insufficient arguments
2246 QVERIFY(EVALUATE_ERROR("object.method_int()"));
2247 QCOMPARE(o.error(), false);
2248 QCOMPARE(o.invoked(), -1);
2249 QCOMPARE(o.actuals().count(), 0);
2252 QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
2253 QCOMPARE(o.error(), false);
2254 QCOMPARE(o.invoked(), -1);
2255 QCOMPARE(o.actuals().count(), 0);
2257 // Excessive arguments
2259 QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", v8::Undefined()));
2260 QCOMPARE(o.error(), false);
2261 QCOMPARE(o.invoked(), 8);
2262 QCOMPARE(o.actuals().count(), 1);
2263 QCOMPARE(o.actuals().at(0), QVariant(10));
2266 QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", v8::Undefined()));
2267 QCOMPARE(o.error(), false);
2268 QCOMPARE(o.invoked(), 9);
2269 QCOMPARE(o.actuals().count(), 2);
2270 QCOMPARE(o.actuals().at(0), QVariant(10));
2271 QCOMPARE(o.actuals().at(1), QVariant(11));
2273 // Test return types
2275 QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", v8::Undefined()));
2276 QCOMPARE(o.error(), false);
2277 QCOMPARE(o.invoked(), 0);
2278 QCOMPARE(o.actuals().count(), 0);
2281 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", v8::Integer::New(6)));
2282 QCOMPARE(o.error(), false);
2283 QCOMPARE(o.invoked(), 1);
2284 QCOMPARE(o.actuals().count(), 0);
2287 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", v8::Number::New(19.75)));
2288 QCOMPARE(o.error(), false);
2289 QCOMPARE(o.invoked(), 2);
2290 QCOMPARE(o.actuals().count(), 0);
2294 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QPointF()");
2295 QVERIFY(!ret.IsEmpty());
2296 QCOMPARE(engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5)));
2297 QCOMPARE(o.error(), false);
2298 QCOMPARE(o.invoked(), 3);
2299 QCOMPARE(o.actuals().count(), 0);
2304 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QObject()");
2305 QCOMPARE(engine->toQObject(ret), (QObject *)&o);
2306 QCOMPARE(o.error(), false);
2307 QCOMPARE(o.invoked(), 4);
2308 QCOMPARE(o.actuals().count(), 0);
2312 QVERIFY(EVALUATE_ERROR("object.method_NoArgs_unknown()"));
2313 QCOMPARE(o.error(), false);
2314 QCOMPARE(o.invoked(), -1);
2315 QCOMPARE(o.actuals().count(), 0);
2319 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QScriptValue()");
2320 QVERIFY(ret->IsString());
2321 QCOMPARE(engine->toString(ret), QString("Hello world"));
2322 QCOMPARE(o.error(), false);
2323 QCOMPARE(o.invoked(), 6);
2324 QCOMPARE(o.actuals().count(), 0);
2328 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks")));
2329 QCOMPARE(o.error(), false);
2330 QCOMPARE(o.invoked(), 7);
2331 QCOMPARE(o.actuals().count(), 0);
2335 QVERIFY(EVALUATE_VALUE("object.method_int(94)", v8::Undefined()));
2336 QCOMPARE(o.error(), false);
2337 QCOMPARE(o.invoked(), 8);
2338 QCOMPARE(o.actuals().count(), 1);
2339 QCOMPARE(o.actuals().at(0), QVariant(94));
2342 QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", v8::Undefined()));
2343 QCOMPARE(o.error(), false);
2344 QCOMPARE(o.invoked(), 8);
2345 QCOMPARE(o.actuals().count(), 1);
2346 QCOMPARE(o.actuals().at(0), QVariant(94));
2349 QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", v8::Undefined()));
2350 QCOMPARE(o.error(), false);
2351 QCOMPARE(o.invoked(), 8);
2352 QCOMPARE(o.actuals().count(), 1);
2353 QCOMPARE(o.actuals().at(0), QVariant(0));
2356 QVERIFY(EVALUATE_VALUE("object.method_int(null)", v8::Undefined()));
2357 QCOMPARE(o.error(), false);
2358 QCOMPARE(o.invoked(), 8);
2359 QCOMPARE(o.actuals().count(), 1);
2360 QCOMPARE(o.actuals().at(0), QVariant(0));
2363 QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", v8::Undefined()));
2364 QCOMPARE(o.error(), false);
2365 QCOMPARE(o.invoked(), 8);
2366 QCOMPARE(o.actuals().count(), 1);
2367 QCOMPARE(o.actuals().at(0), QVariant(0));
2370 QVERIFY(EVALUATE_VALUE("object.method_int(object)", v8::Undefined()));
2371 QCOMPARE(o.error(), false);
2372 QCOMPARE(o.invoked(), 8);
2373 QCOMPARE(o.actuals().count(), 1);
2374 QCOMPARE(o.actuals().at(0), QVariant(0));
2377 QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", v8::Undefined()));
2378 QCOMPARE(o.error(), false);
2379 QCOMPARE(o.invoked(), 9);
2380 QCOMPARE(o.actuals().count(), 2);
2381 QCOMPARE(o.actuals().at(0), QVariant(122));
2382 QCOMPARE(o.actuals().at(1), QVariant(9));
2385 QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", v8::Undefined()));
2386 QCOMPARE(o.error(), false);
2387 QCOMPARE(o.invoked(), 10);
2388 QCOMPARE(o.actuals().count(), 1);
2389 QCOMPARE(o.actuals().at(0), QVariant(94.3));
2392 QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", v8::Undefined()));
2393 QCOMPARE(o.error(), false);
2394 QCOMPARE(o.invoked(), 10);
2395 QCOMPARE(o.actuals().count(), 1);
2396 QCOMPARE(o.actuals().at(0), QVariant(94.3));
2399 QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", v8::Undefined()));
2400 QCOMPARE(o.error(), false);
2401 QCOMPARE(o.invoked(), 10);
2402 QCOMPARE(o.actuals().count(), 1);
2403 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2406 QVERIFY(EVALUATE_VALUE("object.method_real(null)", v8::Undefined()));
2407 QCOMPARE(o.error(), false);
2408 QCOMPARE(o.invoked(), 10);
2409 QCOMPARE(o.actuals().count(), 1);
2410 QCOMPARE(o.actuals().at(0), QVariant(0));
2413 QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", v8::Undefined()));
2414 QCOMPARE(o.error(), false);
2415 QCOMPARE(o.invoked(), 10);
2416 QCOMPARE(o.actuals().count(), 1);
2417 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2420 QVERIFY(EVALUATE_VALUE("object.method_real(object)", v8::Undefined()));
2421 QCOMPARE(o.error(), false);
2422 QCOMPARE(o.invoked(), 10);
2423 QCOMPARE(o.actuals().count(), 1);
2424 QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2427 QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", v8::Undefined()));
2428 QCOMPARE(o.error(), false);
2429 QCOMPARE(o.invoked(), 11);
2430 QCOMPARE(o.actuals().count(), 1);
2431 QCOMPARE(o.actuals().at(0), QVariant("Hello world"));
2434 QVERIFY(EVALUATE_VALUE("object.method_QString(19)", v8::Undefined()));
2435 QCOMPARE(o.error(), false);
2436 QCOMPARE(o.invoked(), 11);
2437 QCOMPARE(o.actuals().count(), 1);
2438 QCOMPARE(o.actuals().at(0), QVariant("19"));
2442 QString expected = "MyInvokableObject(0x" + QString::number((quintptr)&o, 16) + ")";
2443 QVERIFY(EVALUATE_VALUE("object.method_QString(object)", v8::Undefined()));
2444 QCOMPARE(o.error(), false);
2445 QCOMPARE(o.invoked(), 11);
2446 QCOMPARE(o.actuals().count(), 1);
2447 QCOMPARE(o.actuals().at(0), QVariant(expected));
2451 QVERIFY(EVALUATE_VALUE("object.method_QString(null)", v8::Undefined()));
2452 QCOMPARE(o.error(), false);
2453 QCOMPARE(o.invoked(), 11);
2454 QCOMPARE(o.actuals().count(), 1);
2455 QCOMPARE(o.actuals().at(0), QVariant(QString()));
2458 QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", v8::Undefined()));
2459 QCOMPARE(o.error(), false);
2460 QCOMPARE(o.invoked(), 11);
2461 QCOMPARE(o.actuals().count(), 1);
2462 QCOMPARE(o.actuals().at(0), QVariant(QString()));
2465 QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", v8::Undefined()));
2466 QCOMPARE(o.error(), false);
2467 QCOMPARE(o.invoked(), 12);
2468 QCOMPARE(o.actuals().count(), 1);
2469 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2472 QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", v8::Undefined()));
2473 QCOMPARE(o.error(), false);
2474 QCOMPARE(o.invoked(), 12);
2475 QCOMPARE(o.actuals().count(), 1);
2476 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2479 QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", v8::Undefined()));
2480 QCOMPARE(o.error(), false);
2481 QCOMPARE(o.invoked(), 12);
2482 QCOMPARE(o.actuals().count(), 1);
2483 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2486 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", v8::Undefined()));
2487 QCOMPARE(o.error(), false);
2488 QCOMPARE(o.invoked(), 12);
2489 QCOMPARE(o.actuals().count(), 1);
2490 QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2493 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", v8::Undefined()));
2494 QCOMPARE(o.error(), false);
2495 QCOMPARE(o.invoked(), 12);
2496 QCOMPARE(o.actuals().count(), 1);
2497 QCOMPARE(o.actuals().at(0), QVariant(QPointF(99.3, -10.2)));
2500 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", v8::Undefined()));
2501 QCOMPARE(o.error(), false);
2502 QCOMPARE(o.invoked(), 12);
2503 QCOMPARE(o.actuals().count(), 1);
2504 QCOMPARE(o.actuals().at(0), QVariant(QPointF(9, 12)));
2507 QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", v8::Undefined()));
2508 QCOMPARE(o.error(), false);
2509 QCOMPARE(o.invoked(), 13);
2510 QCOMPARE(o.actuals().count(), 1);
2511 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2514 QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", v8::Undefined()));
2515 QCOMPARE(o.error(), false);
2516 QCOMPARE(o.invoked(), 13);
2517 QCOMPARE(o.actuals().count(), 1);
2518 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2521 QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", v8::Undefined()));
2522 QCOMPARE(o.error(), false);
2523 QCOMPARE(o.invoked(), 13);
2524 QCOMPARE(o.actuals().count(), 1);
2525 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2528 QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", v8::Undefined()));
2529 QCOMPARE(o.error(), false);
2530 QCOMPARE(o.invoked(), 13);
2531 QCOMPARE(o.actuals().count(), 1);
2532 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2535 QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", v8::Undefined()));
2536 QCOMPARE(o.error(), false);
2537 QCOMPARE(o.invoked(), 13);
2538 QCOMPARE(o.actuals().count(), 1);
2539 QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)&o));
2542 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined()));
2543 QCOMPARE(o.error(), false);
2544 QCOMPARE(o.invoked(), 14);
2545 QCOMPARE(o.actuals().count(), 1);
2546 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isNull());
2549 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined()));
2550 QCOMPARE(o.error(), false);
2551 QCOMPARE(o.invoked(), 14);
2552 QCOMPARE(o.actuals().count(), 1);
2553 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isUndefined());
2556 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined()));
2557 QCOMPARE(o.error(), false);
2558 QCOMPARE(o.invoked(), 14);
2559 QCOMPARE(o.actuals().count(), 1);
2560 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).strictlyEquals(QJSValue(19)));
2563 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined()));
2564 QCOMPARE(o.error(), false);
2565 QCOMPARE(o.invoked(), 14);
2566 QCOMPARE(o.actuals().count(), 1);
2567 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isArray());
2570 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined()));
2571 QCOMPARE(o.error(), false);
2572 QCOMPARE(o.invoked(), 15);
2573 QCOMPARE(o.actuals().count(), 2);
2574 QCOMPARE(o.actuals().at(0), QVariant(4));
2575 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isNull());
2578 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined()));
2579 QCOMPARE(o.error(), false);
2580 QCOMPARE(o.invoked(), 15);
2581 QCOMPARE(o.actuals().count(), 2);
2582 QCOMPARE(o.actuals().at(0), QVariant(8));
2583 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isUndefined());
2586 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined()));
2587 QCOMPARE(o.error(), false);
2588 QCOMPARE(o.invoked(), 15);
2589 QCOMPARE(o.actuals().count(), 2);
2590 QCOMPARE(o.actuals().at(0), QVariant(3));
2591 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).strictlyEquals(QJSValue(19)));
2594 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined()));
2595 QCOMPARE(o.error(), false);
2596 QCOMPARE(o.invoked(), 15);
2597 QCOMPARE(o.actuals().count(), 2);
2598 QCOMPARE(o.actuals().at(0), QVariant(44));
2599 QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isArray());
2602 QVERIFY(EVALUATE_ERROR("object.method_overload()"));
2603 QCOMPARE(o.error(), false);
2604 QCOMPARE(o.invoked(), -1);
2605 QCOMPARE(o.actuals().count(), 0);
2608 QVERIFY(EVALUATE_VALUE("object.method_overload(10)", v8::Undefined()));
2609 QCOMPARE(o.error(), false);
2610 QCOMPARE(o.invoked(), 16);
2611 QCOMPARE(o.actuals().count(), 1);
2612 QCOMPARE(o.actuals().at(0), QVariant(10));
2615 QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", v8::Undefined()));
2616 QCOMPARE(o.error(), false);
2617 QCOMPARE(o.invoked(), 17);
2618 QCOMPARE(o.actuals().count(), 2);
2619 QCOMPARE(o.actuals().at(0), QVariant(10));
2620 QCOMPARE(o.actuals().at(1), QVariant(11));
2623 QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", v8::Undefined()));
2624 QCOMPARE(o.error(), false);
2625 QCOMPARE(o.invoked(), 18);
2626 QCOMPARE(o.actuals().count(), 1);
2627 QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2630 QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", v8::Undefined()));
2631 QCOMPARE(o.error(), false);
2632 QCOMPARE(o.invoked(), 19);
2633 QCOMPARE(o.actuals().count(), 1);
2634 QCOMPARE(o.actuals().at(0), QVariant(9));
2637 QVERIFY(EVALUATE_VALUE("object.method_default(10)", v8::Integer::New(19)));
2638 QCOMPARE(o.error(), false);
2639 QCOMPARE(o.invoked(), 20);
2640 QCOMPARE(o.actuals().count(), 2);
2641 QCOMPARE(o.actuals().at(0), QVariant(10));
2642 QCOMPARE(o.actuals().at(1), QVariant(19));
2645 QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", v8::Integer::New(13)));
2646 QCOMPARE(o.error(), false);
2647 QCOMPARE(o.invoked(), 20);
2648 QCOMPARE(o.actuals().count(), 2);
2649 QCOMPARE(o.actuals().at(0), QVariant(10));
2650 QCOMPARE(o.actuals().at(1), QVariant(13));
2653 QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", v8::Undefined()));
2654 QCOMPARE(o.error(), false);
2655 QCOMPARE(o.invoked(), -3);
2656 QCOMPARE(o.actuals().count(), 1);
2657 QCOMPARE(o.actuals().at(0), QVariant(9));
2660 QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", v8::Undefined()));
2661 QCOMPARE(o.error(), false);
2662 QCOMPARE(o.invoked(), 21);
2663 QCOMPARE(o.actuals().count(), 2);
2664 QCOMPARE(o.actuals().at(0), QVariant(9));
2665 QCOMPARE(o.actuals().at(1), QVariant());
2668 QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", v8::Undefined()));
2669 QCOMPARE(o.error(), false);
2670 QCOMPARE(o.invoked(), 21);
2671 QCOMPARE(o.actuals().count(), 2);
2672 QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2673 QCOMPARE(o.actuals().at(1), QVariant(QString("World")));
2676 QVERIFY(EVALUATE_VALUE("object.method_QJsonObject({foo:123})", v8::Undefined()));
2677 QCOMPARE(o.error(), false);
2678 QCOMPARE(o.invoked(), 22);
2679 QCOMPARE(o.actuals().count(), 1);
2680 QCOMPARE(qvariant_cast<QJsonObject>(o.actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2683 QVERIFY(EVALUATE_VALUE("object.method_QJsonArray([123])", v8::Undefined()));
2684 QCOMPARE(o.error(), false);
2685 QCOMPARE(o.invoked(), 23);
2686 QCOMPARE(o.actuals().count(), 1);
2687 QCOMPARE(qvariant_cast<QJsonArray>(o.actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2690 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(123)", v8::Undefined()));
2691 QCOMPARE(o.error(), false);
2692 QCOMPARE(o.invoked(), 24);
2693 QCOMPARE(o.actuals().count(), 1);
2694 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(123));
2697 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(42.35)", v8::Undefined()));
2698 QCOMPARE(o.error(), false);
2699 QCOMPARE(o.invoked(), 24);
2700 QCOMPARE(o.actuals().count(), 1);
2701 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(42.35));
2704 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue('ciao')", v8::Undefined()));
2705 QCOMPARE(o.error(), false);
2706 QCOMPARE(o.invoked(), 24);
2707 QCOMPARE(o.actuals().count(), 1);
2708 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QStringLiteral("ciao")));
2711 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(true)", v8::Undefined()));
2712 QCOMPARE(o.error(), false);
2713 QCOMPARE(o.invoked(), 24);
2714 QCOMPARE(o.actuals().count(), 1);
2715 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(true));
2718 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(false)", v8::Undefined()));
2719 QCOMPARE(o.error(), false);
2720 QCOMPARE(o.invoked(), 24);
2721 QCOMPARE(o.actuals().count(), 1);
2722 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(false));
2725 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(null)", v8::Undefined()));
2726 QCOMPARE(o.error(), false);
2727 QCOMPARE(o.invoked(), 24);
2728 QCOMPARE(o.actuals().count(), 1);
2729 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Null));
2732 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(undefined)", v8::Undefined()));
2733 QCOMPARE(o.error(), false);
2734 QCOMPARE(o.invoked(), 24);
2735 QCOMPARE(o.actuals().count(), 1);
2736 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2739 QVERIFY(EVALUATE_VALUE("object.method_overload({foo:123})", v8::Undefined()));
2740 QCOMPARE(o.error(), false);
2741 QCOMPARE(o.invoked(), 25);
2742 QCOMPARE(o.actuals().count(), 1);
2743 QCOMPARE(qvariant_cast<QJsonObject>(o.actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2746 QVERIFY(EVALUATE_VALUE("object.method_overload([123])", v8::Undefined()));
2747 QCOMPARE(o.error(), false);
2748 QCOMPARE(o.invoked(), 26);
2749 QCOMPARE(o.actuals().count(), 1);
2750 QCOMPARE(qvariant_cast<QJsonArray>(o.actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2753 QVERIFY(EVALUATE_VALUE("object.method_overload(null)", v8::Undefined()));
2754 QCOMPARE(o.error(), false);
2755 QCOMPARE(o.invoked(), 27);
2756 QCOMPARE(o.actuals().count(), 1);
2757 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Null));
2760 QVERIFY(EVALUATE_VALUE("object.method_overload(undefined)", v8::Undefined()));
2761 QCOMPARE(o.error(), false);
2762 QCOMPARE(o.invoked(), 27);
2763 QCOMPARE(o.actuals().count(), 1);
2764 QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2767 QVERIFY(EVALUATE_ERROR("object.method_unknown(null)"));
2768 QCOMPARE(o.error(), false);
2769 QCOMPARE(o.invoked(), -1);
2770 QCOMPARE(o.actuals().count(), 0);
2773 // QTBUG-13047 (check that you can pass registered object types as args)
2774 void tst_qqmlecmascript::invokableObjectArg()
2776 QQmlComponent component(&engine, testFileUrl("invokableObjectArg.qml"));
2778 QObject *o = component.create();
2780 MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o);
2782 QCOMPARE(qmlobject->myinvokableObject, qmlobject);
2787 // QTBUG-13047 (check that you can return registered object types from methods)
2788 void tst_qqmlecmascript::invokableObjectRet()
2790 QQmlComponent component(&engine, testFileUrl("invokableObjectRet.qml"));
2792 QObject *o = component.create();
2794 QCOMPARE(o->property("test").toBool(), true);
2798 void tst_qqmlecmascript::invokableEnumRet()
2800 QQmlComponent component(&engine, testFileUrl("invokableEnumRet.qml"));
2802 QObject *o = component.create();
2804 QCOMPARE(o->property("test").toBool(), true);
2809 void tst_qqmlecmascript::listToVariant()
2811 QQmlComponent component(&engine, testFileUrl("listToVariant.qml"));
2813 MyQmlContainer container;
2815 QQmlContext context(engine.rootContext());
2816 context.setContextObject(&container);
2818 QObject *object = component.create(&context);
2819 QVERIFY(object != 0);
2821 QVariant v = object->property("test");
2822 QCOMPARE(v.userType(), qMetaTypeId<QQmlListReference>());
2823 QVERIFY(qvariant_cast<QQmlListReference>(v).object() == &container);
2829 Q_DECLARE_METATYPE(QQmlListProperty<MyQmlObject>)
2830 void tst_qqmlecmascript::listAssignment()
2832 QQmlComponent component(&engine, testFileUrl("listAssignment.qml"));
2833 QObject *obj = component.create();
2834 QCOMPARE(obj->property("list1length").toInt(), 2);
2835 QQmlListProperty<MyQmlObject> list1 = obj->property("list1").value<QQmlListProperty<MyQmlObject> >();
2836 QQmlListProperty<MyQmlObject> list2 = obj->property("list2").value<QQmlListProperty<MyQmlObject> >();
2837 QCOMPARE(list1.count(&list1), list2.count(&list2));
2838 QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0));
2839 QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1));
2844 void tst_qqmlecmascript::multiEngineObject()
2847 obj.setStringProperty("Howdy planet");
2850 e1.rootContext()->setContextProperty("thing", &obj);
2851 QQmlComponent c1(&e1, testFileUrl("multiEngineObject.qml"));
2854 e2.rootContext()->setContextProperty("thing", &obj);
2855 QQmlComponent c2(&e2, testFileUrl("multiEngineObject.qml"));
2857 QObject *o1 = c1.create();
2858 QObject *o2 = c2.create();
2860 QCOMPARE(o1->property("test").toString(), QString("Howdy planet"));
2861 QCOMPARE(o2->property("test").toString(), QString("Howdy planet"));
2867 // Test that references to QObjects are cleanup when the object is destroyed
2868 void tst_qqmlecmascript::deletedObject()
2870 QQmlComponent component(&engine, testFileUrl("deletedObject.qml"));
2872 QObject *object = component.create();
2874 QCOMPARE(object->property("test1").toBool(), true);
2875 QCOMPARE(object->property("test2").toBool(), true);
2876 QCOMPARE(object->property("test3").toBool(), true);
2877 QCOMPARE(object->property("test4").toBool(), true);
2882 void tst_qqmlecmascript::attachedPropertyScope()
2884 QQmlComponent component(&engine, testFileUrl("attachedPropertyScope.qml"));
2886 QObject *object = component.create();
2887 QVERIFY(object != 0);
2889 MyQmlAttachedObject *attached =
2890 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
2891 QVERIFY(attached != 0);
2893 QCOMPARE(object->property("value2").toInt(), 0);
2895 attached->emitMySignal();
2897 QCOMPARE(object->property("value2").toInt(), 9);
2902 void tst_qqmlecmascript::scriptConnect()
2905 QQmlComponent component(&engine, testFileUrl("scriptConnect.1.qml"));
2907 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2908 QVERIFY(object != 0);
2910 QCOMPARE(object->property("test").toBool(), false);
2911 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2912 QCOMPARE(object->property("test").toBool(), true);
2918 QQmlComponent component(&engine, testFileUrl("scriptConnect.2.qml"));
2920 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2921 QVERIFY(object != 0);
2923 QCOMPARE(object->property("test").toBool(), false);
2924 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2925 QCOMPARE(object->property("test").toBool(), true);
2931 QQmlComponent component(&engine, testFileUrl("scriptConnect.3.qml"));
2933 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2934 QVERIFY(object != 0);
2936 QCOMPARE(object->property("test").toBool(), false);
2937 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2938 QCOMPARE(object->property("test").toBool(), true);
2944 QQmlComponent component(&engine, testFileUrl("scriptConnect.4.qml"));
2946 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2947 QVERIFY(object != 0);
2949 QCOMPARE(object->methodCalled(), false);
2950 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2951 QCOMPARE(object->methodCalled(), true);
2957 QQmlComponent component(&engine, testFileUrl("scriptConnect.5.qml"));
2959 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2960 QVERIFY(object != 0);
2962 QCOMPARE(object->methodCalled(), false);
2963 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2964 QCOMPARE(object->methodCalled(), true);
2970 QQmlComponent component(&engine, testFileUrl("scriptConnect.6.qml"));
2972 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2973 QVERIFY(object != 0);
2975 QCOMPARE(object->property("test").toInt(), 0);
2976 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2977 QCOMPARE(object->property("test").toInt(), 2);
2983 void tst_qqmlecmascript::scriptDisconnect()
2986 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.1.qml"));
2988 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2989 QVERIFY(object != 0);
2991 QCOMPARE(object->property("test").toInt(), 0);
2992 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2993 QCOMPARE(object->property("test").toInt(), 1);
2994 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2995 QCOMPARE(object->property("test").toInt(), 2);
2996 emit object->basicSignal();
2997 QCOMPARE(object->property("test").toInt(), 2);
2998 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2999 QCOMPARE(object->property("test").toInt(), 2);
3005 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.2.qml"));
3007 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3008 QVERIFY(object != 0);
3010 QCOMPARE(object->property("test").toInt(), 0);
3011 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3012 QCOMPARE(object->property("test").toInt(), 1);
3013 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3014 QCOMPARE(object->property("test").toInt(), 2);
3015 emit object->basicSignal();
3016 QCOMPARE(object->property("test").toInt(), 2);
3017 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3018 QCOMPARE(object->property("test").toInt(), 2);
3024 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.3.qml"));
3026 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3027 QVERIFY(object != 0);
3029 QCOMPARE(object->property("test").toInt(), 0);
3030 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3031 QCOMPARE(object->property("test").toInt(), 1);
3032 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3033 QCOMPARE(object->property("test").toInt(), 2);
3034 emit object->basicSignal();
3035 QCOMPARE(object->property("test").toInt(), 2);
3036 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3037 QCOMPARE(object->property("test").toInt(), 3);
3042 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.4.qml"));
3044 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3045 QVERIFY(object != 0);
3047 QCOMPARE(object->property("test").toInt(), 0);
3048 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3049 QCOMPARE(object->property("test").toInt(), 1);
3050 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3051 QCOMPARE(object->property("test").toInt(), 2);
3052 emit object->basicSignal();
3053 QCOMPARE(object->property("test").toInt(), 2);
3054 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3055 QCOMPARE(object->property("test").toInt(), 3);
3061 class OwnershipObject : public QObject
3065 OwnershipObject() { object = new QObject; }
3067 QPointer<QObject> object;
3070 QObject *getObject() { return object; }
3073 void tst_qqmlecmascript::ownership()
3075 OwnershipObject own;
3076 QQmlContext *context = new QQmlContext(engine.rootContext());
3077 context->setContextObject(&own);
3080 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3082 QVERIFY(own.object != 0);
3084 QObject *object = component.create(context);
3086 engine.collectGarbage();
3088 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3089 QCoreApplication::processEvents();
3091 QVERIFY(own.object == 0);
3096 own.object = new QObject(&own);
3099 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3101 QVERIFY(own.object != 0);
3103 QObject *object = component.create(context);
3105 engine.collectGarbage();
3107 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3108 QCoreApplication::processEvents();
3110 QVERIFY(own.object != 0);
3118 class CppOwnershipReturnValue : public QObject
3122 CppOwnershipReturnValue() : value(0) {}
3123 ~CppOwnershipReturnValue() { delete value; }
3125 Q_INVOKABLE QObject *create() {
3126 value = new QObject;
3127 QQmlEngine::setObjectOwnership(value, QQmlEngine::CppOwnership);
3131 Q_INVOKABLE MyQmlObject *createQmlObject() {
3132 MyQmlObject *rv = new MyQmlObject;
3137 QPointer<QObject> value;
3141 // Test setObjectOwnership(CppOwnership) works even when there is no QQmlData
3142 void tst_qqmlecmascript::cppOwnershipReturnValue()
3144 CppOwnershipReturnValue source;
3148 engine.rootContext()->setContextProperty("source", &source);
3150 QVERIFY(source.value == 0);
3152 QQmlComponent component(&engine);
3153 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl());
3155 QObject *object = component.create();
3157 QVERIFY(object != 0);
3158 QVERIFY(source.value != 0);
3163 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3164 QCoreApplication::processEvents();
3166 QVERIFY(source.value != 0);
3170 void tst_qqmlecmascript::ownershipCustomReturnValue()
3172 CppOwnershipReturnValue source;
3176 engine.rootContext()->setContextProperty("source", &source);
3178 QVERIFY(source.value == 0);
3180 QQmlComponent component(&engine);
3181 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl());
3183 QObject *object = component.create();
3185 QVERIFY(object != 0);
3186 QVERIFY(source.value != 0);
3191 engine.collectGarbage();
3192 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3193 QCoreApplication::processEvents();
3195 QVERIFY(source.value == 0);
3198 //the return value from getObject will be JS ownership,
3199 //unless strong Cpp ownership has been set
3200 class OwnershipChangingObject : public QObject
3204 OwnershipChangingObject(): object(0) { }
3206 QPointer<QObject> object;
3209 QObject *getObject() { return object; }
3210 void setObject(QObject *obj) { object = obj; }
3213 void tst_qqmlecmascript::ownershipRootObject()
3215 OwnershipChangingObject own;
3216 QQmlContext *context = new QQmlContext(engine.rootContext());
3217 context->setContextObject(&own);
3219 QQmlComponent component(&engine, testFileUrl("ownershipRootObject.qml"));
3220 QQmlGuard<QObject> object = component.create(context);
3223 engine.collectGarbage();
3225 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3226 QCoreApplication::processEvents();
3228 QVERIFY(own.object != 0);
3234 void tst_qqmlecmascript::ownershipConsistency()
3236 OwnershipChangingObject own;
3237 QQmlContext *context = new QQmlContext(engine.rootContext());
3238 context->setContextObject(&own);
3240 QString expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":19: Error: Invalid attempt to destroy() an indestructible object");
3241 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3242 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":15: Error: Invalid attempt to destroy() an indestructible object");
3243 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3244 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":6: Error: Invalid attempt to destroy() an indestructible object");
3245 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3246 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":10: Error: Invalid attempt to destroy() an indestructible object");
3247 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3249 QQmlComponent component(&engine, testFileUrl("ownershipConsistency.qml"));
3250 QQmlGuard<QObject> object = component.create(context);
3253 engine.collectGarbage();
3255 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3256 QCoreApplication::processEvents();
3258 QVERIFY(own.object != 0);
3264 void tst_qqmlecmascript::ownershipQmlIncubated()
3266 QQmlComponent component(&engine, testFileUrl("ownershipQmlIncubated.qml"));
3267 QObject *object = component.create();
3270 QTRY_VERIFY(object->property("incubatedItem").value<QObject*>() != 0);
3272 QMetaObject::invokeMethod(object, "deleteIncubatedItem");
3274 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3275 QCoreApplication::processEvents();
3277 QVERIFY(object->property("incubatedItem").value<QObject*>() == 0);
3282 class QListQObjectMethodsObject : public QObject
3286 QListQObjectMethodsObject() {
3287 m_objects.append(new MyQmlObject());
3288 m_objects.append(new MyQmlObject());
3291 ~QListQObjectMethodsObject() {
3292 qDeleteAll(m_objects);
3296 QList<QObject *> getObjects() { return m_objects; }
3299 QList<QObject *> m_objects;
3302 // Tests that returning a QList<QObject*> from a method works
3303 void tst_qqmlecmascript::qlistqobjectMethods()
3305 QListQObjectMethodsObject obj;
3306 QQmlContext *context = new QQmlContext(engine.rootContext());
3307 context->setContextObject(&obj);
3309 QQmlComponent component(&engine, testFileUrl("qlistqobjectMethods.qml"));
3311 QObject *object = component.create(context);
3313 QCOMPARE(object->property("test").toInt(), 2);
3314 QCOMPARE(object->property("test2").toBool(), true);
3321 void tst_qqmlecmascript::strictlyEquals()
3323 QQmlComponent component(&engine, testFileUrl("strictlyEquals.qml"));
3325 QObject *object = component.create();
3326 QVERIFY(object != 0);
3328 QCOMPARE(object->property("test1").toBool(), true);
3329 QCOMPARE(object->property("test2").toBool(), true);
3330 QCOMPARE(object->property("test3").toBool(), true);
3331 QCOMPARE(object->property("test4").toBool(), true);
3332 QCOMPARE(object->property("test5").toBool(), true);
3333 QCOMPARE(object->property("test6").toBool(), true);
3334 QCOMPARE(object->property("test7").toBool(), true);
3335 QCOMPARE(object->property("test8").toBool(), true);
3340 void tst_qqmlecmascript::compiled()
3342 QQmlComponent component(&engine, testFileUrl("compiled.qml"));
3344 QObject *object = component.create();
3345 QVERIFY(object != 0);
3347 QCOMPARE(object->property("test1").toReal(), qreal(15.7));
3348 QCOMPARE(object->property("test2").toReal(), qreal(-6.7));
3349 QCOMPARE(object->property("test3").toBool(), true);
3350 QCOMPARE(object->property("test4").toBool(), false);
3351 QCOMPARE(object->property("test5").toBool(), false);
3352 QCOMPARE(object->property("test6").toBool(), true);
3354 QCOMPARE(object->property("test7").toInt(), 185);
3355 QCOMPARE(object->property("test8").toInt(), 167);
3356 QCOMPARE(object->property("test9").toBool(), true);
3357 QCOMPARE(object->property("test10").toBool(), false);
3358 QCOMPARE(object->property("test11").toBool(), false);
3359 QCOMPARE(object->property("test12").toBool(), true);
3361 QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld"));
3362 QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World"));
3363 QCOMPARE(object->property("test15").toBool(), false);
3364 QCOMPARE(object->property("test16").toBool(), true);
3366 QCOMPARE(object->property("test17").toInt(), 5);
3367 QCOMPARE(object->property("test18").toReal(), qreal(176));
3368 QCOMPARE(object->property("test19").toInt(), 7);
3369 QCOMPARE(object->property("test20").toReal(), qreal(6.7));
3370 QCOMPARE(object->property("test21").toString(), QLatin1String("6.7"));
3371 QCOMPARE(object->property("test22").toString(), QLatin1String("!"));
3372 QCOMPARE(object->property("test23").toBool(), true);
3373 QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33));
3374 QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA));
3379 // Test that numbers assigned in bindings as strings work consistently
3380 void tst_qqmlecmascript::numberAssignment()
3382 QQmlComponent component(&engine, testFileUrl("numberAssignment.qml"));
3384 QObject *object = component.create();
3385 QVERIFY(object != 0);
3387 QCOMPARE(object->property("test1"), QVariant((qreal)6.7));
3388 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3389 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3390 QCOMPARE(object->property("test3"), QVariant((qreal)6));
3391 QCOMPARE(object->property("test4"), QVariant((qreal)6));
3393 QCOMPARE(object->property("test5"), QVariant((int)7));
3394 QCOMPARE(object->property("test6"), QVariant((int)7));
3395 QCOMPARE(object->property("test7"), QVariant((int)6));
3396 QCOMPARE(object->property("test8"), QVariant((int)6));
3398 QCOMPARE(object->property("test9"), QVariant((unsigned int)7));
3399 QCOMPARE(object->property("test10"), QVariant((unsigned int)7));
3400 QCOMPARE(object->property("test11"), QVariant((unsigned int)6));
3401 QCOMPARE(object->property("test12"), QVariant((unsigned int)6));
3406 void tst_qqmlecmascript::propertySplicing()
3408 QQmlComponent component(&engine, testFileUrl("propertySplicing.qml"));
3410 QObject *object = component.create();
3411 QVERIFY(object != 0);
3413 QCOMPARE(object->property("test").toBool(), true);
3419 void tst_qqmlecmascript::signalWithUnknownTypes()
3421 QQmlComponent component(&engine, testFileUrl("signalWithUnknownTypes.qml"));
3423 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3424 QVERIFY(object != 0);
3426 MyQmlObject::MyType type;
3427 type.value = 0x8971123;
3428 emit object->signalWithUnknownType(type);
3430 MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant());
3432 QCOMPARE(result.value, type.value);
3438 void tst_qqmlecmascript::signalWithJSValueInVariant_data()
3440 QTest::addColumn<QString>("expression");
3441 QTest::addColumn<QString>("compare");
3443 QString compareStrict("(function(a, b) { return a === b; })");
3444 QTest::newRow("true") << "true" << compareStrict;
3445 QTest::newRow("undefined") << "undefined" << compareStrict;
3446 QTest::newRow("null") << "null" << compareStrict;
3447 QTest::newRow("123") << "123" << compareStrict;
3448 QTest::newRow("'ciao'") << "'ciao'" << compareStrict;
3450 QString comparePropertiesStrict(
3452 " if (typeof b != 'object')"
3454 " var props = Object.getOwnPropertyNames(b);"
3455 " for (var i = 0; i < props.length; ++i) {"
3456 " var p = props[i];"
3457 " return arguments.callee(a[p], b[p]);"
3460 QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })" << comparePropertiesStrict;
3461 QTest::newRow("[10,20,30]") << "[10,20,30]" << comparePropertiesStrict;
3464 void tst_qqmlecmascript::signalWithJSValueInVariant()
3466 QFETCH(QString, expression);
3467 QFETCH(QString, compare);
3469 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3470 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3471 QVERIFY(object != 0);
3473 QJSValue value = engine.evaluate(expression);
3474 QVERIFY(!value.isError());
3475 object->setProperty("expression", expression);
3476 object->setProperty("compare", compare);
3477 object->setProperty("pass", false);
3479 emit object->signalWithVariant(QVariant::fromValue(value));
3480 QVERIFY(object->property("pass").toBool());
3483 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines_data()
3485 signalWithJSValueInVariant_data();
3488 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines()
3490 QFETCH(QString, expression);
3491 QFETCH(QString, compare);
3493 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3494 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3495 QVERIFY(object != 0);
3498 QJSValue value = engine2.evaluate(expression);
3499 QVERIFY(!value.isError());
3500 object->setProperty("expression", expression);
3501 object->setProperty("compare", compare);
3502 object->setProperty("pass", false);
3504 QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine.");
3505 emit object->signalWithVariant(QVariant::fromValue(value));
3506 QVERIFY(!object->property("pass").toBool());
3509 void tst_qqmlecmascript::signalWithQJSValue_data()
3511 signalWithJSValueInVariant_data();
3514 void tst_qqmlecmascript::signalWithQJSValue()
3516 QFETCH(QString, expression);
3517 QFETCH(QString, compare);
3519 QQmlComponent component(&engine, testFileUrl("signalWithQJSValue.qml"));
3520 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3521 QVERIFY(object != 0);
3523 QJSValue value = engine.evaluate(expression);
3524 QVERIFY(!value.isError());
3525 object->setProperty("expression", expression);
3526 object->setProperty("compare", compare);
3527 object->setProperty("pass", false);
3529 emit object->signalWithQJSValue(value);
3531 QVERIFY(object->property("pass").toBool());
3532 QVERIFY(object->qjsvalue().strictlyEquals(value));
3535 void tst_qqmlecmascript::singletonType_data()
3537 QTest::addColumn<QUrl>("testfile");
3538 QTest::addColumn<QString>("errorMessage");
3539 QTest::addColumn<QStringList>("warningMessages");
3540 QTest::addColumn<QStringList>("readProperties");
3541 QTest::addColumn<QVariantList>("readExpectedValues");
3542 QTest::addColumn<QStringList>("writeProperties");
3543 QTest::addColumn<QVariantList>("writeValues");
3544 QTest::addColumn<QStringList>("readBackProperties");
3545 QTest::addColumn<QVariantList>("readBackExpectedValues");
3547 QTest::newRow("qobject, register + read + method [no qualifier]")
3548 << testFileUrl("singletontype/qobjectSingletonTypeNoQualifier.qml")
3551 << (QStringList() << "qobjectPropertyTest" << "qobjectMethodTest")
3552 << (QVariantList() << 20 << 1)
3558 QTest::newRow("script, register + read [no qualifier]")
3559 << testFileUrl("singletontype/scriptSingletonTypeNoQualifier.qml")
3562 << (QStringList() << "scriptTest")
3563 << (QVariantList() << 13)
3569 QTest::newRow("qobject, register + read + method")
3570 << testFileUrl("singletontype/qobjectSingletonType.qml")
3573 << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
3574 << "qobjectMinorVersionTest" << "qobjectMajorVersionTest" << "qobjectParentedTest")
3575 << (QVariantList() << 20 << 20 << 2 << 20 << 20 << 26)
3581 QTest::newRow("script, register + read")
3582 << testFileUrl("singletontype/scriptSingletonType.qml")
3585 << (QStringList() << "scriptTest")
3586 << (QVariantList() << 13)
3592 QTest::newRow("qobject, caching + read")
3593 << testFileUrl("singletontype/qobjectSingletonTypeCaching.qml")
3596 << (QStringList() << "existingUriTest" << "qobjectParentedTest")
3597 << (QVariantList() << 20 << 26) // 26, shouldn't have incremented to 27.
3603 QTest::newRow("script, caching + read")
3604 << testFileUrl("singletontype/scriptSingletonTypeCaching.qml")
3607 << (QStringList() << "scriptTest")
3608 << (QVariantList() << 13) // 13, shouldn't have incremented to 14.
3614 QTest::newRow("qobject, writing + readonly constraints")
3615 << testFileUrl("singletontype/qobjectSingletonTypeWriting.qml")
3617 << (QStringList() << QString(testFileUrl("singletontype/qobjectSingletonTypeWriting.qml").toString() + QLatin1String(":15: Error: Cannot assign to read-only property \"qobjectTestProperty\"")))
3618 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3619 << (QVariantList() << 20 << 50 << 10)
3620 << (QStringList() << "firstProperty" << "secondProperty")
3621 << (QVariantList() << 30 << 30)
3622 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3623 << (QVariantList() << 20 << 30 << 30);
3625 QTest::newRow("script, writing + readonly constraints")
3626 << testFileUrl("singletontype/scriptSingletonTypeWriting.qml")
3628 << (QStringList() << QString(testFileUrl("singletontype/scriptSingletonTypeWriting.qml").toString() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\"")))
3629 << (QStringList() << "readBack" << "unchanged")
3630 << (QVariantList() << 13 << 42)
3631 << (QStringList() << "firstProperty" << "secondProperty")
3632 << (QVariantList() << 30 << 30)
3633 << (QStringList() << "readBack" << "unchanged")
3634 << (QVariantList() << 30 << 42);
3636 QTest::newRow("qobject singleton Type enum values in JS")
3637 << testFileUrl("singletontype/qobjectSingletonTypeEnums.qml")
3640 << (QStringList() << "enumValue" << "enumMethod")
3641 << (QVariantList() << 42 << 30)
3647 QTest::newRow("qobject, invalid major version fail")
3648 << testFileUrl("singletontype/singletonTypeMajorVersionFail.qml")
3649 << QString("QQmlComponent: Component is not ready")
3658 QTest::newRow("qobject, invalid minor version fail")
3659 << testFileUrl("singletontype/singletonTypeMinorVersionFail.qml")
3660 << QString("QQmlComponent: Component is not ready")
3670 void tst_qqmlecmascript::singletonType()
3672 QFETCH(QUrl, testfile);
3673 QFETCH(QString, errorMessage);
3674 QFETCH(QStringList, warningMessages);
3675 QFETCH(QStringList, readProperties);
3676 QFETCH(QVariantList, readExpectedValues);
3677 QFETCH(QStringList, writeProperties);
3678 QFETCH(QVariantList, writeValues);
3679 QFETCH(QStringList, readBackProperties);
3680 QFETCH(QVariantList, readBackExpectedValues);
3682 QQmlComponent component(&engine, testfile);
3684 if (!errorMessage.isEmpty())
3685 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3687 if (warningMessages.size())
3688 foreach (const QString &warning, warningMessages)
3689 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3691 QObject *object = component.create();
3692 if (!errorMessage.isEmpty()) {
3693 QVERIFY(object == 0);
3695 QVERIFY(object != 0);
3696 for (int i = 0; i < readProperties.size(); ++i)
3697 QCOMPARE(object->property(readProperties.at(i).toLatin1().constData()), readExpectedValues.at(i));
3698 for (int i = 0; i < writeProperties.size(); ++i)
3699 QVERIFY(object->setProperty(writeProperties.at(i).toLatin1().constData(), writeValues.at(i)));
3700 for (int i = 0; i < readBackProperties.size(); ++i)
3701 QCOMPARE(object->property(readBackProperties.at(i).toLatin1().constData()), readBackExpectedValues.at(i));
3706 void tst_qqmlecmascript::singletonTypeImportOrder()
3708 QQmlComponent component(&engine, testFileUrl("singletontype/singletonTypeImportOrder.qml"));
3709 QObject *object = component.create();
3711 QVERIFY(object->property("v") == 1);
3715 void tst_qqmlecmascript::singletonTypeResolution()
3717 QQmlComponent component(&engine, testFileUrl("singletontype/singletonTypeResolution.qml"));
3718 QObject *object = component.create();
3720 QVERIFY(object->property("success") == true);
3724 void tst_qqmlecmascript::singletonTypeConflicts1()
3726 const char *warning = "Cannot register singleton type TypeName in uri Test.Conflict1 1.5 (a conflicting singleton type already exists)";
3727 QTest::ignoreMessage(QtWarningMsg, warning);
3729 int i0 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict1", 1, 5, "TypeName", 0);
3732 int i1 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict1", 2, 0, "TypeName", 0);
3735 int i2 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict1", 1, 5, "TypeName", 0);
3738 int i3 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict1", 1, 2, "TypeName", 0);
3741 int i4 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict1", 1, 8, "TypeName", 0);
3745 void tst_qqmlecmascript::singletonTypeConflicts2()
3747 int i0 = qmlRegisterType<MyQmlObject>("Test.Conflict2", 1, 5, "TypeName");
3750 int i2 = qmlRegisterType<MyQmlObject>("Test.Conflict2", 1, 8, "TypeName");
3753 int i3 = qmlRegisterType<MyQmlObject>("Test.Conflict2", 2, 0, "TypeName");
3756 int i4 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict2", 1, 0, "TypeName", 0);
3759 const char *warning2 = "Cannot register singleton type TypeName in uri Test.Conflict2 1.9 (a conflicting type already exists)";
3760 QTest::ignoreMessage(QtWarningMsg, warning2);
3762 int i5 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict2", 1, 9, "TypeName", 0);
3766 void tst_qqmlecmascript::singletonTypeConflicts3()
3768 int i0 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict3", 1, 0, "TypeName", 0);
3771 int i1 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict3", 1, 5, "TypeName", 0);
3774 int i2 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict3", 1, 8, "TypeName", 0);
3777 int i3 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict3", 2, 0, "TypeName", 0);
3780 const char *warning = "Cannot register type TypeName in uri Test.Conflict3 1.0 (a conflicting singleton type already exists)";
3781 QTest::ignoreMessage(QtWarningMsg, warning);
3783 int i4 = qmlRegisterType<MyQmlObject>("Test.Conflict3", 1, 0, "TypeName");
3786 int i5 = qmlRegisterType<MyQmlObject>("Test.Conflict3", 1, 3, "TypeName");
3790 void tst_qqmlecmascript::importScripts_data()
3792 QTest::addColumn<QUrl>("testfile");
3793 QTest::addColumn<QString>("errorMessage");
3794 QTest::addColumn<QStringList>("warningMessages");
3795 QTest::addColumn<QStringList>("propertyNames");
3796 QTest::addColumn<QVariantList>("propertyValues");
3798 QTest::newRow("basic functionality")
3799 << testFileUrl("jsimport/testImport.qml")
3802 << (QStringList() << QLatin1String("importedScriptStringValue")
3803 << QLatin1String("importedScriptFunctionValue")
3804 << QLatin1String("importedModuleAttachedPropertyValue")
3805 << QLatin1String("importedModuleEnumValue"))
3806 << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3811 QTest::newRow("import scoping")
3812 << testFileUrl("jsimport/testImportScoping.qml")
3815 << (QStringList() << QLatin1String("componentError"))
3816 << (QVariantList() << QVariant(5));
3818 QTest::newRow("parent scope shouldn't be inherited by import with imports")
3819 << testFileUrl("jsimportfail/failOne.qml")
3821 << (QStringList() << QString(testFileUrl("jsimportfail/failOne.qml").toString() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
3822 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3823 << (QVariantList() << QVariant(QString()));
3825 QTest::newRow("javascript imports in an import should be private to the import scope")
3826 << testFileUrl("jsimportfail/failTwo.qml")
3828 << (QStringList() << QString(testFileUrl("jsimportfail/failTwo.qml").toString() + QLatin1String(":6: ReferenceError: ImportOneJs is not defined")))
3829 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3830 << (QVariantList() << QVariant(QString()));
3832 QTest::newRow("module imports in an import should be private to the import scope")
3833 << testFileUrl("jsimportfail/failThree.qml")
3835 << (QStringList() << QString(testFileUrl("jsimportfail/failThree.qml").toString() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
3836 << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
3837 << (QVariantList() << QVariant(false));
3839 QTest::newRow("typenames in an import should be private to the import scope")
3840 << testFileUrl("jsimportfail/failFour.qml")
3842 << (QStringList() << QString(testFileUrl("jsimportfail/failFour.qml").toString() + QLatin1String(":6: ReferenceError: JsQtTest is not defined")))
3843 << (QStringList() << QLatin1String("importedModuleEnumValue"))
3844 << (QVariantList() << QVariant(0));
3846 QTest::newRow("import with imports has it's own activation scope")
3847 << testFileUrl("jsimportfail/failFive.qml")
3849 << (QStringList() << QString(testFileUrl("jsimportfail/importWithImports.js").toString() + QLatin1String(":8: ReferenceError: Component is not defined")))
3850 << (QStringList() << QLatin1String("componentError"))
3851 << (QVariantList() << QVariant(0));
3853 QTest::newRow("import pragma library script")
3854 << testFileUrl("jsimport/testImportPragmaLibrary.qml")
3857 << (QStringList() << QLatin1String("testValue"))
3858 << (QVariantList() << QVariant(31));
3860 QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3861 << testFileUrl("jsimportfail/testImportPragmaLibrary.qml")
3863 << (QStringList() << QString(testFileUrl("jsimportfail/importPragmaLibrary.js").toString() + QLatin1String(":6: ReferenceError: Component is not defined")))
3864 << (QStringList() << QLatin1String("testValue"))
3865 << (QVariantList() << QVariant(0));
3867 QTest::newRow("import pragma library script which has an import")
3868 << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
3871 << (QStringList() << QLatin1String("testValue"))
3872 << (QVariantList() << QVariant(55));
3874 QTest::newRow("import pragma library script which has a pragma library import")
3875 << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3878 << (QStringList() << QLatin1String("testValue"))
3879 << (QVariantList() << QVariant(18));
3881 QTest::newRow("import singleton type into js import")
3882 << testFileUrl("jsimport/testImportSingletonType.qml")
3885 << (QStringList() << QLatin1String("testValue"))
3886 << (QVariantList() << QVariant(20));
3888 QTest::newRow("import module which exports a script")
3889 << testFileUrl("jsimport/testJsImport.qml")
3892 << (QStringList() << QLatin1String("importedScriptStringValue")
3893 << QLatin1String("renamedScriptStringValue")
3894 << QLatin1String("reimportedScriptStringValue"))
3895 << (QVariantList() << QVariant(QString("Hello"))
3896 << QVariant(QString("Hello"))
3897 << QVariant(QString("Hello")));
3899 QTest::newRow("import module which exports a script which imports a remote module")
3900 << testFileUrl("jsimport/testJsRemoteImport.qml")
3903 << (QStringList() << QLatin1String("importedScriptStringValue")
3904 << QLatin1String("renamedScriptStringValue")
3905 << QLatin1String("reimportedScriptStringValue"))
3906 << (QVariantList() << QVariant(QString("Hello"))
3907 << QVariant(QString("Hello"))
3908 << QVariant(QString("Hello")));
3910 QTest::newRow("malformed import statement")
3911 << testFileUrl("jsimportfail/malformedImport.qml")
3913 << (QStringList() << testFileUrl("jsimportfail/malformedImport.js").toString() + QLatin1String(":1: SyntaxError: Unexpected token ."))
3917 QTest::newRow("malformed file name")
3918 << testFileUrl("jsimportfail/malformedFile.qml")
3920 << (QStringList() << testFileUrl("jsimportfail/malformedFile.js").toString() + QLatin1String(":0:1: Imported file must be a script"))
3924 QTest::newRow("missing file qualifier")
3925 << testFileUrl("jsimportfail/missingFileQualifier.qml")
3927 << (QStringList() << testFileUrl("jsimportfail/missingFileQualifier.js").toString() + QLatin1String(":0:1: File import requires a qualifier"))
3931 QTest::newRow("malformed file qualifier")
3932 << testFileUrl("jsimportfail/malformedFileQualifier.qml")
3934 << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.js").toString() + QLatin1String(":0:1: File import requires a qualifier"))
3938 QTest::newRow("malformed module qualifier 2")
3939 << testFileUrl("jsimportfail/malformedFileQualifier.2.qml")
3941 << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.2.js").toString() + QLatin1String(":0:1: Invalid import qualifier"))
3945 QTest::newRow("malformed module uri")
3946 << testFileUrl("jsimportfail/malformedModule.qml")
3948 << (QStringList() << testFileUrl("jsimportfail/malformedModule.js").toString() + QLatin1String(":0:1: Invalid module URI"))
3952 QTest::newRow("missing module version")
3953 << testFileUrl("jsimportfail/missingModuleVersion.qml")
3955 << (QStringList() << testFileUrl("jsimportfail/missingModuleVersion.js").toString() + QLatin1String(":0:1: Module import requires a version"))
3959 QTest::newRow("malformed module version")
3960 << testFileUrl("jsimportfail/malformedModuleVersion.qml")
3962 << (QStringList() << testFileUrl("jsimportfail/malformedModuleVersion.js").toString() + QLatin1String(":0:1: Module import requires a version"))
3966 QTest::newRow("missing module qualifier")
3967 << testFileUrl("jsimportfail/missingModuleQualifier.qml")
3969 << (QStringList() << testFileUrl("jsimportfail/missingModuleQualifier.js").toString() + QLatin1String(":0:1: Module import requires a qualifier"))
3973 QTest::newRow("malformed module qualifier")
3974 << testFileUrl("jsimportfail/malformedModuleQualifier.qml")
3976 << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.js").toString() + QLatin1String(":0:1: Module import requires a qualifier"))
3980 QTest::newRow("malformed module qualifier 2")
3981 << testFileUrl("jsimportfail/malformedModuleQualifier.2.qml")
3983 << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.2.js").toString() + QLatin1String(":0:1: Invalid import qualifier"))
3988 void tst_qqmlecmascript::importScripts()
3990 QFETCH(QUrl, testfile);
3991 QFETCH(QString, errorMessage);
3992 QFETCH(QStringList, warningMessages);
3993 QFETCH(QStringList, propertyNames);
3994 QFETCH(QVariantList, propertyValues);
3996 TestHTTPServer server(8111);
3997 QVERIFY(server.isValid());
3998 server.serveDirectory(dataDirectory() + "/remote");
4000 QStringList importPathList = engine.importPathList();
4002 QString remotePath(QLatin1String("http://127.0.0.1:8111/"));
4003 engine.addImportPath(remotePath);
4005 QQmlComponent component(&engine, testfile);
4007 if (!errorMessage.isEmpty())
4008 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
4010 if (warningMessages.size())
4011 foreach (const QString &warning, warningMessages)
4012 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
4014 QTRY_VERIFY(component.isReady());
4016 QObject *object = component.create();
4017 if (!errorMessage.isEmpty()) {
4018 QVERIFY(object == 0);
4020 QVERIFY(object != 0);
4021 for (int i = 0; i < propertyNames.size(); ++i)
4022 QCOMPARE(object->property(propertyNames.at(i).toLatin1().constData()), propertyValues.at(i));
4026 engine.setImportPathList(importPathList);
4029 void tst_qqmlecmascript::scarceResources_other()
4031 /* These tests require knowledge of state, since we test values after
4032 performing signal or function invocation. */
4034 QPixmap origPixmap(100, 100);
4035 origPixmap.fill(Qt::blue);
4036 QString srp_name, expectedWarning;
4037 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4038 ScarceResourceObject *eo = 0;
4040 QObject *object = 0;
4042 /* property var semantics */
4044 // test that scarce resources are handled properly in signal invocation
4045 QQmlComponent varComponentTen(&engine, testFileUrl("scarceResourceSignal.var.qml"));
4046 object = varComponentTen.create();
4047 srsc = object->findChild<QObject*>("srsc");
4049 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
4050 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
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 QMetaObject::invokeMethod(srsc, "testSignal");
4054 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
4055 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
4056 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4057 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
4058 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
4059 QVERIFY(srsc->property("scarceResourceCopy").isValid());
4060 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4061 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4062 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
4063 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4066 // test that scarce resources are handled properly from js functions in qml files
4067 QQmlComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml"));
4068 object = varComponentEleven.create();
4069 QVERIFY(object != 0);
4070 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4071 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4072 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4073 QMetaObject::invokeMethod(object, "retrieveScarceResource");
4074 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
4075 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4076 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4077 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
4078 QMetaObject::invokeMethod(object, "releaseScarceResource");
4079 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
4080 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4081 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4082 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4085 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
4086 QQmlComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
4087 object = varComponentTwelve.create();
4088 QVERIFY(object != 0);
4089 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4090 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4091 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4092 srp_name = object->property("srp_name").toString();
4093 expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
4094 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
4095 QMetaObject::invokeMethod(object, "retrieveScarceResource");
4096 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
4097 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4098 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4099 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4102 // test that if an Item which has JS ownership but has a scarce resource property is garbage collected,
4103 // that the scarce resource is removed from the engine's list of scarce resources to clean up.
4104 QQmlComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml"));
4105 object = varComponentThirteen.create();
4106 QVERIFY(object != 0);
4107 QVERIFY(!object->property("varProperty").isValid()); // not assigned yet
4108 QMetaObject::invokeMethod(object, "assignVarProperty");
4109 QVERIFY(ep->scarceResources.isEmpty()); // the scarce resource is a VME property.
4110 QMetaObject::invokeMethod(object, "deassignVarProperty");
4111 QVERIFY(ep->scarceResources.isEmpty()); // should still be empty; the resource should have been released on gc.
4114 /* property variant semantics */
4116 // test that scarce resources are handled properly in signal invocation
4117 QQmlComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml"));
4118 object = variantComponentTen.create();
4119 QVERIFY(object != 0);
4120 srsc = object->findChild<QObject*>("srsc");
4122 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
4123 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
4124 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4125 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4126 QMetaObject::invokeMethod(srsc, "testSignal");
4127 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
4128 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
4129 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4130 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
4131 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
4132 QVERIFY(srsc->property("scarceResourceCopy").isValid());
4133 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4134 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4135 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
4136 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4139 // test that scarce resources are handled properly from js functions in qml files
4140 QQmlComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml"));
4141 object = variantComponentEleven.create();
4142 QVERIFY(object != 0);
4143 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4144 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4145 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4146 QMetaObject::invokeMethod(object, "retrieveScarceResource");
4147 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
4148 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4149 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4150 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
4151 QMetaObject::invokeMethod(object, "releaseScarceResource");
4152 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
4153 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4154 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4155 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4158 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
4159 QQmlComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml"));
4160 object = variantComponentTwelve.create();
4161 QVERIFY(object != 0);
4162 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4163 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4164 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4165 srp_name = object->property("srp_name").toString();
4166 expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
4167 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
4168 QMetaObject::invokeMethod(object, "retrieveScarceResource");
4169 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
4170 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4171 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4172 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4176 void tst_qqmlecmascript::scarceResources_data()
4178 QTest::addColumn<QUrl>("qmlFile");
4179 QTest::addColumn<bool>("readDetachStatus");
4180 QTest::addColumn<bool>("expectedDetachStatus");
4181 QTest::addColumn<QStringList>("propertyNames");
4182 QTest::addColumn<QVariantList>("expectedValidity");
4183 QTest::addColumn<QVariantList>("expectedValues");
4184 QTest::addColumn<QStringList>("expectedErrors");
4186 QPixmap origPixmap(100, 100);
4187 origPixmap.fill(Qt::blue);
4189 /* property var semantics */
4191 // in the following three cases, the instance created from the component
4192 // has a property which is a copy of the scarce resource; hence, the
4193 // resource should NOT be detached prior to deletion of the object instance,
4194 // unless the resource is destroyed explicitly.
4195 QTest::newRow("var: import scarce resource copy directly")
4196 << testFileUrl("scarceResourceCopy.var.qml")
4198 << false // won't be detached, because assigned to property and not explicitly released
4199 << (QStringList() << QLatin1String("scarceResourceCopy"))
4200 << (QList<QVariant>() << true)
4201 << (QList<QVariant>() << origPixmap)
4204 QTest::newRow("var: import scarce resource copy from JS")
4205 << testFileUrl("scarceResourceCopyFromJs.var.qml")
4207 << false // won't be detached, because assigned to property and not explicitly released
4208 << (QStringList() << QLatin1String("scarceResourceCopy"))
4209 << (QList<QVariant>() << true)
4210 << (QList<QVariant>() << origPixmap)
4213 QTest::newRow("var: import released scarce resource copy from JS")
4214 << testFileUrl("scarceResourceDestroyedCopy.var.qml")
4216 << true // explicitly released, so it will be detached
4217 << (QStringList() << QLatin1String("scarceResourceCopy"))
4218 << (QList<QVariant>() << false)
4219 << (QList<QVariant>() << QVariant())
4222 // in the following three cases, no other copy should exist in memory,
4223 // and so it should be detached (unless explicitly preserved).
4224 QTest::newRow("var: import auto-release SR from JS in binding side-effect")
4225 << testFileUrl("scarceResourceTest.var.qml")
4227 << true // auto released, so it will be detached
4228 << (QStringList() << QLatin1String("scarceResourceTest"))
4229 << (QList<QVariant>() << true)
4230 << (QList<QVariant>() << QVariant(100))
4232 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4233 << testFileUrl("scarceResourceTestPreserve.var.qml")
4235 << false // won't be detached because we explicitly preserve it
4236 << (QStringList() << QLatin1String("scarceResourceTest"))
4237 << (QList<QVariant>() << true)
4238 << (QList<QVariant>() << QVariant(100))
4240 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4241 << testFileUrl("scarceResourceTestMultiple.var.qml")
4243 << true // will be detached because all resources were released manually or automatically.
4244 << (QStringList() << QLatin1String("scarceResourceTest"))
4245 << (QList<QVariant>() << true)
4246 << (QList<QVariant>() << QVariant(100))
4249 // In the following three cases, test that scarce resources are handled
4250 // correctly for imports.
4251 QTest::newRow("var: import with no binding")
4252 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4253 << false // cannot check detach status.
4256 << QList<QVariant>()
4257 << QList<QVariant>()
4259 QTest::newRow("var: import with binding without explicit preserve")
4260 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4263 << (QStringList() << QLatin1String("scarceResourceCopy"))
4264 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4265 << (QList<QVariant>() << QVariant())
4267 QTest::newRow("var: import with explicit release after binding evaluation")
4268 << testFileUrl("scarceResourceCopyImport.var.qml")
4271 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4272 << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated.
4273 << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true))
4275 QTest::newRow("var: import with different js objects")
4276 << testFileUrl("scarceResourceCopyImportDifferent.var.qml")
4279 << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4280 << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4281 << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false))
4283 QTest::newRow("var: import with different js objects and explicit release")
4284 << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml")
4287 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4288 << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4289 << (QList<QVariant>() << QVariant(origPixmap) << QVariant())
4291 QTest::newRow("var: import with same js objects and explicit release")
4292 << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml")
4295 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4296 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4297 << (QList<QVariant>() << QVariant() << QVariant())
4299 QTest::newRow("var: binding with same js objects and explicit release")
4300 << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml")
4303 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4304 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4305 << (QList<QVariant>() << QVariant() << QVariant())
4309 /* property variant semantics */
4311 // in the following three cases, the instance created from the component
4312 // has a property which is a copy of the scarce resource; hence, the
4313 // resource should NOT be detached prior to deletion of the object instance,
4314 // unless the resource is destroyed explicitly.
4315 QTest::newRow("variant: import scarce resource copy directly")
4316 << testFileUrl("scarceResourceCopy.variant.qml")
4318 << false // won't be detached, because assigned to property and not explicitly released
4319 << (QStringList() << QLatin1String("scarceResourceCopy"))
4320 << (QList<QVariant>() << true)
4321 << (QList<QVariant>() << origPixmap)
4324 QTest::newRow("variant: import scarce resource copy from JS")
4325 << testFileUrl("scarceResourceCopyFromJs.variant.qml")
4327 << false // won't be detached, because assigned to property and not explicitly released
4328 << (QStringList() << QLatin1String("scarceResourceCopy"))
4329 << (QList<QVariant>() << true)
4330 << (QList<QVariant>() << origPixmap)
4333 QTest::newRow("variant: import released scarce resource copy from JS")
4334 << testFileUrl("scarceResourceDestroyedCopy.variant.qml")
4336 << true // explicitly released, so it will be detached
4337 << (QStringList() << QLatin1String("scarceResourceCopy"))
4338 << (QList<QVariant>() << false)
4339 << (QList<QVariant>() << QVariant())
4342 // in the following three cases, no other copy should exist in memory,
4343 // and so it should be detached (unless explicitly preserved).
4344 QTest::newRow("variant: import auto-release SR from JS in binding side-effect")
4345 << testFileUrl("scarceResourceTest.variant.qml")
4347 << true // auto released, so it will be detached
4348 << (QStringList() << QLatin1String("scarceResourceTest"))
4349 << (QList<QVariant>() << true)
4350 << (QList<QVariant>() << QVariant(100))
4352 QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect")
4353 << testFileUrl("scarceResourceTestPreserve.variant.qml")
4355 << false // won't be detached because we explicitly preserve it
4356 << (QStringList() << QLatin1String("scarceResourceTest"))
4357 << (QList<QVariant>() << true)
4358 << (QList<QVariant>() << QVariant(100))
4360 QTest::newRow("variant: import multiple scarce resources")
4361 << testFileUrl("scarceResourceTestMultiple.variant.qml")
4363 << true // will be detached because all resources were released manually or automatically.
4364 << (QStringList() << QLatin1String("scarceResourceTest"))
4365 << (QList<QVariant>() << true)
4366 << (QList<QVariant>() << QVariant(100))
4369 // In the following three cases, test that scarce resources are handled
4370 // correctly for imports.
4371 QTest::newRow("variant: import with no binding")
4372 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4373 << false // cannot check detach status.
4376 << QList<QVariant>()
4377 << QList<QVariant>()
4379 QTest::newRow("variant: import with binding without explicit preserve")
4380 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4383 << (QStringList() << QLatin1String("scarceResourceCopy"))
4384 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4385 << (QList<QVariant>() << QVariant())
4387 QTest::newRow("variant: import with explicit release after binding evaluation")
4388 << testFileUrl("scarceResourceCopyImport.variant.qml")
4391 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo"))
4392 << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies.
4393 << (QList<QVariant>() << origPixmap << origPixmap << QVariant())
4397 void tst_qqmlecmascript::scarceResources()
4399 QFETCH(QUrl, qmlFile);
4400 QFETCH(bool, readDetachStatus);
4401 QFETCH(bool, expectedDetachStatus);
4402 QFETCH(QStringList, propertyNames);
4403 QFETCH(QVariantList, expectedValidity);
4404 QFETCH(QVariantList, expectedValues);
4405 QFETCH(QStringList, expectedErrors);
4407 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4408 ScarceResourceObject *eo = 0;
4409 QObject *object = 0;
4411 QQmlComponent c(&engine, qmlFile);
4412 object = c.create();
4413 QVERIFY(object != 0);
4414 for (int i = 0; i < propertyNames.size(); ++i) {
4415 QString prop = propertyNames.at(i);
4416 bool validity = expectedValidity.at(i).toBool();
4417 QVariant value = expectedValues.at(i);
4419 QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity);
4420 if (value.type() == QVariant::Int) {
4421 QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt());
4422 } else if (value.type() == QVariant::Pixmap) {
4423 QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>());
4427 if (readDetachStatus) {
4428 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4429 QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
4432 QVERIFY(ep->scarceResources.isEmpty());
4436 void tst_qqmlecmascript::propertyChangeSlots()
4438 // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
4439 QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml"));
4440 QObject *object = component.create();
4441 QVERIFY(object != 0);
4444 // ensure that invalid property names fail properly.
4445 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4446 QQmlComponent e1(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.1.qml"));
4447 QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
4448 QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
4449 object = e1.create();
4450 QVERIFY(object == 0);
4453 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4454 QQmlComponent e2(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.2.qml"));
4455 expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
4456 QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
4457 object = e2.create();
4458 QVERIFY(object == 0);
4461 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4462 QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml"));
4463 expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
4464 QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
4465 object = e3.create();
4466 QVERIFY(object == 0);
4469 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4470 QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml"));
4471 expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
4472 QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
4473 object = e4.create();
4474 QVERIFY(object == 0);
4478 void tst_qqmlecmascript::propertyVar_data()
4480 QTest::addColumn<QUrl>("qmlFile");
4483 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyVar.1.qml");
4484 QTest::newRow("non-bindable object changed") << testFileUrl("propertyVar.2.qml");
4485 QTest::newRow("primitive changed") << testFileUrl("propertyVar.3.qml");
4486 QTest::newRow("javascript array modification") << testFileUrl("propertyVar.4.qml");
4487 QTest::newRow("javascript map modification") << testFileUrl("propertyVar.5.qml");
4488 QTest::newRow("javascript array assignment") << testFileUrl("propertyVar.6.qml");
4489 QTest::newRow("javascript map assignment") << testFileUrl("propertyVar.7.qml");
4490 QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml");
4491 QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml");
4492 QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml");
4493 QTest::newRow("javascript function assignment") << testFileUrl("propertyVar.11.qml");
4494 QTest::newRow("javascript special assignment") << testFileUrl("propertyVar.12.qml");
4495 QTest::newRow("declarative binding assignment") << testFileUrl("propertyVar.13.qml");
4496 QTest::newRow("imperative binding assignment") << testFileUrl("propertyVar.14.qml");
4497 QTest::newRow("stored binding assignment") << testFileUrl("propertyVar.15.qml");
4498 QTest::newRow("function expression binding assignment") << testFileUrl("propertyVar.16.qml");
4501 void tst_qqmlecmascript::propertyVar()
4503 QFETCH(QUrl, qmlFile);
4505 QQmlComponent component(&engine, qmlFile);
4506 QObject *object = component.create();
4507 QVERIFY(object != 0);
4509 QCOMPARE(object->property("test").toBool(), true);
4514 void tst_qqmlecmascript::propertyQJSValue_data()
4516 QTest::addColumn<QUrl>("qmlFile");
4519 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyQJSValue.1.qml");
4520 QTest::newRow("non-bindable object changed") << testFileUrl("propertyQJSValue.2.qml");
4521 QTest::newRow("primitive changed") << testFileUrl("propertyQJSValue.3.qml");
4522 QTest::newRow("javascript array modification") << testFileUrl("propertyQJSValue.4.qml");
4523 QTest::newRow("javascript map modification") << testFileUrl("propertyQJSValue.5.qml");
4524 QTest::newRow("javascript array assignment") << testFileUrl("propertyQJSValue.6.qml");
4525 QTest::newRow("javascript map assignment") << testFileUrl("propertyQJSValue.7.qml");
4526 QTest::newRow("literal property assignment") << testFileUrl("propertyQJSValue.8.qml");
4527 QTest::newRow("qobject property assignment") << testFileUrl("propertyQJSValue.9.qml");
4528 QTest::newRow("base class var property assignment") << testFileUrl("propertyQJSValue.10.qml");
4529 QTest::newRow("javascript function assignment") << testFileUrl("propertyQJSValue.11.qml");
4530 QTest::newRow("javascript special assignment") << testFileUrl("propertyQJSValue.12.qml");
4531 QTest::newRow("declarative binding assignment") << testFileUrl("propertyQJSValue.13.qml");
4532 QTest::newRow("imperative binding assignment") << testFileUrl("propertyQJSValue.14.qml");
4533 QTest::newRow("stored binding assignment") << testFileUrl("propertyQJSValue.15.qml");
4534 QTest::newRow("javascript function binding") << testFileUrl("propertyQJSValue.16.qml");
4536 QTest::newRow("reset property") << testFileUrl("propertyQJSValue.reset.qml");
4537 QTest::newRow("reset property in binding") << testFileUrl("propertyQJSValue.bindingreset.qml");
4540 void tst_qqmlecmascript::propertyQJSValue()
4542 QFETCH(QUrl, qmlFile);
4544 QQmlComponent component(&engine, qmlFile);
4545 QObject *object = component.create();
4546 QVERIFY(object != 0);
4548 QCOMPARE(object->property("test").toBool(), true);
4553 // Tests that we can write QVariant values to var properties from C++
4554 void tst_qqmlecmascript::propertyVarCpp()
4556 QObject *object = 0;
4558 // ensure that writing to and reading from a var property from cpp works as required.
4559 // Literal values stored in var properties can be read and written as QVariants
4560 // of a specific type, whereas object values are read as QVariantMaps.
4561 QQmlComponent component(&engine, testFileUrl("propertyVarCpp.qml"));
4562 object = component.create();
4563 QVERIFY(object != 0);
4564 // assign int to property var that currently has int assigned
4565 QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
4566 QCOMPARE(object->property("varBound"), QVariant(15));
4567 QCOMPARE(object->property("intBound"), QVariant(15));
4568 QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
4569 QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
4570 // assign string to property var that current has bool assigned
4571 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
4572 QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
4573 QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
4574 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
4575 // now enforce behaviour when accessing JavaScript objects from cpp.
4576 QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
4580 static void gc(QQmlEngine &engine)
4582 engine.collectGarbage();
4583 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4584 QCoreApplication::processEvents();
4587 void tst_qqmlecmascript::propertyVarOwnership()
4589 // Referenced JS objects are not collected
4591 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.qml"));
4592 QObject *object = component.create();
4593 QVERIFY(object != 0);
4594 QCOMPARE(object->property("test").toBool(), false);
4595 QMetaObject::invokeMethod(object, "runTest");
4596 QCOMPARE(object->property("test").toBool(), true);
4599 // Referenced JS objects are not collected
4601 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml"));
4602 QObject *object = component.create();
4603 QVERIFY(object != 0);
4604 QCOMPARE(object->property("test").toBool(), false);
4605 QMetaObject::invokeMethod(object, "runTest");
4606 QCOMPARE(object->property("test").toBool(), true);
4609 // Qt objects are not collected until they've been dereferenced
4611 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml"));
4612 QObject *object = component.create();
4613 QVERIFY(object != 0);
4615 QCOMPARE(object->property("test2").toBool(), false);
4616 QCOMPARE(object->property("test2").toBool(), false);
4618 QMetaObject::invokeMethod(object, "runTest");
4619 QCOMPARE(object->property("test1").toBool(), true);
4621 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4622 QVERIFY(!referencedObject.isNull());
4624 QVERIFY(!referencedObject.isNull());
4626 QMetaObject::invokeMethod(object, "runTest2");
4627 QCOMPARE(object->property("test2").toBool(), true);
4629 QVERIFY(referencedObject.isNull());
4633 // Self reference does not prevent Qt object collection
4635 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml"));
4636 QObject *object = component.create();
4637 QVERIFY(object != 0);
4639 QCOMPARE(object->property("test").toBool(), true);
4641 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4642 QVERIFY(!referencedObject.isNull());
4644 QVERIFY(!referencedObject.isNull());
4646 QMetaObject::invokeMethod(object, "runTest");
4648 QVERIFY(referencedObject.isNull());
4652 // Garbage collection cannot result in attempted dereference of empty handle
4654 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.5.qml"));
4655 QObject *object = component.create();
4656 QVERIFY(object != 0);
4657 QMetaObject::invokeMethod(object, "runTest");
4658 QCOMPARE(object->property("test").toBool(), true);
4663 void tst_qqmlecmascript::propertyVarImplicitOwnership()
4665 // The childObject has a reference to a different QObject. We want to ensure
4666 // that the different item will not be cleaned up until required. IE, the childObject
4667 // has implicit ownership of the constructed QObject.
4668 QQmlComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml"));
4669 QObject *object = component.create();
4670 QVERIFY(object != 0);
4671 QMetaObject::invokeMethod(object, "assignCircular");
4672 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4673 QCoreApplication::processEvents();
4674 QObject *rootObject = object->property("vp").value<QObject*>();
4675 QVERIFY(rootObject != 0);
4676 QObject *childObject = rootObject->findChild<QObject*>("text");
4677 QVERIFY(childObject != 0);
4678 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4679 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4680 QMetaObject::invokeMethod(childObject, "constructQObject"); // creates a reference to a constructed QObject.
4681 QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
4682 QVERIFY(!qobjectGuard.isNull());
4683 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4684 QCoreApplication::processEvents();
4685 QVERIFY(!qobjectGuard.isNull());
4686 QMetaObject::invokeMethod(object, "deassignCircular");
4687 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4688 QCoreApplication::processEvents();
4689 QVERIFY(qobjectGuard.isNull()); // should have been collected now.
4693 void tst_qqmlecmascript::propertyVarReparent()
4695 // ensure that nothing breaks if we re-parent objects
4696 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4697 QObject *object = component.create();
4698 QVERIFY(object != 0);
4699 QMetaObject::invokeMethod(object, "assignVarProp");
4700 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4701 QCoreApplication::processEvents();
4702 QObject *rect = object->property("vp").value<QObject*>();
4703 QObject *text = rect->findChild<QObject*>("textOne");
4704 QObject *text2 = rect->findChild<QObject*>("textTwo");
4705 QWeakPointer<QObject> rectGuard(rect);
4706 QWeakPointer<QObject> textGuard(text);
4707 QWeakPointer<QObject> text2Guard(text2);
4708 QVERIFY(!rectGuard.isNull());
4709 QVERIFY(!textGuard.isNull());
4710 QVERIFY(!text2Guard.isNull());
4711 QCOMPARE(text->property("textCanary").toInt(), 11);
4712 QCOMPARE(text2->property("textCanary").toInt(), 12);
4713 // now construct an image which we will reparent.
4714 QMetaObject::invokeMethod(text2, "constructQObject");
4715 QObject *image = text2->property("vp").value<QObject*>();
4716 QWeakPointer<QObject> imageGuard(image);
4717 QVERIFY(!imageGuard.isNull());
4718 QCOMPARE(image->property("imageCanary").toInt(), 13);
4719 // now reparent the "Image" object (currently, it has JS ownership)
4720 image->setParent(text); // shouldn't be collected after deassignVp now, since has a parent.
4721 QMetaObject::invokeMethod(text2, "deassignVp");
4722 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4723 QCoreApplication::processEvents();
4724 QCOMPARE(text->property("textCanary").toInt(), 11);
4725 QCOMPARE(text2->property("textCanary").toInt(), 22);
4726 QVERIFY(!imageGuard.isNull()); // should still be alive.
4727 QCOMPARE(image->property("imageCanary").toInt(), 13); // still able to access var properties
4728 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4729 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4730 QCoreApplication::processEvents();
4731 QVERIFY(imageGuard.isNull()); // should now have been deleted, due to parent being deleted.
4735 void tst_qqmlecmascript::propertyVarReparentNullContext()
4737 // sometimes reparenting can cause problems
4738 // (eg, if the ctxt is collected, varproperties are no longer available)
4739 // this test ensures that no crash occurs in that situation.
4740 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4741 QObject *object = component.create();
4742 QVERIFY(object != 0);
4743 QMetaObject::invokeMethod(object, "assignVarProp");
4744 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4745 QCoreApplication::processEvents();
4746 QObject *rect = object->property("vp").value<QObject*>();
4747 QObject *text = rect->findChild<QObject*>("textOne");
4748 QObject *text2 = rect->findChild<QObject*>("textTwo");
4749 QWeakPointer<QObject> rectGuard(rect);
4750 QWeakPointer<QObject> textGuard(text);
4751 QWeakPointer<QObject> text2Guard(text2);
4752 QVERIFY(!rectGuard.isNull());
4753 QVERIFY(!textGuard.isNull());
4754 QVERIFY(!text2Guard.isNull());
4755 QCOMPARE(text->property("textCanary").toInt(), 11);
4756 QCOMPARE(text2->property("textCanary").toInt(), 12);
4757 // now construct an image which we will reparent.
4758 QMetaObject::invokeMethod(text2, "constructQObject");
4759 QObject *image = text2->property("vp").value<QObject*>();
4760 QWeakPointer<QObject> imageGuard(image);
4761 QVERIFY(!imageGuard.isNull());
4762 QCOMPARE(image->property("imageCanary").toInt(), 13);
4763 // now reparent the "Image" object (currently, it has JS ownership)
4764 image->setParent(object); // reparented to base object. after deassignVarProp, the ctxt will be invalid.
4765 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4766 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4767 QCoreApplication::processEvents();
4768 QVERIFY(!imageGuard.isNull()); // should still be alive.
4769 QVERIFY(!image->property("imageCanary").isValid()); // but varProperties won't be available (null context).
4771 QVERIFY(imageGuard.isNull()); // should now be dead.
4774 void tst_qqmlecmascript::propertyVarCircular()
4776 // enforce behaviour regarding circular references - ensure qdvmemo deletion.
4777 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.qml"));
4778 QObject *object = component.create();
4779 QVERIFY(object != 0);
4780 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4781 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4782 QCoreApplication::processEvents();
4783 QCOMPARE(object->property("canaryInt"), QVariant(5));
4784 QVariant canaryResourceVariant = object->property("canaryResource");
4785 QVERIFY(canaryResourceVariant.isValid());
4786 QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
4787 canaryResourceVariant = QVariant(); // invalidate it to remove one copy of the pixmap from memory.
4788 QMetaObject::invokeMethod(object, "deassignCanaryResource"); // remove one copy of the pixmap from memory
4789 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4790 QCoreApplication::processEvents();
4791 QVERIFY(!canaryResourcePixmap.isDetached()); // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
4792 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4793 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4794 QCoreApplication::processEvents();
4795 QCOMPARE(object->property("canaryInt"), QVariant(2));
4796 QCOMPARE(object->property("canaryResource"), QVariant(1));
4797 QVERIFY(canaryResourcePixmap.isDetached()); // now detached, since orig copy was member of qdvmemo which was deleted.
4801 void tst_qqmlecmascript::propertyVarCircular2()
4803 // track deletion of JS-owned parent item with Cpp-owned child
4804 // where the child has a var property referencing its parent.
4805 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4806 QObject *object = component.create();
4807 QVERIFY(object != 0);
4808 QMetaObject::invokeMethod(object, "assignCircular");
4809 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4810 QCoreApplication::processEvents();
4811 QObject *rootObject = object->property("vp").value<QObject*>();
4812 QVERIFY(rootObject != 0);
4813 QObject *childObject = rootObject->findChild<QObject*>("text");
4814 QVERIFY(childObject != 0);
4815 QWeakPointer<QObject> rootObjectTracker(rootObject);
4816 QVERIFY(!rootObjectTracker.isNull());
4817 QWeakPointer<QObject> childObjectTracker(childObject);
4818 QVERIFY(!childObjectTracker.isNull());
4820 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4821 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4822 QMetaObject::invokeMethod(object, "deassignCircular");
4823 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4824 QCoreApplication::processEvents();
4825 QVERIFY(rootObjectTracker.isNull()); // should have been collected
4826 QVERIFY(childObjectTracker.isNull()); // should have been collected
4830 void tst_qqmlecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
4832 *(int*)(parameter) += 1;
4833 qPersistentDispose(object);
4836 void tst_qqmlecmascript::propertyVarInheritance()
4838 int propertyVarWeakRefCallbackCount = 0;
4840 // enforce behaviour regarding element inheritance - ensure handle disposal.
4841 // The particular component under test here has a chain of references.
4842 QQmlComponent component(&engine, testFileUrl("propertyVar.inherit.qml"));
4843 QObject *object = component.create();
4844 QVERIFY(object != 0);
4845 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4846 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4847 QCoreApplication::processEvents();
4848 // we want to be able to track when the varProperties array of the last metaobject is disposed
4849 QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
4850 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*>();
4851 QQmlVMEMetaObject *icovmemo = QQmlVMEMetaObject::get(ico5);
4852 QQmlVMEMetaObject *ccovmemo = QQmlVMEMetaObject::get(cco5);
4853 v8::Persistent<v8::Value> icoCanaryHandle;
4854 v8::Persistent<v8::Value> ccoCanaryHandle;
4857 // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only
4858 // public function which can return us a handle to something in the varProperties array.
4859 icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ")));
4860 ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")));
4861 // we make them weak and invoke the gc, but we should not hit the weak-callback yet
4862 // as the varproperties array of each vmemo still references the resource.
4863 icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4864 ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4866 QVERIFY(propertyVarWeakRefCallbackCount == 0);
4868 // now we deassign the var prop, which should trigger collection of item subtrees.
4869 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4870 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4871 QCoreApplication::processEvents();
4872 // ensure that there are only weak handles to the underlying varProperties array remaining.
4874 QCOMPARE(propertyVarWeakRefCallbackCount, 2); // should have been called for both, since all refs should be weak.
4876 // since there are no parent vmemo's to keep implicit references alive, and the only handles
4877 // to what remains are weak, all varProperties arrays must have been collected.
4880 void tst_qqmlecmascript::propertyVarInheritance2()
4882 int propertyVarWeakRefCallbackCount = 0;
4884 // The particular component under test here does NOT have a chain of references; the
4885 // only link between rootObject and childObject is that rootObject is the parent of childObject.
4886 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4887 QObject *object = component.create();
4888 QVERIFY(object != 0);
4889 QMetaObject::invokeMethod(object, "assignCircular");
4890 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4891 QCoreApplication::processEvents();
4892 QObject *rootObject = object->property("vp").value<QObject*>();
4893 QVERIFY(rootObject != 0);
4894 QObject *childObject = rootObject->findChild<QObject*>("text");
4895 QVERIFY(childObject != 0);
4896 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4897 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4898 v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
4901 propertyVarWeakRefCallbackCount = 0; // reset callback count.
4902 childObjectVarArrayValueHandle = qPersistentNew(QQmlVMEMetaObject::get(childObject)->vmeProperty(childObject->metaObject()->indexOfProperty("vp")));
4903 childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4905 QVERIFY(propertyVarWeakRefCallbackCount == 0); // should not have been collected yet.
4906 QCOMPARE(childObject->property("vp").value<QObject*>(), rootObject);
4907 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4909 QMetaObject::invokeMethod(object, "deassignCircular");
4910 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4911 QCoreApplication::processEvents();
4912 QVERIFY(propertyVarWeakRefCallbackCount == 1); // should have been collected now.
4916 // Ensure that QObject type conversion works on binding assignment
4917 void tst_qqmlecmascript::elementAssign()
4919 QQmlComponent component(&engine, testFileUrl("elementAssign.qml"));
4921 QObject *object = component.create();
4922 QVERIFY(object != 0);
4924 QCOMPARE(object->property("test").toBool(), true);
4930 void tst_qqmlecmascript::objectPassThroughSignals()
4932 QQmlComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml"));
4934 QObject *object = component.create();
4935 QVERIFY(object != 0);
4937 QCOMPARE(object->property("test").toBool(), true);
4943 void tst_qqmlecmascript::objectConversion()
4945 QQmlComponent component(&engine, testFileUrl("objectConversion.qml"));
4947 QObject *object = component.create();
4948 QVERIFY(object != 0);
4950 QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
4951 QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
4958 void tst_qqmlecmascript::booleanConversion()
4960 QQmlComponent component(&engine, testFileUrl("booleanConversion.qml"));
4962 QObject *object = component.create();
4963 QVERIFY(object != 0);
4965 QCOMPARE(object->property("test_true1").toBool(), true);
4966 QCOMPARE(object->property("test_true2").toBool(), true);
4967 QCOMPARE(object->property("test_true3").toBool(), true);
4968 QCOMPARE(object->property("test_true4").toBool(), true);
4969 QCOMPARE(object->property("test_true5").toBool(), true);
4971 QCOMPARE(object->property("test_false1").toBool(), false);
4972 QCOMPARE(object->property("test_false2").toBool(), false);
4973 QCOMPARE(object->property("test_false3").toBool(), false);
4978 void tst_qqmlecmascript::handleReferenceManagement()
4983 // Linear QObject reference
4984 QQmlEngine hrmEngine;
4985 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml"));
4986 QObject *object = component.create();
4987 QVERIFY(object != 0);
4988 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4989 cro->setEngine(&hrmEngine);
4990 cro->setDtorCount(&dtorCount);
4991 QMetaObject::invokeMethod(object, "createReference");
4993 QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
4995 hrmEngine.collectGarbage();
4996 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4997 QCoreApplication::processEvents();
4998 QCOMPARE(dtorCount, 3);
5003 // Circular QObject reference
5004 QQmlEngine hrmEngine;
5005 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml"));
5006 QObject *object = component.create();
5007 QVERIFY(object != 0);
5008 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
5009 cro->setEngine(&hrmEngine);
5010 cro->setDtorCount(&dtorCount);
5011 QMetaObject::invokeMethod(object, "circularReference");
5013 QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
5015 hrmEngine.collectGarbage();
5016 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5017 QCoreApplication::processEvents();
5018 QCOMPARE(dtorCount, 3);
5023 // Linear handle reference
5024 QQmlEngine hrmEngine;
5025 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.1.qml"));
5026 QObject *object = component.create();
5027 QVERIFY(object != 0);
5028 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
5030 crh->setEngine(&hrmEngine);
5031 crh->setDtorCount(&dtorCount);
5032 QMetaObject::invokeMethod(object, "createReference");
5033 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
5034 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
5035 QVERIFY(first != 0);
5036 QVERIFY(second != 0);
5037 first->addReference(QQmlData::get(second)->v8object); // create reference
5038 // now we have to reparent second and make second owned by JS.
5039 second->setParent(0);
5040 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
5042 QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
5044 hrmEngine.collectGarbage();
5045 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5046 QCoreApplication::processEvents();
5047 QCOMPARE(dtorCount, 3);
5052 // Circular handle reference
5053 QQmlEngine hrmEngine;
5054 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.2.qml"));
5055 QObject *object = component.create();
5056 QVERIFY(object != 0);
5057 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
5059 crh->setEngine(&hrmEngine);
5060 crh->setDtorCount(&dtorCount);
5061 QMetaObject::invokeMethod(object, "circularReference");
5062 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
5063 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
5064 QVERIFY(first != 0);
5065 QVERIFY(second != 0);
5066 first->addReference(QQmlData::get(second)->v8object); // create circular reference
5067 second->addReference(QQmlData::get(first)->v8object); // note: must be weak.
5068 // now we have to reparent and change ownership, and unset the property references.
5069 first->setParent(0);
5070 second->setParent(0);
5071 QQmlEngine::setObjectOwnership(first, QQmlEngine::JavaScriptOwnership);
5072 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
5073 object->setProperty("first", QVariant::fromValue<QObject*>(0));
5074 object->setProperty("second", QVariant::fromValue<QObject*>(0));
5076 QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
5078 hrmEngine.collectGarbage();
5079 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5080 QCoreApplication::processEvents();
5081 QCOMPARE(dtorCount, 3);
5086 // multiple engine interaction - linear reference
5087 QQmlEngine hrmEngine1;
5088 QQmlEngine hrmEngine2;
5089 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
5090 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
5091 QObject *object1 = component1.create();
5092 QObject *object2 = component2.create();
5093 QVERIFY(object1 != 0);
5094 QVERIFY(object2 != 0);
5095 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
5096 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
5099 crh1->setEngine(&hrmEngine1);
5100 crh2->setEngine(&hrmEngine2);
5101 crh1->setDtorCount(&dtorCount);
5102 crh2->setDtorCount(&dtorCount);
5103 QMetaObject::invokeMethod(object1, "createReference");
5104 QMetaObject::invokeMethod(object2, "createReference");
5105 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
5106 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
5107 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
5108 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
5109 QVERIFY(first1 != 0);
5110 QVERIFY(second1 != 0);
5111 QVERIFY(first2 != 0);
5112 QVERIFY(second2 != 0);
5113 first1->addReference(QQmlData::get(second2)->v8object); // create reference across engines
5114 // now we have to reparent second2 and make second2 owned by JS.
5115 second2->setParent(0);
5116 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5118 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5119 QCoreApplication::processEvents();
5120 QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
5123 hrmEngine1.collectGarbage();
5124 hrmEngine2.collectGarbage();
5125 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5126 QCoreApplication::processEvents();
5127 QCOMPARE(dtorCount, 6);
5132 // multiple engine interaction - circular reference
5133 QQmlEngine hrmEngine1;
5134 QQmlEngine hrmEngine2;
5135 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
5136 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
5137 QObject *object1 = component1.create();
5138 QObject *object2 = component2.create();
5139 QVERIFY(object1 != 0);
5140 QVERIFY(object2 != 0);
5141 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
5142 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
5145 crh1->setEngine(&hrmEngine1);
5146 crh2->setEngine(&hrmEngine2);
5147 crh1->setDtorCount(&dtorCount);
5148 crh2->setDtorCount(&dtorCount);
5149 QMetaObject::invokeMethod(object1, "createReference");
5150 QMetaObject::invokeMethod(object2, "createReference");
5151 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
5152 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
5153 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
5154 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
5155 QVERIFY(first1 != 0);
5156 QVERIFY(second1 != 0);
5157 QVERIFY(first2 != 0);
5158 QVERIFY(second2 != 0);
5159 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
5160 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
5161 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
5162 first2->addReference(QQmlData::get(first1)->v8object); // close the loop - circular ref across engines
5163 // now we have to reparent and change ownership to JS, and remove property references.
5164 first1->setParent(0);
5165 second1->setParent(0);
5166 first2->setParent(0);
5167 second2->setParent(0);
5168 QQmlEngine::setObjectOwnership(first1, QQmlEngine::JavaScriptOwnership);
5169 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
5170 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
5171 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5172 object1->setProperty("first", QVariant::fromValue<QObject*>(0));
5173 object1->setProperty("second", QVariant::fromValue<QObject*>(0));
5174 object2->setProperty("first", QVariant::fromValue<QObject*>(0));
5175 object2->setProperty("second", QVariant::fromValue<QObject*>(0));
5177 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5178 QCoreApplication::processEvents();
5179 QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
5182 hrmEngine1.collectGarbage();
5183 hrmEngine2.collectGarbage();
5184 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5185 QCoreApplication::processEvents();
5186 QCOMPARE(dtorCount, 6);
5191 // multiple engine interaction - linear reference with engine deletion
5192 QQmlEngine *hrmEngine1 = new QQmlEngine;
5193 QQmlEngine *hrmEngine2 = new QQmlEngine;
5194 QQmlComponent component1(hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
5195 QQmlComponent component2(hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
5196 QObject *object1 = component1.create();
5197 QObject *object2 = component2.create();
5198 QVERIFY(object1 != 0);
5199 QVERIFY(object2 != 0);
5200 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
5201 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
5204 crh1->setEngine(hrmEngine1);
5205 crh2->setEngine(hrmEngine2);
5206 crh1->setDtorCount(&dtorCount);
5207 crh2->setDtorCount(&dtorCount);
5208 QMetaObject::invokeMethod(object1, "createReference");
5209 QMetaObject::invokeMethod(object2, "createReference");
5210 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
5211 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
5212 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
5213 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
5214 QVERIFY(first1 != 0);
5215 QVERIFY(second1 != 0);
5216 QVERIFY(first2 != 0);
5217 QVERIFY(second2 != 0);
5218 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
5219 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
5220 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
5221 // now we have to reparent and change ownership to JS.
5222 first1->setParent(crh1);
5223 second1->setParent(0);
5224 first2->setParent(0);
5225 second2->setParent(0);
5226 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
5227 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
5228 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5231 QCOMPARE(dtorCount, 0);
5232 delete hrmEngine2; // should trigger deletion of objects with JS ownership tracked by this engine
5234 QCOMPARE(dtorCount, 2); // first2 and second2 should have been deleted.
5238 QCOMPARE(dtorCount, 6); // deleting object1 and object2 should trigger deletion of first1 and first2.
5240 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5241 QCoreApplication::processEvents();
5242 QCOMPARE(dtorCount, 6); // all objects should have been cleaned up prior to deleting hrmEngine1.
5246 // Dynamic variant property reference keeps target alive
5247 QQmlEngine hrmEngine;
5248 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.qml"));
5249 QObject *object = component.create();
5250 QVERIFY(object != 0);
5251 QMetaObject::invokeMethod(object, "createReference");
5253 QMetaObject::invokeMethod(object, "ensureReference");
5255 QMetaObject::invokeMethod(object, "removeReference");
5257 QMetaObject::invokeMethod(object, "ensureDeletion");
5258 QCOMPARE(object->property("success").toBool(), true);
5263 // Dynamic Item property reference keeps target alive
5264 QQmlEngine hrmEngine;
5265 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.2.qml"));
5266 QObject *object = component.create();
5267 QVERIFY(object != 0);
5268 QMetaObject::invokeMethod(object, "createReference");
5270 QMetaObject::invokeMethod(object, "ensureReference");
5272 QMetaObject::invokeMethod(object, "removeReference");
5274 QMetaObject::invokeMethod(object, "ensureDeletion");
5275 QCOMPARE(object->property("success").toBool(), true);
5280 // Item property reference to deleted item doesn't crash
5281 QQmlEngine hrmEngine;
5282 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.3.qml"));
5283 QObject *object = component.create();
5284 QVERIFY(object != 0);
5285 QMetaObject::invokeMethod(object, "createReference");
5287 QMetaObject::invokeMethod(object, "ensureReference");
5289 QMetaObject::invokeMethod(object, "manuallyDelete");
5291 QMetaObject::invokeMethod(object, "ensureDeleted");
5292 QCOMPARE(object->property("success").toBool(), true);
5297 void tst_qqmlecmascript::stringArg()
5299 QQmlComponent component(&engine, testFileUrl("stringArg.qml"));
5300 QObject *object = component.create();
5301 QVERIFY(object != 0);
5302 QMetaObject::invokeMethod(object, "success");
5303 QVERIFY(object->property("returnValue").toBool());
5305 QString w1 = testFileUrl("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
5306 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5307 QMetaObject::invokeMethod(object, "failure");
5308 QVERIFY(object->property("returnValue").toBool());
5313 void tst_qqmlecmascript::readonlyDeclaration()
5315 QQmlComponent component(&engine, testFileUrl("readonlyDeclaration.qml"));
5317 QObject *object = component.create();
5318 QVERIFY(object != 0);
5320 QCOMPARE(object->property("test").toBool(), true);
5325 Q_DECLARE_METATYPE(QList<int>)
5326 Q_DECLARE_METATYPE(QList<qreal>)
5327 Q_DECLARE_METATYPE(QList<bool>)
5328 Q_DECLARE_METATYPE(QList<QString>)
5329 Q_DECLARE_METATYPE(QList<QUrl>)
5330 void tst_qqmlecmascript::sequenceConversionRead()
5333 QUrl qmlFile = testFileUrl("sequenceConversion.read.qml");
5334 QQmlComponent component(&engine, qmlFile);
5335 QObject *object = component.create();
5336 QVERIFY(object != 0);
5337 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5340 QMetaObject::invokeMethod(object, "readSequences");
5341 QList<int> intList; intList << 1 << 2 << 3 << 4;
5342 QCOMPARE(object->property("intListLength").toInt(), intList.length());
5343 QCOMPARE(object->property("intList").value<QList<int> >(), intList);
5344 QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
5345 QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
5346 QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
5347 QList<bool> boolList; boolList << true << false << true << false;
5348 QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
5349 QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
5350 QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5351 QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
5352 QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
5353 QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
5354 QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
5355 QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
5356 QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5357 QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
5358 QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
5360 QMetaObject::invokeMethod(object, "readSequenceElements");
5361 QCOMPARE(object->property("intVal").toInt(), 2);
5362 QCOMPARE(object->property("qrealVal").toReal(), 2.2);
5363 QCOMPARE(object->property("boolVal").toBool(), false);
5364 QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
5365 QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
5366 QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
5368 QMetaObject::invokeMethod(object, "enumerateSequenceElements");
5369 QCOMPARE(object->property("enumerationMatches").toBool(), true);
5371 intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
5372 QQmlProperty seqProp(seq, "intListProperty");
5373 QCOMPARE(seqProp.read().value<QList<int> >(), intList);
5374 QQmlProperty seqProp2(seq, "intListProperty", &engine);
5375 QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
5377 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5378 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5384 QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml");
5385 QQmlComponent component(&engine, qmlFile);
5386 QObject *object = component.create();
5387 QVERIFY(object != 0);
5388 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5391 // we haven't registered QList<QPoint> as a sequence type.
5392 QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5393 QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
5394 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5395 QTest::ignoreMessage(QtWarningMsg, warningTwo.toLatin1().constData());
5397 QMetaObject::invokeMethod(object, "performTest");
5399 // QList<QPoint> has not been registered as a sequence type.
5400 QCOMPARE(object->property("pointListLength").toInt(), 0);
5401 QVERIFY(!object->property("pointList").isValid());
5402 QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5403 QQmlProperty seqProp(seq, "pointListProperty", &engine);
5404 QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
5410 void tst_qqmlecmascript::sequenceConversionWrite()
5413 QUrl qmlFile = testFileUrl("sequenceConversion.write.qml");
5414 QQmlComponent component(&engine, qmlFile);
5415 QObject *object = component.create();
5416 QVERIFY(object != 0);
5417 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5420 QMetaObject::invokeMethod(object, "writeSequences");
5421 QCOMPARE(object->property("success").toBool(), true);
5423 QMetaObject::invokeMethod(object, "writeSequenceElements");
5424 QCOMPARE(object->property("success").toBool(), true);
5426 QMetaObject::invokeMethod(object, "writeOtherElements");
5427 QCOMPARE(object->property("success").toBool(), true);
5429 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5430 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5436 QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml");
5437 QQmlComponent component(&engine, qmlFile);
5438 QObject *object = component.create();
5439 QVERIFY(object != 0);
5440 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5443 // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
5444 QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to an unregistered type");
5445 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5447 QMetaObject::invokeMethod(object, "performTest");
5449 QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
5450 QCOMPARE(seq->pointListProperty(), pointList);
5456 void tst_qqmlecmascript::sequenceConversionArray()
5458 // ensure that in JS the returned sequences act just like normal JS Arrays.
5459 QUrl qmlFile = testFileUrl("sequenceConversion.array.qml");
5460 QQmlComponent component(&engine, qmlFile);
5461 QObject *object = component.create();
5462 QVERIFY(object != 0);
5463 QMetaObject::invokeMethod(object, "indexedAccess");
5464 QVERIFY(object->property("success").toBool());
5465 QMetaObject::invokeMethod(object, "arrayOperations");
5466 QVERIFY(object->property("success").toBool());
5467 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5468 QVERIFY(object->property("success").toBool());
5469 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5470 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5475 void tst_qqmlecmascript::sequenceConversionIndexes()
5477 // ensure that we gracefully fail if unsupported index values are specified.
5478 // Qt container classes only support non-negative, signed integer index values.
5479 QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
5480 QQmlComponent component(&engine, qmlFile);
5481 QObject *object = component.create();
5482 QVERIFY(object != 0);
5483 QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
5484 QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
5485 QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
5486 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
5487 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
5488 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
5489 QMetaObject::invokeMethod(object, "indexedAccess");
5490 QVERIFY(object->property("success").toBool());
5494 void tst_qqmlecmascript::sequenceConversionThreads()
5496 // ensure that sequence conversion operations work correctly in a worker thread
5497 // and that serialisation between the main and worker thread succeeds.
5498 QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml");
5499 QQmlComponent component(&engine, qmlFile);
5500 QObject *object = component.create();
5501 QVERIFY(object != 0);
5503 QMetaObject::invokeMethod(object, "testIntSequence");
5504 QTRY_VERIFY(object->property("finished").toBool());
5505 QVERIFY(object->property("success").toBool());
5507 QMetaObject::invokeMethod(object, "testQrealSequence");
5508 QTRY_VERIFY(object->property("finished").toBool());
5509 QVERIFY(object->property("success").toBool());
5511 QMetaObject::invokeMethod(object, "testBoolSequence");
5512 QTRY_VERIFY(object->property("finished").toBool());
5513 QVERIFY(object->property("success").toBool());
5515 QMetaObject::invokeMethod(object, "testStringSequence");
5516 QTRY_VERIFY(object->property("finished").toBool());
5517 QVERIFY(object->property("success").toBool());
5519 QMetaObject::invokeMethod(object, "testQStringSequence");
5520 QTRY_VERIFY(object->property("finished").toBool());
5521 QVERIFY(object->property("success").toBool());
5523 QMetaObject::invokeMethod(object, "testUrlSequence");
5524 QTRY_VERIFY(object->property("finished").toBool());
5525 QVERIFY(object->property("success").toBool());
5527 QMetaObject::invokeMethod(object, "testVariantSequence");
5528 QTRY_VERIFY(object->property("finished").toBool());
5529 QVERIFY(object->property("success").toBool());
5534 void tst_qqmlecmascript::sequenceConversionBindings()
5537 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml");
5538 QQmlComponent component(&engine, qmlFile);
5539 QObject *object = component.create();
5540 QVERIFY(object != 0);
5541 QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
5542 QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
5543 QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
5544 QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
5545 QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
5550 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml");
5551 QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
5552 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5553 QQmlComponent component(&engine, qmlFile);
5554 QObject *object = component.create();
5555 QVERIFY(object != 0);
5560 void tst_qqmlecmascript::sequenceConversionCopy()
5562 QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml");
5563 QQmlComponent component(&engine, qmlFile);
5564 QObject *object = component.create();
5565 QVERIFY(object != 0);
5566 QMetaObject::invokeMethod(object, "testCopySequences");
5567 QCOMPARE(object->property("success").toBool(), true);
5568 QMetaObject::invokeMethod(object, "readSequenceCopyElements");
5569 QCOMPARE(object->property("success").toBool(), true);
5570 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5571 QCOMPARE(object->property("success").toBool(), true);
5575 void tst_qqmlecmascript::assignSequenceTypes()
5577 // test binding array to sequence type property
5579 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml"));
5580 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5581 QVERIFY(object != 0);
5582 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5583 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5584 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5585 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5586 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5587 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5591 // test binding literal to sequence type property
5593 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml"));
5594 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5595 QVERIFY(object != 0);
5596 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5597 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5598 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5599 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5600 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5601 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5605 // test binding single value to sequence type property
5607 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml"));
5608 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5609 QVERIFY(object != 0);
5610 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5611 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5612 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5613 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5617 // test assigning array to sequence type property in js function
5619 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml"));
5620 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5621 QVERIFY(object != 0);
5622 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5623 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5624 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5625 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5626 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5627 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5631 // test assigning literal to sequence type property in js function
5633 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml"));
5634 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5635 QVERIFY(object != 0);
5636 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5637 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5638 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5639 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5640 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5641 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5645 // test assigning single value to sequence type property in js function
5647 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml"));
5648 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5649 QVERIFY(object != 0);
5650 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5651 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5652 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5653 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5657 // test QList<QUrl> literal assignment and binding assignment causes url resolution when required
5659 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml"));
5660 QObject *object = component.create();
5661 QVERIFY(object != 0);
5662 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
5663 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
5664 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
5665 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
5666 MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco5"));
5667 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0);
5668 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5669 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5670 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5671 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5672 QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5677 // Test that assigning a null object works
5678 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
5679 void tst_qqmlecmascript::nullObjectBinding()
5681 QQmlComponent component(&engine, testFileUrl("nullObjectBinding.qml"));
5683 QObject *object = component.create();
5684 QVERIFY(object != 0);
5686 QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
5691 // Test that bindings don't evaluate once the engine has been destroyed
5692 void tst_qqmlecmascript::deletedEngine()
5694 QQmlEngine *engine = new QQmlEngine;
5695 QQmlComponent component(engine, testFileUrl("deletedEngine.qml"));
5697 QObject *object = component.create();
5698 QVERIFY(object != 0);
5700 QCOMPARE(object->property("a").toInt(), 39);
5701 object->setProperty("b", QVariant(9));
5702 QCOMPARE(object->property("a").toInt(), 117);
5706 QCOMPARE(object->property("a").toInt(), 117);
5707 object->setProperty("b", QVariant(10));
5708 QCOMPARE(object->property("a").toInt(), 117);
5713 // Test the crashing part of QTBUG-9705
5714 void tst_qqmlecmascript::libraryScriptAssert()
5716 QQmlComponent component(&engine, testFileUrl("libraryScriptAssert.qml"));
5718 QObject *object = component.create();
5719 QVERIFY(object != 0);
5724 void tst_qqmlecmascript::variantsAssignedUndefined()
5726 QQmlComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml"));
5728 QObject *object = component.create();
5729 QVERIFY(object != 0);
5731 QCOMPARE(object->property("test1").toInt(), 10);
5732 QCOMPARE(object->property("test2").toInt(), 11);
5734 object->setProperty("runTest", true);
5736 QCOMPARE(object->property("test1"), QVariant());
5737 QCOMPARE(object->property("test2"), QVariant());
5743 void tst_qqmlecmascript::qtbug_9792()
5745 QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml"));
5747 QQmlContext *context = new QQmlContext(engine.rootContext());
5749 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
5750 QVERIFY(object != 0);
5752 QTest::ignoreMessage(QtDebugMsg, "Hello world!");
5753 object->basicSignal();
5757 transientErrorsMsgCount = 0;
5758 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5760 object->basicSignal();
5762 qInstallMsgHandler(old);
5764 QCOMPARE(transientErrorsMsgCount, 0);
5769 // Verifies that QQmlGuard<>s used in the vmemetaobject are cleaned correctly
5770 void tst_qqmlecmascript::qtcreatorbug_1289()
5772 QQmlComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml"));
5774 QObject *o = component.create();
5777 QObject *nested = qvariant_cast<QObject *>(o->property("object"));
5778 QVERIFY(nested != 0);
5780 QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
5783 nested = qvariant_cast<QObject *>(o->property("object"));
5784 QVERIFY(nested == 0);
5786 // If the bug is present, the next line will crash
5790 // Test that we shut down without stupid warnings
5791 void tst_qqmlecmascript::noSpuriousWarningsAtShutdown()
5794 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml"));
5796 QObject *o = component.create();
5798 transientErrorsMsgCount = 0;
5799 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5803 qInstallMsgHandler(old);
5805 QCOMPARE(transientErrorsMsgCount, 0);
5810 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml"));
5812 QObject *o = component.create();
5814 transientErrorsMsgCount = 0;
5815 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5819 qInstallMsgHandler(old);
5821 QCOMPARE(transientErrorsMsgCount, 0);
5825 void tst_qqmlecmascript::canAssignNullToQObject()
5828 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml"));
5830 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5833 QVERIFY(o->objectProperty() != 0);
5835 o->setProperty("runTest", true);
5837 QVERIFY(o->objectProperty() == 0);
5843 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml"));
5845 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5848 QVERIFY(o->objectProperty() == 0);
5854 void tst_qqmlecmascript::functionAssignment_fromBinding()
5856 QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml"));
5858 QString url = component.url().toString();
5859 QString w1 = url + ":4:25: Unable to assign a function to a property of any type other than var.";
5860 QString w2 = url + ":5:25: Invalid use of Qt.binding() in a binding declaration.";
5861 QString w3 = url + ":6:21: Invalid use of Qt.binding() in a binding declaration.";
5862 QString w4 = url + ":7:15: Invalid use of Qt.binding() in a binding declaration.";
5863 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5864 QTest::ignoreMessage(QtWarningMsg, w2.toLatin1().constData());
5865 QTest::ignoreMessage(QtWarningMsg, w3.toLatin1().constData());
5866 QTest::ignoreMessage(QtWarningMsg, w4.toLatin1().constData());
5868 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5871 QVERIFY(!o->property("a").isValid());
5876 void tst_qqmlecmascript::functionAssignment_fromJS()
5878 QFETCH(QString, triggerProperty);
5880 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5881 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5883 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5885 QVERIFY(!o->property("a").isValid());
5887 o->setProperty("aNumber", QVariant(5));
5888 o->setProperty(triggerProperty.toUtf8().constData(), true);
5889 QCOMPARE(o->property("a"), QVariant(50));
5891 o->setProperty("aNumber", QVariant(10));
5892 QCOMPARE(o->property("a"), QVariant(100));
5897 void tst_qqmlecmascript::functionAssignment_fromJS_data()
5899 QTest::addColumn<QString>("triggerProperty");
5901 QTest::newRow("assign to property") << "assignToProperty";
5902 QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
5904 QTest::newRow("assign to value type") << "assignToValueType";
5906 QTest::newRow("use 'this'") << "assignWithThis";
5907 QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
5910 void tst_qqmlecmascript::functionAssignmentfromJS_invalid()
5912 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5913 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5915 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5917 QVERIFY(!o->property("a").isValid());
5919 o->setProperty("assignFuncWithoutReturn", true);
5920 QVERIFY(!o->property("a").isValid());
5922 QString url = component.url().toString();
5923 QString warning = url + ":67:17: Unable to assign QString to int";
5924 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5925 o->setProperty("assignWrongType", true);
5927 warning = url + ":71:29: Unable to assign QString to int";
5928 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5929 o->setProperty("assignWrongTypeToValueType", true);
5934 void tst_qqmlecmascript::functionAssignment_afterBinding()
5936 QQmlComponent component(&engine, testFileUrl("functionAssignment.3.qml"));
5938 QString url = component.url().toString();
5939 QString w1 = url + ":16: Error: Cannot assign JavaScript function to int";
5940 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5942 QObject *o = component.create();
5944 QCOMPARE(o->property("t1"), QVariant::fromValue<int>(4)); // should have bound
5945 QCOMPARE(o->property("t2"), QVariant::fromValue<int>(2)); // should not have changed
5950 void tst_qqmlecmascript::eval()
5952 QQmlComponent component(&engine, testFileUrl("eval.qml"));
5954 QObject *o = component.create();
5957 QCOMPARE(o->property("test1").toBool(), true);
5958 QCOMPARE(o->property("test2").toBool(), true);
5959 QCOMPARE(o->property("test3").toBool(), true);
5960 QCOMPARE(o->property("test4").toBool(), true);
5961 QCOMPARE(o->property("test5").toBool(), true);
5966 void tst_qqmlecmascript::function()
5968 QQmlComponent component(&engine, testFileUrl("function.qml"));
5970 QObject *o = component.create();
5973 QCOMPARE(o->property("test1").toBool(), true);
5974 QCOMPARE(o->property("test2").toBool(), true);
5975 QCOMPARE(o->property("test3").toBool(), true);
5980 void tst_qqmlecmascript::functionException()
5982 // QTBUG-24037 - shouldn't crash.
5983 QString errstr = testFileUrl("v8functionException.qml").toString() + QLatin1String(":13: SyntaxError: Unexpected token ILLEGAL");
5984 QTest::ignoreMessage(QtWarningMsg, qPrintable(errstr));
5985 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Exception occurred during compilation of function: dynamicSlot()");
5986 QQmlComponent component(&engine, testFileUrl("v8functionException.qml"));
5987 QObject *o = component.create();
5989 QMetaObject::invokeMethod(o, "dynamicSlot");
5993 // Test the "Qt.include" method
5994 void tst_qqmlecmascript::include()
5996 // Non-library relative include
5998 QQmlComponent component(&engine, testFileUrl("include.qml"));
5999 QObject *o = component.create();
6002 QCOMPARE(o->property("test0").toInt(), 99);
6003 QCOMPARE(o->property("test1").toBool(), true);
6004 QCOMPARE(o->property("test2").toBool(), true);
6005 QCOMPARE(o->property("test2_1").toBool(), true);
6006 QCOMPARE(o->property("test3").toBool(), true);
6007 QCOMPARE(o->property("test3_1").toBool(), true);
6012 // Library relative include
6014 QQmlComponent component(&engine, testFileUrl("include_shared.qml"));
6015 QObject *o = component.create();
6018 QCOMPARE(o->property("test0").toInt(), 99);
6019 QCOMPARE(o->property("test1").toBool(), true);
6020 QCOMPARE(o->property("test2").toBool(), true);
6021 QCOMPARE(o->property("test2_1").toBool(), true);
6022 QCOMPARE(o->property("test3").toBool(), true);
6023 QCOMPARE(o->property("test3_1").toBool(), true);
6030 QQmlComponent component(&engine, testFileUrl("include_callback.qml"));
6031 QObject *o = component.create();
6034 QCOMPARE(o->property("test1").toBool(), true);
6035 QCOMPARE(o->property("test2").toBool(), true);
6036 QCOMPARE(o->property("test3").toBool(), true);
6037 QCOMPARE(o->property("test4").toBool(), true);
6038 QCOMPARE(o->property("test5").toBool(), true);
6039 QCOMPARE(o->property("test6").toBool(), true);
6044 // Including file with ".pragma library"
6046 QQmlComponent component(&engine, testFileUrl("include_pragma.qml"));
6047 QObject *o = component.create();
6049 QCOMPARE(o->property("test1").toInt(), 100);
6056 TestHTTPServer server(8111);
6057 QVERIFY(server.isValid());
6058 server.serveDirectory(dataDirectory());
6060 QQmlComponent component(&engine, testFileUrl("include_remote.qml"));
6061 QObject *o = component.create();
6064 QTRY_VERIFY(o->property("done").toBool() == true);
6065 QTRY_VERIFY(o->property("done2").toBool() == true);
6067 QCOMPARE(o->property("test1").toBool(), true);
6068 QCOMPARE(o->property("test2").toBool(), true);
6069 QCOMPARE(o->property("test3").toBool(), true);
6070 QCOMPARE(o->property("test4").toBool(), true);
6071 QCOMPARE(o->property("test5").toBool(), true);
6073 QCOMPARE(o->property("test6").toBool(), true);
6074 QCOMPARE(o->property("test7").toBool(), true);
6075 QCOMPARE(o->property("test8").toBool(), true);
6076 QCOMPARE(o->property("test9").toBool(), true);
6077 QCOMPARE(o->property("test10").toBool(), true);
6084 TestHTTPServer server(8111);
6085 QVERIFY(server.isValid());
6086 server.serveDirectory(dataDirectory());
6088 QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml"));
6089 QObject *o = component.create();
6092 QTRY_VERIFY(o->property("done").toBool() == true);
6094 QCOMPARE(o->property("test1").toBool(), true);
6095 QCOMPARE(o->property("test2").toBool(), true);
6096 QCOMPARE(o->property("test3").toBool(), true);
6102 void tst_qqmlecmascript::signalHandlers()
6104 QQmlComponent component(&engine, testFileUrl("signalHandlers.qml"));
6105 QObject *o = component.create();
6108 QVERIFY(o->property("count").toInt() == 0);
6109 QMetaObject::invokeMethod(o, "testSignalCall");
6110 QCOMPARE(o->property("count").toInt(), 1);
6112 QMetaObject::invokeMethod(o, "testSignalHandlerCall");
6113 QCOMPARE(o->property("count").toInt(), 1);
6114 QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
6116 QVERIFY(o->property("funcCount").toInt() == 0);
6117 QMetaObject::invokeMethod(o, "testSignalConnection");
6118 QCOMPARE(o->property("funcCount").toInt(), 1);
6120 QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
6121 QCOMPARE(o->property("funcCount").toInt(), 2);
6123 QMetaObject::invokeMethod(o, "testSignalDefined");
6124 QCOMPARE(o->property("definedResult").toBool(), true);
6126 QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
6127 QCOMPARE(o->property("definedHandlerResult").toBool(), true);
6132 void tst_qqmlecmascript::qtbug_10696()
6134 QQmlComponent component(&engine, testFileUrl("qtbug_10696.qml"));
6135 QObject *o = component.create();
6140 void tst_qqmlecmascript::qtbug_11606()
6142 QQmlComponent component(&engine, testFileUrl("qtbug_11606.qml"));
6143 QObject *o = component.create();
6145 QCOMPARE(o->property("test").toBool(), true);
6149 void tst_qqmlecmascript::qtbug_11600()
6151 QQmlComponent component(&engine, testFileUrl("qtbug_11600.qml"));
6152 QObject *o = component.create();
6154 QCOMPARE(o->property("test").toBool(), true);
6158 void tst_qqmlecmascript::qtbug_21864()
6160 QQmlComponent component(&engine, testFileUrl("qtbug_21864.qml"));
6161 QObject *o = component.create();
6163 QCOMPARE(o->property("test").toBool(), true);
6167 void tst_qqmlecmascript::rewriteMultiLineStrings()
6171 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml"));
6172 QObject *o = component.create();
6174 QTRY_COMPARE(o->property("test").toBool(), true);
6179 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings_crlf.1.qml"));
6180 QObject *o = component.create();
6186 void tst_qqmlecmascript::qobjectConnectionListExceptionHandling()
6189 QQmlComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml"));
6190 QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined");
6191 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6192 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6193 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6194 QObject *o = component.create();
6196 QCOMPARE(o->property("test").toBool(), true);
6200 // Reading and writing non-scriptable properties should fail
6201 void tst_qqmlecmascript::nonscriptable()
6203 QQmlComponent component(&engine, testFileUrl("nonscriptable.qml"));
6204 QObject *o = component.create();
6206 QCOMPARE(o->property("readOk").toBool(), true);
6207 QCOMPARE(o->property("writeOk").toBool(), true);
6211 // deleteLater() should not be callable from QML
6212 void tst_qqmlecmascript::deleteLater()
6214 QQmlComponent component(&engine, testFileUrl("deleteLater.qml"));
6215 QObject *o = component.create();
6217 QCOMPARE(o->property("test").toBool(), true);
6221 // objectNameChanged() should be usable from QML
6222 void tst_qqmlecmascript::objectNameChangedSignal()
6224 QQmlComponent component(&engine, testFileUrl("objectNameChangedSignal.qml"));
6225 QObject *o = component.create();
6227 QCOMPARE(o->property("test").toBool(), false);
6228 o->setObjectName("obj");
6229 QCOMPARE(o->property("test").toBool(), true);
6233 // destroyed() should not be usable from QML
6234 void tst_qqmlecmascript::destroyedSignal()
6236 QQmlComponent component(&engine, testFileUrl("destroyedSignal.qml"));
6237 QVERIFY(component.isError());
6239 QString expectedErrorString = component.url().toString() + QLatin1String(":5:5: Cannot assign to non-existent property \"onDestroyed\"");
6240 QCOMPARE(component.errors().at(0).toString(), expectedErrorString);
6243 void tst_qqmlecmascript::in()
6245 QQmlComponent component(&engine, testFileUrl("in.qml"));
6246 QObject *o = component.create();
6248 QCOMPARE(o->property("test1").toBool(), true);
6249 QCOMPARE(o->property("test2").toBool(), true);
6253 void tst_qqmlecmascript::typeOf()
6255 QQmlComponent component(&engine, testFileUrl("typeOf.qml"));
6257 QObject *o = component.create();
6260 QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
6261 QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
6262 QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
6263 QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
6264 QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
6265 QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
6266 QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
6267 QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
6268 QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
6273 void tst_qqmlecmascript::qtbug_24448()
6275 QQmlComponent component(&engine, testFileUrl("qtbug_24448.qml"));
6276 QScopedPointer<QObject> o(component.create());
6278 QVERIFY(o->property("test").toBool());
6281 void tst_qqmlecmascript::sharedAttachedObject()
6283 QQmlComponent component(&engine, testFileUrl("sharedAttachedObject.qml"));
6284 QObject *o = component.create();
6286 QCOMPARE(o->property("test1").toBool(), true);
6287 QCOMPARE(o->property("test2").toBool(), true);
6292 void tst_qqmlecmascript::objectName()
6294 QQmlComponent component(&engine, testFileUrl("objectName.qml"));
6295 QObject *o = component.create();
6298 QCOMPARE(o->property("test1").toString(), QString("hello"));
6299 QCOMPARE(o->property("test2").toString(), QString("ell"));
6301 o->setObjectName("world");
6303 QCOMPARE(o->property("test1").toString(), QString("world"));
6304 QCOMPARE(o->property("test2").toString(), QString("orl"));
6309 void tst_qqmlecmascript::writeRemovesBinding()
6311 QQmlComponent component(&engine, testFileUrl("writeRemovesBinding.qml"));
6312 QObject *o = component.create();
6315 QCOMPARE(o->property("test").toBool(), true);
6320 // Test bindings assigned to alias properties actually assign to the alias' target
6321 void tst_qqmlecmascript::aliasBindingsAssignCorrectly()
6323 QQmlComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml"));
6324 QObject *o = component.create();
6327 QCOMPARE(o->property("test").toBool(), true);
6332 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
6333 void tst_qqmlecmascript::aliasBindingsOverrideTarget()
6336 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml"));
6337 QObject *o = component.create();
6340 QCOMPARE(o->property("test").toBool(), true);
6346 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml"));
6347 QObject *o = component.create();
6350 QCOMPARE(o->property("test").toBool(), true);
6356 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml"));
6357 QObject *o = component.create();
6360 QCOMPARE(o->property("test").toBool(), true);
6366 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
6367 void tst_qqmlecmascript::aliasWritesOverrideBindings()
6370 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml"));
6371 QObject *o = component.create();
6374 QCOMPARE(o->property("test").toBool(), true);
6380 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml"));
6381 QObject *o = component.create();
6384 QCOMPARE(o->property("test").toBool(), true);
6390 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml"));
6391 QObject *o = component.create();
6394 QCOMPARE(o->property("test").toBool(), true);
6400 // Allow an alais to a composite element
6402 void tst_qqmlecmascript::aliasToCompositeElement()
6404 QQmlComponent component(&engine, testFileUrl("aliasToCompositeElement.qml"));
6406 QObject *object = component.create();
6407 QVERIFY(object != 0);
6412 void tst_qqmlecmascript::qtbug_20344()
6414 QQmlComponent component(&engine, testFileUrl("qtbug_20344.qml"));
6416 QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
6417 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6419 QObject *object = component.create();
6420 QVERIFY(object != 0);
6425 void tst_qqmlecmascript::revisionErrors()
6428 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml"));
6429 QString url = component.url().toString();
6431 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6432 QString warning2 = url + ":11: ReferenceError: prop2 is not defined";
6433 QString warning3 = url + ":13: ReferenceError: method2 is not defined";
6435 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6436 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6437 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6438 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6439 QVERIFY(object != 0);
6443 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml"));
6444 QString url = component.url().toString();
6446 // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
6447 // method2, prop2 from MyRevisionedClass not available
6448 // method4, prop4 from MyRevisionedSubclass not available
6449 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6450 QString warning2 = url + ":14: ReferenceError: prop2 is not defined";
6451 QString warning3 = url + ":10: ReferenceError: prop4 is not defined";
6452 QString warning4 = url + ":16: ReferenceError: prop4 is not defined";
6453 QString warning5 = url + ":20: ReferenceError: method2 is not defined";
6455 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6456 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6457 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6458 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
6459 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
6460 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6461 QVERIFY(object != 0);
6465 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml"));
6466 QString url = component.url().toString();
6468 // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
6469 // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
6470 QString warning1 = url + ":30: ReferenceError: methodD is not defined";
6471 QString warning2 = url + ":10: ReferenceError: propD is not defined";
6472 QString warning3 = url + ":20: ReferenceError: propD is not defined";
6473 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6474 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6475 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6476 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6477 QVERIFY(object != 0);
6482 void tst_qqmlecmascript::revision()
6485 QQmlComponent component(&engine, testFileUrl("metaobjectRevision.qml"));
6486 QString url = component.url().toString();
6488 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6489 QVERIFY(object != 0);
6493 QQmlComponent component(&engine, testFileUrl("metaobjectRevision2.qml"));
6494 QString url = component.url().toString();
6496 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6497 QVERIFY(object != 0);
6501 QQmlComponent component(&engine, testFileUrl("metaobjectRevision3.qml"));
6502 QString url = component.url().toString();
6504 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6505 QVERIFY(object != 0);
6508 // Test that non-root classes can resolve revisioned methods
6510 QQmlComponent component(&engine, testFileUrl("metaobjectRevision4.qml"));
6512 QObject *object = component.create();
6513 QVERIFY(object != 0);
6514 QCOMPARE(object->property("test").toReal(), 11.);
6519 void tst_qqmlecmascript::realToInt()
6521 QQmlComponent component(&engine, testFileUrl("realToInt.qml"));
6522 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6523 QVERIFY(object != 0);
6525 QMetaObject::invokeMethod(object, "test1");
6526 QCOMPARE(object->value(), int(4));
6527 QMetaObject::invokeMethod(object, "test2");
6528 QCOMPARE(object->value(), int(8));
6531 void tst_qqmlecmascript::urlProperty()
6534 QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml"));
6535 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6536 QVERIFY(object != 0);
6537 object->setStringProperty("http://qt-project.org");
6538 QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
6539 QCOMPARE(object->intProperty(), 123);
6540 QCOMPARE(object->value(), 1);
6541 QCOMPARE(object->property("result").toBool(), true);
6545 void tst_qqmlecmascript::urlPropertyWithEncoding()
6548 QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml"));
6549 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6550 QVERIFY(object != 0);
6551 object->setStringProperty("http://qt-project.org");
6553 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6554 QCOMPARE(object->urlProperty(), encoded);
6555 QCOMPARE(object->value(), 0); // Interpreting URL as string yields canonicalised version
6556 QCOMPARE(object->property("result").toBool(), true);
6560 void tst_qqmlecmascript::urlListPropertyWithEncoding()
6563 QQmlComponent component(&engine, testFileUrl("urlListProperty.qml"));
6564 QObject *object = component.create();
6565 QVERIFY(object != 0);
6566 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
6567 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
6568 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
6569 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
6570 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0);
6572 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6573 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded));
6574 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded));
6575 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6576 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6581 void tst_qqmlecmascript::dynamicString()
6583 QQmlComponent component(&engine, testFileUrl("dynamicString.qml"));
6584 QObject *object = component.create();
6585 QVERIFY(object != 0);
6586 QCOMPARE(object->property("stringProperty").toString(),
6587 QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
6590 void tst_qqmlecmascript::deleteLaterObjectMethodCall()
6592 QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml"));
6593 QObject *object = component.create();
6594 QVERIFY(object != 0);
6597 void tst_qqmlecmascript::automaticSemicolon()
6599 QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml"));
6600 QObject *object = component.create();
6601 QVERIFY(object != 0);
6604 void tst_qqmlecmascript::unaryExpression()
6606 QQmlComponent component(&engine, testFileUrl("unaryExpression.qml"));
6607 QObject *object = component.create();
6608 QVERIFY(object != 0);
6611 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
6612 void tst_qqmlecmascript::doubleEvaluate()
6614 QQmlComponent component(&engine, testFileUrl("doubleEvaluate.qml"));
6615 QObject *object = component.create();
6616 QVERIFY(object != 0);
6617 WriteCounter *wc = qobject_cast<WriteCounter *>(object);
6619 QCOMPARE(wc->count(), 1);
6621 wc->setProperty("x", 9);
6623 QCOMPARE(wc->count(), 2);
6628 static QStringList messages;
6629 static void captureMsgHandler(QtMsgType, const char *msg)
6631 messages.append(QLatin1String(msg));
6634 void tst_qqmlecmascript::nonNotifyable()
6636 QV4Compiler::enableV4(false);
6637 QQmlComponent component(&engine, testFileUrl("nonNotifyable.qml"));
6638 QV4Compiler::enableV4(true);
6640 QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
6642 QObject *object = component.create();
6643 qInstallMsgHandler(old);
6645 QVERIFY(object != 0);
6647 QString expected1 = QLatin1String("QQmlExpression: Expression ") +
6648 component.url().toString() +
6649 QLatin1String(":5 depends on non-NOTIFYable properties:");
6650 QString expected2 = QLatin1String(" ") +
6651 QLatin1String(object->metaObject()->className()) +
6652 QLatin1String("::value");
6654 QCOMPARE(messages.length(), 2);
6655 QCOMPARE(messages.at(0), expected1);
6656 QCOMPARE(messages.at(1), expected2);
6661 void tst_qqmlecmascript::forInLoop()
6663 QQmlComponent component(&engine, testFileUrl("forInLoop.qml"));
6664 QObject *object = component.create();
6665 QVERIFY(object != 0);
6667 QMetaObject::invokeMethod(object, "listProperty");
6669 QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
6670 QCOMPARE(r.size(), 3);
6671 QCOMPARE(r[0],QLatin1String("0=obj1"));
6672 QCOMPARE(r[1],QLatin1String("1=obj2"));
6673 QCOMPARE(r[2],QLatin1String("2=obj3"));
6675 //TODO: should test for in loop for other objects (such as QObjects) as well.
6680 // An object the binding depends on is deleted while the binding is still running
6681 void tst_qqmlecmascript::deleteWhileBindingRunning()
6683 QQmlComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml"));
6684 QObject *object = component.create();
6685 QVERIFY(object != 0);
6689 void tst_qqmlecmascript::qtbug_22679()
6692 object.setStringProperty(QLatin1String("Please work correctly"));
6693 engine.rootContext()->setContextProperty("contextProp", &object);
6695 QQmlComponent component(&engine, testFileUrl("qtbug_22679.qml"));
6696 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6697 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6699 QObject *o = component.create();
6701 QCOMPARE(warningsSpy.count(), 0);
6705 void tst_qqmlecmascript::qtbug_22843_data()
6707 QTest::addColumn<bool>("library");
6709 QTest::newRow("without .pragma library") << false;
6710 QTest::newRow("with .pragma library") << true;
6713 void tst_qqmlecmascript::qtbug_22843()
6715 QFETCH(bool, library);
6717 QString fileName("qtbug_22843");
6719 fileName += QLatin1String(".library");
6720 fileName += QLatin1String(".qml");
6722 QQmlComponent component(&engine, testFileUrl(fileName));
6723 QString url = component.url().toString();
6724 QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )");
6725 QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'");
6727 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6728 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6729 for (int x = 0; x < 3; ++x) {
6730 warningsSpy.clear();
6731 // For libraries, only the first import attempt should produce a
6732 // SyntaxError warning; subsequent component creation should not
6733 // attempt to reload the script.
6734 bool expectSyntaxError = !library || (x == 0);
6735 if (expectSyntaxError)
6736 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
6737 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
6738 QObject *object = component.create();
6739 QVERIFY(object != 0);
6740 QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
6746 void tst_qqmlecmascript::switchStatement()
6749 QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml"));
6750 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6751 QVERIFY(object != 0);
6753 // `object->value()' is the number of executed statements
6755 object->setStringProperty("A");
6756 QCOMPARE(object->value(), 5);
6758 object->setStringProperty("S");
6759 QCOMPARE(object->value(), 3);
6761 object->setStringProperty("D");
6762 QCOMPARE(object->value(), 3);
6764 object->setStringProperty("F");
6765 QCOMPARE(object->value(), 4);
6767 object->setStringProperty("something else");
6768 QCOMPARE(object->value(), 1);
6772 QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml"));
6773 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6774 QVERIFY(object != 0);
6776 // `object->value()' is the number of executed statements
6778 object->setStringProperty("A");
6779 QCOMPARE(object->value(), 5);
6781 object->setStringProperty("S");
6782 QCOMPARE(object->value(), 3);
6784 object->setStringProperty("D");
6785 QCOMPARE(object->value(), 3);
6787 object->setStringProperty("F");
6788 QCOMPARE(object->value(), 3);
6790 object->setStringProperty("something else");
6791 QCOMPARE(object->value(), 4);
6795 QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml"));
6796 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6797 QVERIFY(object != 0);
6799 // `object->value()' is the number of executed statements
6801 object->setStringProperty("A");
6802 QCOMPARE(object->value(), 5);
6804 object->setStringProperty("S");
6805 QCOMPARE(object->value(), 3);
6807 object->setStringProperty("D");
6808 QCOMPARE(object->value(), 3);
6810 object->setStringProperty("F");
6811 QCOMPARE(object->value(), 3);
6813 object->setStringProperty("something else");
6814 QCOMPARE(object->value(), 6);
6818 QQmlComponent component(&engine, testFileUrl("switchStatement.4.qml"));
6820 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
6821 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6823 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6824 QVERIFY(object != 0);
6826 // `object->value()' is the number of executed statements
6828 object->setStringProperty("A");
6829 QCOMPARE(object->value(), 5);
6831 object->setStringProperty("S");
6832 QCOMPARE(object->value(), 3);
6834 object->setStringProperty("D");
6835 QCOMPARE(object->value(), 3);
6837 object->setStringProperty("F");
6838 QCOMPARE(object->value(), 3);
6840 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6842 object->setStringProperty("something else");
6846 QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml"));
6847 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6848 QVERIFY(object != 0);
6850 // `object->value()' is the number of executed statements
6852 object->setStringProperty("A");
6853 QCOMPARE(object->value(), 1);
6855 object->setStringProperty("S");
6856 QCOMPARE(object->value(), 1);
6858 object->setStringProperty("D");
6859 QCOMPARE(object->value(), 1);
6861 object->setStringProperty("F");
6862 QCOMPARE(object->value(), 1);
6864 object->setStringProperty("something else");
6865 QCOMPARE(object->value(), 1);
6869 QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml"));
6870 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6871 QVERIFY(object != 0);
6873 // `object->value()' is the number of executed statements
6875 object->setStringProperty("A");
6876 QCOMPARE(object->value(), 123);
6878 object->setStringProperty("S");
6879 QCOMPARE(object->value(), 123);
6881 object->setStringProperty("D");
6882 QCOMPARE(object->value(), 321);
6884 object->setStringProperty("F");
6885 QCOMPARE(object->value(), 321);
6887 object->setStringProperty("something else");
6888 QCOMPARE(object->value(), 0);
6892 void tst_qqmlecmascript::withStatement()
6895 QQmlComponent component(&engine, testFileUrl("withStatement.1.qml"));
6896 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6897 QVERIFY(object != 0);
6899 QCOMPARE(object->value(), 123);
6903 void tst_qqmlecmascript::tryStatement()
6906 QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml"));
6907 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6908 QVERIFY(object != 0);
6910 QCOMPARE(object->value(), 123);
6914 QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml"));
6915 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6916 QVERIFY(object != 0);
6918 QCOMPARE(object->value(), 321);
6922 QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml"));
6923 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6924 QVERIFY(object != 0);
6926 QCOMPARE(object->value(), 1);
6930 QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml"));
6931 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6932 QVERIFY(object != 0);
6934 QCOMPARE(object->value(), 1);
6938 class CppInvokableWithQObjectDerived : public QObject
6942 CppInvokableWithQObjectDerived() {}
6943 ~CppInvokableWithQObjectDerived() {}
6945 Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data)
6947 MyQmlObject *obj = new MyQmlObject();
6948 obj->setStringProperty(data);
6952 Q_INVOKABLE QString getStringProperty(MyQmlObject *obj)
6954 return obj->stringProperty();
6958 void tst_qqmlecmascript::invokableWithQObjectDerived()
6960 CppInvokableWithQObjectDerived invokable;
6964 engine.rootContext()->setContextProperty("invokable", &invokable);
6966 QQmlComponent component(&engine, testFileUrl("qobjectDerivedArgument.qml"));
6968 QObject *object = component.create();
6970 QVERIFY(object != 0);
6971 QVERIFY(object->property("result").value<bool>() == true);
6977 void tst_qqmlecmascript::realTypePrecision()
6979 // Properties and signal parameters of type real should have double precision.
6980 QQmlComponent component(&engine, testFileUrl("realTypePrecision.qml"));
6981 QScopedPointer<QObject> object(component.create());
6982 QVERIFY(object != 0);
6983 QCOMPARE(object->property("test").toDouble(), 1234567890.);
6984 QCOMPARE(object->property("test2").toDouble(), 1234567890.);
6985 QCOMPARE(object->property("test3").toDouble(), 1234567890.);
6986 QCOMPARE(object->property("test4").toDouble(), 1234567890.);
6987 QCOMPARE(object->property("test5").toDouble(), 1234567890.);
6988 QCOMPARE(object->property("test6").toDouble(), 1234567890.*2);
6991 void tst_qqmlecmascript::registeredFlagMethod()
6994 QQmlComponent component(&engine, testFileUrl("registeredFlagMethod.qml"));
6995 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6996 QVERIFY(object != 0);
6998 QCOMPARE(object->buttons(), 0);
6999 emit object->basicSignal();
7000 QCOMPARE(object->buttons(), Qt::RightButton);
7006 void tst_qqmlecmascript::replaceBinding()
7009 QQmlComponent c(&engine, testFileUrl("replaceBinding.qml"));
7010 QObject *obj = c.create();
7013 QVERIFY(obj->property("success").toBool());
7017 void tst_qqmlecmascript::deleteRootObjectInCreation()
7021 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.qml"));
7022 QObject *obj = c.create();
7024 QVERIFY(obj->property("rootIndestructible").toBool());
7025 QVERIFY(!obj->property("childDestructible").toBool());
7027 QVERIFY(obj->property("childDestructible").toBool());
7032 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.2.qml"));
7033 QObject *object = c.create();
7034 QVERIFY(object != 0);
7035 QVERIFY(object->property("testConditionsMet").toBool());
7040 void tst_qqmlecmascript::onDestruction()
7043 // Delete object manually to invoke the associated handlers,
7044 // prior to engine destruction.
7046 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
7047 QObject *obj = c.create();
7053 // In this case, the teardown of the engine causes deletion
7054 // of contexts and child items. This triggers the
7055 // onDestruction handler of a (previously .destroy()ed)
7056 // component instance. This shouldn't crash.
7058 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
7059 QObject *obj = c.create();
7064 struct EventProcessor : public QObject
7068 Q_INVOKABLE void process()
7070 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
7071 QCoreApplication::processEvents();
7075 void tst_qqmlecmascript::bindingSuppression()
7078 EventProcessor processor;
7079 engine.rootContext()->setContextProperty("pendingEvents", &processor);
7081 transientErrorsMsgCount = 0;
7082 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
7084 QQmlComponent c(&engine, testFileUrl("bindingSuppression.qml"));
7085 QObject *obj = c.create();
7089 qInstallMsgHandler(old);
7090 QCOMPARE(transientErrorsMsgCount, 0);
7093 void tst_qqmlecmascript::signalEmitted()
7096 // calling destroy on the sibling.
7098 QQmlComponent c(&engine, testFileUrl("signalEmitted.2.qml"));
7099 QObject *obj = c.create();
7101 QTRY_VERIFY(obj->property("success").toBool());
7106 // allowing gc to clean up the sibling.
7108 QQmlComponent c(&engine, testFileUrl("signalEmitted.3.qml"));
7109 QObject *obj = c.create();
7111 gc(engine); // should collect c1.
7112 QTRY_VERIFY(obj->property("success").toBool());
7117 // allowing gc to clean up the sibling after manually destroying target.
7119 QQmlComponent c(&engine, testFileUrl("signalEmitted.4.qml"));
7120 QObject *obj = c.create();
7122 gc(engine); // should collect c1.
7123 QMetaObject::invokeMethod(obj, "destroyC2");
7124 QTRY_VERIFY(obj->property("success").toBool()); // handles events (incl. delete later).
7130 void tst_qqmlecmascript::threadSignal()
7133 QQmlComponent c(&engine, testFileUrl("threadSignal.qml"));
7134 QObject *object = c.create();
7135 QVERIFY(object != 0);
7136 QTRY_VERIFY(object->property("passed").toBool());
7140 QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml"));
7141 QObject *object = c.create();
7142 QVERIFY(object != 0);
7143 QSignalSpy doneSpy(object, SIGNAL(done(const QString &)));
7144 QMetaObject::invokeMethod(object, "doIt");
7145 QTRY_VERIFY(object->property("passed").toBool());
7146 QCOMPARE(doneSpy.count(), 1);
7151 // ensure that the qqmldata::destroyed() handler doesn't cause problems
7152 void tst_qqmlecmascript::qqmldataDestroyed()
7154 // gc cleans up a qobject, later the qqmldata destroyed handler will run.
7156 QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.qml"));
7157 QObject *object = c.create();
7158 QVERIFY(object != 0);
7159 // now gc causing the collection of the dynamically constructed object.
7160 engine.collectGarbage();
7161 engine.collectGarbage();
7162 // now process events to allow deletion (calling qqmldata::destroyed())
7163 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
7164 QCoreApplication::processEvents();
7169 // in this case, the object has CPP ownership, and the gc will
7170 // be triggered during its beginCreate stage.
7172 QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.2.qml"));
7173 QObject *object = c.create();
7174 QVERIFY(object != 0);
7175 QVERIFY(object->property("testConditionsMet").toBool());
7176 // the gc() within the handler will have triggered the weak
7177 // qobject reference callback. If that incorrectly disposes
7178 // the handle, when the qqmldata::destroyed() handler is
7179 // called due to object deletion we will see a crash.
7181 // shouldn't have crashed.
7185 void tst_qqmlecmascript::secondAlias()
7187 QQmlComponent c(&engine, testFileUrl("secondAlias.qml"));
7188 QObject *object = c.create();
7189 QVERIFY(object != 0);
7190 QCOMPARE(object->property("test").toInt(), 200);
7194 // An alias to a var property works
7195 void tst_qqmlecmascript::varAlias()
7197 QQmlComponent c(&engine, testFileUrl("varAlias.qml"));
7198 QObject *object = c.create();
7199 QVERIFY(object != 0);
7200 QCOMPARE(object->property("test").toInt(), 192);
7204 // Used to trigger an assert in the lazy meta object creation stage
7205 void tst_qqmlecmascript::overrideDataAssert()
7207 QQmlComponent c(&engine, testFileUrl("overrideDataAssert.qml"));
7208 QObject *object = c.create();
7209 QVERIFY(object != 0);
7210 object->metaObject();
7214 void tst_qqmlecmascript::fallbackBindings_data()
7216 QTest::addColumn<QString>("source");
7218 QTest::newRow("Property without fallback") << "fallbackBindings.1.qml";
7219 QTest::newRow("Property fallback") << "fallbackBindings.2.qml";
7220 QTest::newRow("SingletonType without fallback") << "fallbackBindings.3.qml";
7221 QTest::newRow("SingletonType fallback") << "fallbackBindings.4.qml";
7222 QTest::newRow("Attached without fallback") << "fallbackBindings.5.qml";
7223 QTest::newRow("Attached fallback") << "fallbackBindings.6.qml";
7226 void tst_qqmlecmascript::fallbackBindings()
7228 QFETCH(QString, source);
7230 QQmlComponent component(&engine, testFileUrl(source));
7231 QScopedPointer<QObject> object(component.create());
7232 QVERIFY(object != 0);
7234 QCOMPARE(object->property("success").toBool(), true);
7237 void tst_qqmlecmascript::sequenceSort_data()
7239 QTest::addColumn<QString>("function");
7240 QTest::addColumn<bool>("useComparer");
7242 QTest::newRow("qtbug_25269") << "test_qtbug_25269" << false;
7244 const char *types[] = { "alphabet", "numbers", "reals" };
7245 const char *sort[] = { "insertionSort", "quickSort" };
7247 for (size_t t=0 ; t < sizeof(types)/sizeof(types[0]) ; ++t) {
7248 for (size_t s=0 ; s < sizeof(sort)/sizeof(sort[0]) ; ++s) {
7249 for (int c=0 ; c < 2 ; ++c) {
7250 QString testName = QLatin1String(types[t]) + QLatin1String("_") + QLatin1String(sort[s]);
7251 QString fnName = QLatin1String("test_") + testName;
7252 bool useComparer = c != 0;
7253 testName += useComparer ? QLatin1String("[custom]") : QLatin1String("[default]");
7254 QTest::newRow(testName.toAscii().constData()) << fnName << useComparer;
7260 void tst_qqmlecmascript::sequenceSort()
7262 QFETCH(QString, function);
7263 QFETCH(bool, useComparer);
7265 QQmlComponent component(&engine, testFileUrl("sequenceSort.qml"));
7267 QObject *object = component.create();
7269 qDebug() << component.errorString();
7270 QVERIFY(object != 0);
7273 QMetaObject::invokeMethod(object, function.toAscii().constData(), Q_RETURN_ARG(QVariant, q), Q_ARG(QVariant, useComparer));
7274 QVERIFY(q.toBool() == true);
7279 QTEST_MAIN(tst_qqmlecmascript)
7281 #include "tst_qqmlecmascript.moc"