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();
82 void signalArguments();
84 void basicExpressions();
85 void basicExpressions_data();
86 void arrayExpressions();
87 void contextPropertiesTriggerReeval();
88 void objectPropertiesTriggerReeval();
89 void deferredProperties();
90 void deferredPropertiesErrors();
91 void extensionObjects();
92 void overrideExtensionProperties();
93 void attachedProperties();
95 void valueTypeFunctions();
96 void constantsOverrideBindings();
97 void outerBindingOverridesInnerBinding();
98 void aliasPropertyAndBinding();
99 void aliasPropertyReset();
100 void nonExistentAttachedObject();
103 void signalParameterTypes();
104 void objectsCompareAsEqual();
105 void componentCreation_data();
106 void componentCreation();
107 void dynamicCreation_data();
108 void dynamicCreation();
109 void dynamicDestruction();
110 void objectToString();
111 void objectHasOwnProperty();
112 void selfDeletingBinding();
113 void extendedObjectPropertyLookup();
114 void extendedObjectPropertyLookup2();
116 void functionErrors();
117 void propertyAssignmentErrors();
118 void signalTriggeredBindings();
119 void listProperties();
120 void exceptionClearsOnReeval();
121 void exceptionSlotProducesWarning();
122 void exceptionBindingProducesWarning();
123 void compileInvalidBinding();
124 void transientErrors();
125 void shutdownErrors();
126 void compositePropertyType();
128 void undefinedResetsProperty();
129 void listToVariant();
130 void listAssignment();
131 void multiEngineObject();
132 void deletedObject();
133 void attachedPropertyScope();
134 void scriptConnect();
135 void scriptDisconnect();
137 void cppOwnershipReturnValue();
138 void ownershipCustomReturnValue();
139 void ownershipRootObject();
140 void ownershipConsistency();
141 void ownershipQmlIncubated();
142 void qlistqobjectMethods();
143 void strictlyEquals();
145 void numberAssignment();
146 void propertySplicing();
147 void signalWithUnknownTypes();
148 void signalWithJSValueInVariant_data();
149 void signalWithJSValueInVariant();
150 void signalWithJSValueInVariant_twoEngines_data();
151 void signalWithJSValueInVariant_twoEngines();
152 void signalWithQJSValue_data();
153 void signalWithQJSValue();
154 void singletonType_data();
155 void singletonType();
156 void singletonTypeCaching_data();
157 void singletonTypeCaching();
158 void singletonTypeImportOrder();
159 void singletonTypeResolution();
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();
284 void propertyOverride();
285 void concatenatedStringPropertyAccess();
288 static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
292 void tst_qqmlecmascript::initTestCase()
294 QQmlDataTest::initTestCase();
297 QString dataDir(dataDirectory() + QLatin1Char('/') + QLatin1String("lib"));
298 engine.addImportPath(dataDir);
301 void tst_qqmlecmascript::assignBasicTypes()
304 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.qml"));
305 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
306 QVERIFY(object != 0);
307 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
308 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
309 QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue);
310 QCOMPARE(object->stringProperty(), QString("Hello World!"));
311 QCOMPARE(object->uintProperty(), uint(10));
312 QCOMPARE(object->intProperty(), -19);
313 QCOMPARE((float)object->realProperty(), float(23.2));
314 QCOMPARE((float)object->doubleProperty(), float(-19.75));
315 QCOMPARE((float)object->floatProperty(), float(8.5));
316 QCOMPARE(object->colorProperty(), QColor("red"));
317 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
318 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
319 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
320 QCOMPARE(object->pointProperty(), QPoint(99,13));
321 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
322 QCOMPARE(object->sizeProperty(), QSize(99, 13));
323 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
324 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
325 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
326 QCOMPARE(object->boolProperty(), true);
327 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
328 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
329 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
333 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.2.qml"));
334 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
335 QVERIFY(object != 0);
336 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
337 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
338 QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue);
339 QCOMPARE(object->stringProperty(), QString("Hello World!"));
340 QCOMPARE(object->uintProperty(), uint(10));
341 QCOMPARE(object->intProperty(), -19);
342 QCOMPARE((float)object->realProperty(), float(23.2));
343 QCOMPARE((float)object->doubleProperty(), float(-19.75));
344 QCOMPARE((float)object->floatProperty(), float(8.5));
345 QCOMPARE(object->colorProperty(), QColor("red"));
346 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
347 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
348 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
349 QCOMPARE(object->pointProperty(), QPoint(99,13));
350 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
351 QCOMPARE(object->sizeProperty(), QSize(99, 13));
352 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
353 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
354 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
355 QCOMPARE(object->boolProperty(), true);
356 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
357 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
358 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
363 void tst_qqmlecmascript::assignDate_data()
365 QTest::addColumn<QUrl>("source");
367 QTest::newRow("Component.onComplete JS Parse") << testFileUrl("assignDate.qml");
368 QTest::newRow("Component.onComplete JS") << testFileUrl("assignDate.1.qml");
369 QTest::newRow("Binding JS") << testFileUrl("assignDate.2.qml");
370 QTest::newRow("Binding UTC") << testFileUrl("assignDate.3.qml");
371 QTest::newRow("Binding JS UTC") << testFileUrl("assignDate.4.qml");
372 QTest::newRow("Binding UTC+2") << testFileUrl("assignDate.5.qml");
373 QTest::newRow("Binding JS UTC+2 ") << testFileUrl("assignDate.6.qml");
376 void tst_qqmlecmascript::assignDate()
378 QFETCH(QUrl, source);
380 QQmlComponent component(&engine, source);
381 QScopedPointer<QObject> obj(component.create());
382 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
383 QVERIFY(object != 0);
385 // Dates received from JS are automatically converted to local time
386 QDate expectedDate(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 0), Qt::UTC).toLocalTime().date());
387 QDateTime expectedDateTime(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 1), Qt::UTC).toLocalTime());
388 QDateTime expectedDateTime2(QDateTime(QDate(2009, 5, 12), QTime(23, 59, 59), Qt::UTC).toLocalTime());
390 QCOMPARE(object->dateProperty(), expectedDate);
391 QCOMPARE(object->dateTimeProperty(), expectedDateTime);
392 QCOMPARE(object->dateTimeProperty2(), expectedDateTime2);
393 QCOMPARE(object->boolProperty(), true);
396 void tst_qqmlecmascript::exportDate_data()
398 QTest::addColumn<QUrl>("source");
399 QTest::addColumn<QDateTime>("datetime");
401 // Verify that we can export datetime information to QML and that consumers can access
402 // the data correctly provided they know the TZ info associated with the value
404 const QDate date(2009, 5, 12);
405 const QTime early(0, 0, 1);
406 const QTime late(23, 59, 59);
407 const int offset(((11 * 60) + 30) * 60);
409 QTest::newRow("Localtime early") << testFileUrl("exportDate.qml") << QDateTime(date, early, Qt::LocalTime);
410 QTest::newRow("Localtime late") << testFileUrl("exportDate.2.qml") << QDateTime(date, late, Qt::LocalTime);
411 QTest::newRow("UTC early") << testFileUrl("exportDate.3.qml") << QDateTime(date, early, Qt::UTC);
412 QTest::newRow("UTC late") << testFileUrl("exportDate.4.qml") << QDateTime(date, late, Qt::UTC);
414 QDateTime dt(date, early, Qt::OffsetFromUTC);
415 dt.setUtcOffset(offset);
416 QTest::newRow("+11:30 early") << testFileUrl("exportDate.5.qml") << dt;
419 QDateTime dt(date, late, Qt::OffsetFromUTC);
420 dt.setUtcOffset(offset);
421 QTest::newRow("+11:30 late") << testFileUrl("exportDate.6.qml") << dt;
424 QDateTime dt(date, early, Qt::OffsetFromUTC);
425 dt.setUtcOffset(-offset);
426 QTest::newRow("-11:30 early") << testFileUrl("exportDate.7.qml") << dt;
429 QDateTime dt(date, late, Qt::OffsetFromUTC);
430 dt.setUtcOffset(-offset);
431 QTest::newRow("-11:30 late") << testFileUrl("exportDate.8.qml") << dt;
435 void tst_qqmlecmascript::exportDate()
437 QFETCH(QUrl, source);
438 QFETCH(QDateTime, datetime);
440 DateTimeExporter exporter(datetime);
443 e.rootContext()->setContextProperty("datetimeExporter", &exporter);
445 QQmlComponent component(&e, source);
446 QScopedPointer<QObject> obj(component.create());
447 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
448 QVERIFY(object != 0);
449 QCOMPARE(object->boolProperty(), true);
452 void tst_qqmlecmascript::idShortcutInvalidates()
455 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.qml"));
456 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
457 QVERIFY(object != 0);
458 QVERIFY(object->objectProperty() != 0);
459 delete object->objectProperty();
460 QVERIFY(object->objectProperty() == 0);
465 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.1.qml"));
466 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
467 QVERIFY(object != 0);
468 QVERIFY(object->objectProperty() != 0);
469 delete object->objectProperty();
470 QVERIFY(object->objectProperty() == 0);
475 void tst_qqmlecmascript::boolPropertiesEvaluateAsBool()
478 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.1.qml"));
479 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
480 QVERIFY(object != 0);
481 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
485 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.2.qml"));
486 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
487 QVERIFY(object != 0);
488 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
493 void tst_qqmlecmascript::signalAssignment()
496 QQmlComponent component(&engine, testFileUrl("signalAssignment.1.qml"));
497 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
498 QVERIFY(object != 0);
499 QCOMPARE(object->string(), QString());
500 emit object->basicSignal();
501 QCOMPARE(object->string(), QString("pass"));
506 QQmlComponent component(&engine, testFileUrl("signalAssignment.2.qml"));
507 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
508 QVERIFY(object != 0);
509 QCOMPARE(object->string(), QString());
510 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
511 QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
516 QQmlComponent component(&engine, testFileUrl("signalAssignment.3.qml"));
517 QVERIFY(component.isError());
518 QString expectedErrorString = component.url().toString() + QLatin1String(":4 Signal uses unnamed parameter followed by named parameter.\n");
519 QCOMPARE(component.errorString(), expectedErrorString);
523 QQmlComponent component(&engine, testFileUrl("signalAssignment.4.qml"));
524 QVERIFY(component.isError());
525 QString expectedErrorString = component.url().toString() + QLatin1String(":5 Signal parameter \"parseInt\" hides global variable.\n");
526 QCOMPARE(component.errorString(), expectedErrorString);
530 void tst_qqmlecmascript::signalArguments()
533 QQmlComponent component(&engine, testFileUrl("signalArguments.1.qml"));
534 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
535 QVERIFY(object != 0);
536 QCOMPARE(object->string(), QString());
537 emit object->basicSignal();
538 QCOMPARE(object->string(), QString("pass"));
539 QCOMPARE(object->property("argumentCount").toInt(), 0);
540 QCOMPARE(object->property("calleeCorrect").toBool(), true);
545 QQmlComponent component(&engine, testFileUrl("signalArguments.2.qml"));
546 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
547 QVERIFY(object != 0);
548 QCOMPARE(object->string(), QString());
549 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
550 QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
551 QCOMPARE(object->property("argumentCount").toInt(), 5);
552 QCOMPARE(object->property("calleeCorrect").toBool(), true);
557 void tst_qqmlecmascript::methods()
560 QQmlComponent component(&engine, testFileUrl("methods.1.qml"));
561 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
562 QVERIFY(object != 0);
563 QCOMPARE(object->methodCalled(), false);
564 QCOMPARE(object->methodIntCalled(), false);
565 emit object->basicSignal();
566 QCOMPARE(object->methodCalled(), true);
567 QCOMPARE(object->methodIntCalled(), false);
572 QQmlComponent component(&engine, testFileUrl("methods.2.qml"));
573 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
574 QVERIFY(object != 0);
575 QCOMPARE(object->methodCalled(), false);
576 QCOMPARE(object->methodIntCalled(), false);
577 emit object->basicSignal();
578 QCOMPARE(object->methodCalled(), false);
579 QCOMPARE(object->methodIntCalled(), true);
584 QQmlComponent component(&engine, testFileUrl("methods.3.qml"));
585 QObject *object = component.create();
586 QVERIFY(object != 0);
587 QCOMPARE(object->property("test").toInt(), 19);
592 QQmlComponent component(&engine, testFileUrl("methods.4.qml"));
593 QObject *object = component.create();
594 QVERIFY(object != 0);
595 QCOMPARE(object->property("test").toInt(), 19);
596 QCOMPARE(object->property("test2").toInt(), 17);
597 QCOMPARE(object->property("test3").toInt(), 16);
602 QQmlComponent component(&engine, testFileUrl("methods.5.qml"));
603 QObject *object = component.create();
604 QVERIFY(object != 0);
605 QCOMPARE(object->property("test").toInt(), 9);
610 void tst_qqmlecmascript::bindingLoop()
612 QQmlComponent component(&engine, testFileUrl("bindingLoop.qml"));
613 QString warning = component.url().toString() + ":5:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\"";
614 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
615 QObject *object = component.create();
616 QVERIFY(object != 0);
620 void tst_qqmlecmascript::basicExpressions_data()
622 QTest::addColumn<QString>("expression");
623 QTest::addColumn<QVariant>("result");
624 QTest::addColumn<bool>("nest");
626 QTest::newRow("Syntax error (self test)") << "{console.log({'a':1'}.a)}" << QVariant() << false;
627 QTest::newRow("Context property") << "a" << QVariant(1944) << false;
628 QTest::newRow("Context property") << "a" << QVariant(1944) << true;
629 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << false;
630 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << true;
631 QTest::newRow("Overridden context property") << "b" << QVariant("Milk") << false;
632 QTest::newRow("Overridden context property") << "b" << QVariant("Cow") << true;
633 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << false;
634 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << true;
635 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object2") << false;
636 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object3") << true;
637 QTest::newRow("Default object property") << "horseLegs" << QVariant(4) << false;
638 QTest::newRow("Default object property") << "antLegs" << QVariant(6) << false;
639 QTest::newRow("Default object property") << "emuLegs" << QVariant(2) << false;
640 QTest::newRow("Nested default object property") << "horseLegs" << QVariant(4) << true;
641 QTest::newRow("Nested default object property") << "antLegs" << QVariant(7) << true;
642 QTest::newRow("Nested default object property") << "emuLegs" << QVariant(2) << true;
643 QTest::newRow("Nested default object property") << "humanLegs" << QVariant(2) << true;
644 QTest::newRow("Context property override default object property") << "millipedeLegs" << QVariant(100) << true;
647 void tst_qqmlecmascript::basicExpressions()
649 QFETCH(QString, expression);
650 QFETCH(QVariant, result);
656 MyDefaultObject1 default1;
657 MyDefaultObject3 default3;
658 object1.setStringProperty("Object1");
659 object2.setStringProperty("Object2");
660 object3.setStringProperty("Object3");
662 QQmlContext context(engine.rootContext());
663 QQmlContext nestedContext(&context);
665 context.setContextObject(&default1);
666 context.setContextProperty("a", QVariant(1944));
667 context.setContextProperty("b", QVariant("Milk"));
668 context.setContextProperty("object", &object1);
669 context.setContextProperty("objectOverride", &object2);
670 nestedContext.setContextObject(&default3);
671 nestedContext.setContextProperty("b", QVariant("Cow"));
672 nestedContext.setContextProperty("objectOverride", &object3);
673 nestedContext.setContextProperty("millipedeLegs", QVariant(100));
675 MyExpression expr(nest?&nestedContext:&context, expression);
676 QCOMPARE(expr.evaluate(), result);
679 void tst_qqmlecmascript::arrayExpressions()
685 QQmlContext context(engine.rootContext());
686 context.setContextProperty("a", &obj1);
687 context.setContextProperty("b", &obj2);
688 context.setContextProperty("c", &obj3);
690 MyExpression expr(&context, "[a, b, c, 10]");
691 QVariant result = expr.evaluate();
692 QCOMPARE(result.userType(), qMetaTypeId<QVariantList>());
693 QVariantList list = qvariant_cast<QVariantList>(result);
694 QCOMPARE(list.count(), 4);
695 QCOMPARE(list.at(0).value<QObject*>(), &obj1);
696 QCOMPARE(list.at(1).value<QObject*>(), &obj2);
697 QCOMPARE(list.at(2).value<QObject*>(), &obj3);
698 QCOMPARE(list.at(3).value<int>(), 10);
701 // Tests that modifying a context property will reevaluate expressions
702 void tst_qqmlecmascript::contextPropertiesTriggerReeval()
704 QQmlContext context(engine.rootContext());
707 MyQmlObject *object3 = new MyQmlObject;
709 object1.setStringProperty("Hello");
710 object2.setStringProperty("World");
712 context.setContextProperty("testProp", QVariant(1));
713 context.setContextProperty("testObj", &object1);
714 context.setContextProperty("testObj2", object3);
717 MyExpression expr(&context, "testProp + 1");
718 QCOMPARE(expr.changed, false);
719 QCOMPARE(expr.evaluate(), QVariant(2));
721 context.setContextProperty("testProp", QVariant(2));
722 QCOMPARE(expr.changed, true);
723 QCOMPARE(expr.evaluate(), QVariant(3));
727 MyExpression expr(&context, "testProp + testProp + testProp");
728 QCOMPARE(expr.changed, false);
729 QCOMPARE(expr.evaluate(), QVariant(6));
731 context.setContextProperty("testProp", QVariant(4));
732 QCOMPARE(expr.changed, true);
733 QCOMPARE(expr.evaluate(), QVariant(12));
737 MyExpression expr(&context, "testObj.stringProperty");
738 QCOMPARE(expr.changed, false);
739 QCOMPARE(expr.evaluate(), QVariant("Hello"));
741 context.setContextProperty("testObj", &object2);
742 QCOMPARE(expr.changed, true);
743 QCOMPARE(expr.evaluate(), QVariant("World"));
747 MyExpression expr(&context, "testObj.stringProperty /**/");
748 QCOMPARE(expr.changed, false);
749 QCOMPARE(expr.evaluate(), QVariant("World"));
751 context.setContextProperty("testObj", &object1);
752 QCOMPARE(expr.changed, true);
753 QCOMPARE(expr.evaluate(), QVariant("Hello"));
757 MyExpression expr(&context, "testObj2");
758 QCOMPARE(expr.changed, false);
759 QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3));
765 void tst_qqmlecmascript::objectPropertiesTriggerReeval()
767 QQmlContext context(engine.rootContext());
771 context.setContextProperty("testObj", &object1);
773 object1.setStringProperty(QLatin1String("Hello"));
774 object2.setStringProperty(QLatin1String("Dog"));
775 object3.setStringProperty(QLatin1String("Cat"));
778 MyExpression expr(&context, "testObj.stringProperty");
779 QCOMPARE(expr.changed, false);
780 QCOMPARE(expr.evaluate(), QVariant("Hello"));
782 object1.setStringProperty(QLatin1String("World"));
783 QCOMPARE(expr.changed, true);
784 QCOMPARE(expr.evaluate(), QVariant("World"));
788 MyExpression expr(&context, "testObj.objectProperty.stringProperty");
789 QCOMPARE(expr.changed, false);
790 QCOMPARE(expr.evaluate(), QVariant());
792 object1.setObjectProperty(&object2);
793 QCOMPARE(expr.changed, true);
794 expr.changed = false;
795 QCOMPARE(expr.evaluate(), QVariant("Dog"));
797 object1.setObjectProperty(&object3);
798 QCOMPARE(expr.changed, true);
799 expr.changed = false;
800 QCOMPARE(expr.evaluate(), QVariant("Cat"));
802 object1.setObjectProperty(0);
803 QCOMPARE(expr.changed, true);
804 expr.changed = false;
805 QCOMPARE(expr.evaluate(), QVariant());
807 object1.setObjectProperty(&object3);
808 QCOMPARE(expr.changed, true);
809 expr.changed = false;
810 QCOMPARE(expr.evaluate(), QVariant("Cat"));
812 object3.setStringProperty("Donkey");
813 QCOMPARE(expr.changed, true);
814 expr.changed = false;
815 QCOMPARE(expr.evaluate(), QVariant("Donkey"));
819 void tst_qqmlecmascript::deferredProperties()
821 QQmlComponent component(&engine, testFileUrl("deferredProperties.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);
828 qmlExecuteDeferred(object);
829 QCOMPARE(object->value(), 10);
830 QVERIFY(object->objectProperty() != 0);
831 MyQmlObject *qmlObject =
832 qobject_cast<MyQmlObject *>(object->objectProperty());
833 QVERIFY(qmlObject != 0);
834 QCOMPARE(qmlObject->value(), 10);
835 object->setValue(19);
836 QCOMPARE(qmlObject->value(), 19);
841 // Check errors on deferred properties are correctly emitted
842 void tst_qqmlecmascript::deferredPropertiesErrors()
844 QQmlComponent component(&engine, testFileUrl("deferredPropertiesErrors.qml"));
845 MyDeferredObject *object =
846 qobject_cast<MyDeferredObject *>(component.create());
847 QVERIFY(object != 0);
848 QCOMPARE(object->value(), 0);
849 QVERIFY(object->objectProperty() == 0);
850 QVERIFY(object->objectProperty2() == 0);
852 QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject*";
853 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
855 qmlExecuteDeferred(object);
860 void tst_qqmlecmascript::extensionObjects()
862 QQmlComponent component(&engine, testFileUrl("extensionObjects.qml"));
863 MyExtendedObject *object =
864 qobject_cast<MyExtendedObject *>(component.create());
865 QVERIFY(object != 0);
866 QCOMPARE(object->baseProperty(), 13);
867 QCOMPARE(object->coreProperty(), 9);
868 object->setProperty("extendedProperty", QVariant(11));
869 object->setProperty("baseExtendedProperty", QVariant(92));
870 QCOMPARE(object->coreProperty(), 11);
871 QCOMPARE(object->baseProperty(), 92);
873 MyExtendedObject *nested = qobject_cast<MyExtendedObject*>(qvariant_cast<QObject *>(object->property("nested")));
875 QCOMPARE(nested->baseProperty(), 13);
876 QCOMPARE(nested->coreProperty(), 9);
877 nested->setProperty("extendedProperty", QVariant(11));
878 nested->setProperty("baseExtendedProperty", QVariant(92));
879 QCOMPARE(nested->coreProperty(), 11);
880 QCOMPARE(nested->baseProperty(), 92);
885 void tst_qqmlecmascript::overrideExtensionProperties()
887 QQmlComponent component(&engine, testFileUrl("extensionObjectsPropertyOverride.qml"));
888 OverrideDefaultPropertyObject *object =
889 qobject_cast<OverrideDefaultPropertyObject *>(component.create());
890 QVERIFY(object != 0);
891 QVERIFY(object->secondProperty() != 0);
892 QVERIFY(object->firstProperty() == 0);
897 void tst_qqmlecmascript::attachedProperties()
900 QQmlComponent component(&engine, testFileUrl("attachedProperty.qml"));
901 QObject *object = component.create();
902 QVERIFY(object != 0);
903 QCOMPARE(object->property("a").toInt(), 19);
904 QCOMPARE(object->property("b").toInt(), 19);
905 QCOMPARE(object->property("c").toInt(), 19);
906 QCOMPARE(object->property("d").toInt(), 19);
911 QQmlComponent component(&engine, testFileUrl("attachedProperty.2.qml"));
912 QObject *object = component.create();
913 QVERIFY(object != 0);
914 QCOMPARE(object->property("a").toInt(), 26);
915 QCOMPARE(object->property("b").toInt(), 26);
916 QCOMPARE(object->property("c").toInt(), 26);
917 QCOMPARE(object->property("d").toInt(), 26);
923 QQmlComponent component(&engine, testFileUrl("writeAttachedProperty.qml"));
924 QObject *object = component.create();
925 QVERIFY(object != 0);
927 QMetaObject::invokeMethod(object, "writeValue2");
929 MyQmlAttachedObject *attached =
930 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
931 QVERIFY(attached != 0);
933 QCOMPARE(attached->value2(), 9);
938 void tst_qqmlecmascript::enums()
942 QQmlComponent component(&engine, testFileUrl("enums.1.qml"));
943 QObject *object = component.create();
944 QVERIFY(object != 0);
946 QCOMPARE(object->property("enumProperty").toInt(), (int)MyQmlObject::EnumValue2);
947 QCOMPARE(object->property("relatedEnumProperty").toInt(), (int)MyEnumContainer::RelatedValue);
948 QCOMPARE(object->property("unrelatedEnumProperty").toInt(), (int)MyEnumContainer::RelatedValue);
949 QCOMPARE(object->property("qtEnumProperty").toInt(), (int)Qt::CaseInsensitive);
950 QCOMPARE(object->property("a").toInt(), 0);
951 QCOMPARE(object->property("b").toInt(), 1);
952 QCOMPARE(object->property("c").toInt(), 2);
953 QCOMPARE(object->property("d").toInt(), 3);
954 QCOMPARE(object->property("e").toInt(), 0);
955 QCOMPARE(object->property("f").toInt(), 1);
956 QCOMPARE(object->property("g").toInt(), 2);
957 QCOMPARE(object->property("h").toInt(), 3);
958 QCOMPARE(object->property("i").toInt(), 19);
959 QCOMPARE(object->property("j").toInt(), 19);
960 QCOMPARE(object->property("k").toInt(), 42);
961 QCOMPARE(object->property("l").toInt(), 333);
965 // Non-existent enums
967 QUrl file = testFileUrl("enums.2.qml");
968 QString w1 = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'MyEnum' for property 'MyUnregisteredEnumTypeObject::enumProperty'");
969 QString w2 = QLatin1String("QQmlExpression: Expression ") + testFileUrl("enums.2.qml").toString() + QLatin1String(":9 depends on non-NOTIFYable properties:");
970 QString w3 = QLatin1String(" MyUnregisteredEnumTypeObject::enumProperty");
971 QString w4 = file.toString() + ":7: Unable to assign [undefined] to int";
972 QString w5 = file.toString() + ":8: Unable to assign [undefined] to int";
973 QString w6 = file.toString() + ":9: Unable to assign [undefined] to int";
974 QString w7 = file.toString() + ":13: Unable to assign [undefined] to [unknown property type]";
975 QString w8 = file.toString() + ":31: Unable to assign int to [unknown property type]";
976 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
977 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
978 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
979 QTest::ignoreMessage(QtWarningMsg, qPrintable(w4));
980 QTest::ignoreMessage(QtWarningMsg, qPrintable(w5));
981 QTest::ignoreMessage(QtWarningMsg, qPrintable(w6));
982 QTest::ignoreMessage(QtWarningMsg, qPrintable(w7));
983 QTest::ignoreMessage(QtWarningMsg, qPrintable(w8));
985 QQmlComponent component(&engine, testFileUrl("enums.2.qml"));
986 QObject *object = component.create();
987 QVERIFY(object != 0);
988 QCOMPARE(object->property("a").toInt(), 0);
989 QCOMPARE(object->property("b").toInt(), 0);
990 QCOMPARE(object->property("c").toInt(), 0);
992 QString w9 = file.toString() + ":18: Error: Cannot assign JavaScript function to [unknown property type]";
993 QTest::ignoreMessage(QtWarningMsg, qPrintable(w9));
994 QMetaObject::invokeMethod(object, "testAssignmentOne");
996 QString w10 = file.toString() + ":21: Error: Cannot assign [undefined] to [unknown property type]";
997 QTest::ignoreMessage(QtWarningMsg, qPrintable(w10));
998 QMetaObject::invokeMethod(object, "testAssignmentTwo");
1000 QString w11 = file.toString() + ":24: Error: Cannot assign [undefined] to [unknown property type]";
1001 QTest::ignoreMessage(QtWarningMsg, qPrintable(w11));
1002 QMetaObject::invokeMethod(object, "testAssignmentThree");
1004 QString w12 = file.toString() + ":34: Error: Cannot assign int to an unregistered type";
1005 QTest::ignoreMessage(QtWarningMsg, qPrintable(w12));
1006 QMetaObject::invokeMethod(object, "testAssignmentFour");
1010 // Enums as literals
1012 QQmlComponent component(&engine, testFileUrl("enums.3.qml"));
1013 QObject *object = component.create();
1014 QVERIFY(object != 0);
1016 // check the values are what we expect
1017 QCOMPARE(object->property("a").toInt(), 4);
1018 QCOMPARE(object->property("b").toInt(), 5);
1019 QCOMPARE(object->property("c").toInt(), 9);
1020 QCOMPARE(object->property("d").toInt(), 13);
1021 QCOMPARE(object->property("e").toInt(), 2);
1022 QCOMPARE(object->property("f").toInt(), 3);
1023 QCOMPARE(object->property("h").toInt(), 2);
1024 QCOMPARE(object->property("i").toInt(), 3);
1025 QCOMPARE(object->property("j").toInt(), -1);
1026 QCOMPARE(object->property("k").toInt(), 42);
1028 // count of change signals
1029 QCOMPARE(object->property("ac").toInt(), 0);
1030 QCOMPARE(object->property("bc").toInt(), 0);
1031 QCOMPARE(object->property("cc").toInt(), 0);
1032 QCOMPARE(object->property("dc").toInt(), 0);
1033 QCOMPARE(object->property("ec").toInt(), 0);
1034 QCOMPARE(object->property("fc").toInt(), 0);
1035 QCOMPARE(object->property("hc").toInt(), 1); // namespace -> binding
1036 QCOMPARE(object->property("ic").toInt(), 1); // namespace -> binding
1037 QCOMPARE(object->property("jc").toInt(), 0);
1038 QCOMPARE(object->property("kc").toInt(), 0);
1044 void tst_qqmlecmascript::valueTypeFunctions()
1046 QQmlComponent component(&engine, testFileUrl("valueTypeFunctions.qml"));
1047 MyTypeObject *obj = qobject_cast<MyTypeObject*>(component.create());
1049 QCOMPARE(obj->rectProperty(), QRect(0,0,100,100));
1050 QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5));
1056 Tests that writing a constant to a property with a binding on it disables the
1059 void tst_qqmlecmascript::constantsOverrideBindings()
1063 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.1.qml"));
1064 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1065 QVERIFY(object != 0);
1067 QCOMPARE(object->property("c2").toInt(), 0);
1068 object->setProperty("c1", QVariant(9));
1069 QCOMPARE(object->property("c2").toInt(), 9);
1071 emit object->basicSignal();
1073 QCOMPARE(object->property("c2").toInt(), 13);
1074 object->setProperty("c1", QVariant(8));
1075 QCOMPARE(object->property("c2").toInt(), 13);
1080 // During construction
1082 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.2.qml"));
1083 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1084 QVERIFY(object != 0);
1086 QCOMPARE(object->property("c1").toInt(), 0);
1087 QCOMPARE(object->property("c2").toInt(), 10);
1088 object->setProperty("c1", QVariant(9));
1089 QCOMPARE(object->property("c1").toInt(), 9);
1090 QCOMPARE(object->property("c2").toInt(), 10);
1098 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.3.qml"));
1099 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1100 QVERIFY(object != 0);
1102 QCOMPARE(object->property("c2").toInt(), 0);
1103 object->setProperty("c1", QVariant(9));
1104 QCOMPARE(object->property("c2").toInt(), 9);
1106 object->setProperty("c2", QVariant(13));
1107 QCOMPARE(object->property("c2").toInt(), 13);
1108 object->setProperty("c1", QVariant(7));
1109 QCOMPARE(object->property("c1").toInt(), 7);
1110 QCOMPARE(object->property("c2").toInt(), 13);
1118 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.4.qml"));
1119 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1120 QVERIFY(object != 0);
1122 QCOMPARE(object->property("c1").toInt(), 0);
1123 QCOMPARE(object->property("c3").toInt(), 10);
1124 object->setProperty("c1", QVariant(9));
1125 QCOMPARE(object->property("c1").toInt(), 9);
1126 QCOMPARE(object->property("c3").toInt(), 10);
1133 Tests that assigning a binding to a property that already has a binding causes
1134 the original binding to be disabled.
1136 void tst_qqmlecmascript::outerBindingOverridesInnerBinding()
1138 QQmlComponent component(&engine,
1139 testFileUrl("outerBindingOverridesInnerBinding.qml"));
1140 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1141 QVERIFY(object != 0);
1143 QCOMPARE(object->property("c1").toInt(), 0);
1144 QCOMPARE(object->property("c2").toInt(), 0);
1145 QCOMPARE(object->property("c3").toInt(), 0);
1147 object->setProperty("c1", QVariant(9));
1148 QCOMPARE(object->property("c1").toInt(), 9);
1149 QCOMPARE(object->property("c2").toInt(), 0);
1150 QCOMPARE(object->property("c3").toInt(), 0);
1152 object->setProperty("c3", QVariant(8));
1153 QCOMPARE(object->property("c1").toInt(), 9);
1154 QCOMPARE(object->property("c2").toInt(), 8);
1155 QCOMPARE(object->property("c3").toInt(), 8);
1161 Access a non-existent attached object.
1163 Tests for a regression where this used to crash.
1165 void tst_qqmlecmascript::nonExistentAttachedObject()
1167 QQmlComponent component(&engine, testFileUrl("nonExistentAttachedObject.qml"));
1169 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString";
1170 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
1172 QObject *object = component.create();
1173 QVERIFY(object != 0);
1178 void tst_qqmlecmascript::scope()
1181 QQmlComponent component(&engine, testFileUrl("scope.qml"));
1182 QObject *object = component.create();
1183 QVERIFY(object != 0);
1185 QCOMPARE(object->property("test1").toInt(), 1);
1186 QCOMPARE(object->property("test2").toInt(), 2);
1187 QCOMPARE(object->property("test3").toString(), QString("1Test"));
1188 QCOMPARE(object->property("test4").toString(), QString("2Test"));
1189 QCOMPARE(object->property("test5").toInt(), 1);
1190 QCOMPARE(object->property("test6").toInt(), 1);
1191 QCOMPARE(object->property("test7").toInt(), 2);
1192 QCOMPARE(object->property("test8").toInt(), 2);
1193 QCOMPARE(object->property("test9").toInt(), 1);
1194 QCOMPARE(object->property("test10").toInt(), 3);
1200 QQmlComponent component(&engine, testFileUrl("scope.2.qml"));
1201 QObject *object = component.create();
1202 QVERIFY(object != 0);
1204 QCOMPARE(object->property("test1").toInt(), 19);
1205 QCOMPARE(object->property("test2").toInt(), 19);
1206 QCOMPARE(object->property("test3").toInt(), 14);
1207 QCOMPARE(object->property("test4").toInt(), 14);
1208 QCOMPARE(object->property("test5").toInt(), 24);
1209 QCOMPARE(object->property("test6").toInt(), 24);
1215 QQmlComponent component(&engine, testFileUrl("scope.3.qml"));
1216 QObject *object = component.create();
1217 QVERIFY(object != 0);
1219 QCOMPARE(object->property("test1").toBool(), true);
1220 QCOMPARE(object->property("test2").toBool(), true);
1221 QCOMPARE(object->property("test3").toBool(), true);
1226 // Signal argument scope
1228 QQmlComponent component(&engine, testFileUrl("scope.4.qml"));
1229 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1230 QVERIFY(object != 0);
1232 QCOMPARE(object->property("test").toInt(), 0);
1233 QCOMPARE(object->property("test2").toString(), QString());
1235 emit object->argumentSignal(13, "Argument Scope", 9, MyQmlObject::EnumValue4, Qt::RightButton);
1237 QCOMPARE(object->property("test").toInt(), 13);
1238 QCOMPARE(object->property("test2").toString(), QString("Argument Scope"));
1244 QQmlComponent component(&engine, testFileUrl("scope.5.qml"));
1245 QObject *object = component.create();
1246 QVERIFY(object != 0);
1248 QCOMPARE(object->property("test1").toBool(), true);
1249 QCOMPARE(object->property("test2").toBool(), true);
1255 QQmlComponent component(&engine, testFileUrl("scope.6.qml"));
1256 QObject *object = component.create();
1257 QVERIFY(object != 0);
1259 QCOMPARE(object->property("test").toBool(), true);
1265 // In 4.7, non-library javascript files that had no imports shared the imports of their
1266 // importing context
1267 void tst_qqmlecmascript::importScope()
1269 QQmlComponent component(&engine, testFileUrl("importScope.qml"));
1270 QObject *o = component.create();
1273 QCOMPARE(o->property("test").toInt(), 240);
1279 Tests that "any" type passes through a synthesized signal parameter. This
1280 is essentially a test of QQmlMetaType::copy()
1282 void tst_qqmlecmascript::signalParameterTypes()
1284 QQmlComponent component(&engine, testFileUrl("signalParameterTypes.qml"));
1285 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1286 QVERIFY(object != 0);
1288 emit object->basicSignal();
1290 QCOMPARE(object->property("intProperty").toInt(), 10);
1291 QCOMPARE(object->property("realProperty").toReal(), 19.2);
1292 QVERIFY(object->property("colorProperty").value<QColor>() == QColor(255, 255, 0, 255));
1293 QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255)));
1294 QVERIFY(object->property("enumProperty") == MyQmlObject::EnumValue3);
1295 QVERIFY(object->property("qtEnumProperty") == Qt::LeftButton);
1301 Test that two JS objects for the same QObject compare as equal.
1303 void tst_qqmlecmascript::objectsCompareAsEqual()
1305 QQmlComponent component(&engine, testFileUrl("objectsCompareAsEqual.qml"));
1306 QObject *object = component.create();
1307 QVERIFY(object != 0);
1309 QCOMPARE(object->property("test1").toBool(), true);
1310 QCOMPARE(object->property("test2").toBool(), true);
1311 QCOMPARE(object->property("test3").toBool(), true);
1312 QCOMPARE(object->property("test4").toBool(), true);
1313 QCOMPARE(object->property("test5").toBool(), true);
1319 Confirm bindings and alias properties can coexist.
1321 Tests for a regression where the binding would not reevaluate.
1323 void tst_qqmlecmascript::aliasPropertyAndBinding()
1325 QQmlComponent component(&engine, testFileUrl("aliasPropertyAndBinding.qml"));
1326 QObject *object = component.create();
1327 QVERIFY(object != 0);
1329 QCOMPARE(object->property("c2").toInt(), 3);
1330 QCOMPARE(object->property("c3").toInt(), 3);
1332 object->setProperty("c2", QVariant(19));
1334 QCOMPARE(object->property("c2").toInt(), 19);
1335 QCOMPARE(object->property("c3").toInt(), 19);
1341 Ensure that we can write undefined value to an alias property,
1342 and that the aliased property is reset correctly if possible.
1344 void tst_qqmlecmascript::aliasPropertyReset()
1346 QObject *object = 0;
1348 // test that a manual write (of undefined) to a resettable aliased property succeeds
1349 QQmlComponent c1(&engine, testFileUrl("aliasreset/aliasPropertyReset.1.qml"));
1350 object = c1.create();
1351 QVERIFY(object != 0);
1352 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1353 QCOMPARE(object->property("aliasIsUndefined"), QVariant(false));
1354 QMetaObject::invokeMethod(object, "resetAliased");
1355 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1356 QCOMPARE(object->property("aliasIsUndefined"), QVariant(true));
1359 // test that a manual write (of undefined) to a resettable alias property succeeds
1360 QQmlComponent c2(&engine, testFileUrl("aliasreset/aliasPropertyReset.2.qml"));
1361 object = c2.create();
1362 QVERIFY(object != 0);
1363 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1364 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false));
1365 QMetaObject::invokeMethod(object, "resetAlias");
1366 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1367 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(true));
1370 // test that an alias to a bound property works correctly
1371 QQmlComponent c3(&engine, testFileUrl("aliasreset/aliasPropertyReset.3.qml"));
1372 object = c3.create();
1373 QVERIFY(object != 0);
1374 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1375 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false));
1376 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1377 QMetaObject::invokeMethod(object, "resetAlias");
1378 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1379 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(true));
1380 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1383 // test that a manual write (of undefined) to a resettable alias property
1384 // whose aliased property's object has been deleted, does not crash.
1385 QQmlComponent c4(&engine, testFileUrl("aliasreset/aliasPropertyReset.4.qml"));
1386 object = c4.create();
1387 QVERIFY(object != 0);
1388 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1389 QObject *loader = object->findChild<QObject*>("loader");
1390 QVERIFY(loader != 0);
1392 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // deletion should have caused value unset.
1393 QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash.
1394 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1395 QMetaObject::invokeMethod(object, "setAlias"); // shouldn't crash, and shouldn't change value (since it's no longer referencing anything).
1396 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1399 // test that binding an alias property to an undefined value works correctly
1400 QQmlComponent c5(&engine, testFileUrl("aliasreset/aliasPropertyReset.5.qml"));
1401 object = c5.create();
1402 QVERIFY(object != 0);
1403 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // bound to undefined value.
1406 // test that a manual write (of undefined) to a non-resettable property fails properly
1407 QUrl url = testFileUrl("aliasreset/aliasPropertyReset.error.1.qml");
1408 QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int");
1409 QQmlComponent e1(&engine, url);
1410 object = e1.create();
1411 QVERIFY(object != 0);
1412 QCOMPARE(object->property("intAlias").value<int>(), 12);
1413 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1414 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1415 QMetaObject::invokeMethod(object, "resetAlias");
1416 QCOMPARE(object->property("intAlias").value<int>(), 12);
1417 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1421 void tst_qqmlecmascript::componentCreation_data()
1423 QTest::addColumn<QString>("method");
1424 QTest::addColumn<QString>("creationError");
1425 QTest::addColumn<QString>("createdParent");
1427 QTest::newRow("url")
1431 QTest::newRow("urlMode")
1435 QTest::newRow("urlParent")
1439 QTest::newRow("urlNullParent")
1443 QTest::newRow("urlModeParent")
1447 QTest::newRow("urlModeNullParent")
1448 << "urlModeNullParent"
1451 QTest::newRow("invalidSecondArg")
1452 << "invalidSecondArg"
1453 << ":40: Error: Qt.createComponent(): Invalid arguments"
1455 QTest::newRow("invalidThirdArg")
1456 << "invalidThirdArg"
1457 << ":45: Error: Qt.createComponent(): Invalid parent object"
1459 QTest::newRow("invalidMode")
1461 << ":50: Error: Qt.createComponent(): Invalid arguments"
1466 Test using createComponent to dynamically generate a component.
1468 void tst_qqmlecmascript::componentCreation()
1470 QFETCH(QString, method);
1471 QFETCH(QString, creationError);
1472 QFETCH(QString, createdParent);
1474 QUrl testUrl(testFileUrl("componentCreation.qml"));
1476 if (!creationError.isEmpty()) {
1477 QString warning = testUrl.toString() + creationError;
1478 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1481 QQmlComponent component(&engine, testUrl);
1482 MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create());
1483 QVERIFY(object != 0);
1485 QMetaObject::invokeMethod(object, method.toUtf8());
1486 QQmlComponent *created = object->componentProperty();
1488 if (creationError.isEmpty()) {
1491 QObject *expectedParent;
1492 if (createdParent == QLatin1String("obj")) {
1493 expectedParent = object;
1494 } else if ((createdParent == QLatin1String("null")) || createdParent.isEmpty()) {
1497 QCOMPARE(created->parent(), expectedParent);
1501 void tst_qqmlecmascript::dynamicCreation_data()
1503 QTest::addColumn<QString>("method");
1504 QTest::addColumn<QString>("createdName");
1506 QTest::newRow("One") << "createOne" << "objectOne";
1507 QTest::newRow("Two") << "createTwo" << "objectTwo";
1508 QTest::newRow("Three") << "createThree" << "objectThree";
1512 Test using createQmlObject to dynamically generate an item
1513 Also using createComponent is tested.
1515 void tst_qqmlecmascript::dynamicCreation()
1517 QFETCH(QString, method);
1518 QFETCH(QString, createdName);
1520 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
1521 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1522 QVERIFY(object != 0);
1524 QMetaObject::invokeMethod(object, method.toUtf8());
1525 QObject *created = object->objectProperty();
1527 QCOMPARE(created->objectName(), createdName);
1533 Tests the destroy function
1535 void tst_qqmlecmascript::dynamicDestruction()
1538 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.qml"));
1539 QQmlGuard<MyQmlObject> object = qobject_cast<MyQmlObject*>(component.create());
1540 QVERIFY(object != 0);
1541 QQmlGuard<QObject> createdQmlObject = 0;
1543 QMetaObject::invokeMethod(object, "create");
1544 createdQmlObject = object->objectProperty();
1545 QVERIFY(createdQmlObject);
1546 QCOMPARE(createdQmlObject->objectName(), QString("emptyObject"));
1548 QMetaObject::invokeMethod(object, "killOther");
1549 QVERIFY(createdQmlObject);
1551 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1552 QCoreApplication::processEvents();
1553 QVERIFY(createdQmlObject);
1554 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1555 if (createdQmlObject) {
1557 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1558 QCoreApplication::processEvents();
1561 QVERIFY(!createdQmlObject);
1563 QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
1564 QMetaObject::invokeMethod(object, "killMe");
1566 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1567 QCoreApplication::processEvents();
1572 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.2.qml"));
1573 QObject *o = component.create();
1576 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1578 QMetaObject::invokeMethod(o, "create");
1580 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) != 0);
1582 QMetaObject::invokeMethod(o, "destroy");
1584 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1585 QCoreApplication::processEvents();
1587 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1594 QQmlGuard<QObject> createdQmlObject = 0;
1595 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.3.qml"));
1596 QObject *o = component.create();
1598 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1599 QMetaObject::invokeMethod(o, "create");
1600 createdQmlObject = qvariant_cast<QObject*>(o->property("objectProperty"));
1601 QVERIFY(createdQmlObject);
1602 QMetaObject::invokeMethod(o, "destroy");
1603 QVERIFY(qvariant_cast<bool>(o->property("test")) == false);
1604 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1606 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1607 QCoreApplication::processEvents();
1609 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1610 QVERIFY(qvariant_cast<bool>(o->property("test")) == true);
1616 tests that id.toString() works
1618 void tst_qqmlecmascript::objectToString()
1620 QQmlComponent component(&engine, testFileUrl("qmlToString.qml"));
1621 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1622 QVERIFY(object != 0);
1623 QMetaObject::invokeMethod(object, "testToString");
1624 QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_"));
1625 QVERIFY(object->stringProperty().endsWith(", \"objName\")"));
1631 tests that id.hasOwnProperty() works
1633 void tst_qqmlecmascript::objectHasOwnProperty()
1635 QUrl url = testFileUrl("qmlHasOwnProperty.qml");
1636 QString warning1 = url.toString() + ":59: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1637 QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1638 QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1640 QQmlComponent component(&engine, url);
1641 QObject *object = component.create();
1642 QVERIFY(object != 0);
1644 // test QObjects in QML
1645 QMetaObject::invokeMethod(object, "testHasOwnPropertySuccess");
1646 QVERIFY(object->property("result").value<bool>() == true);
1647 QMetaObject::invokeMethod(object, "testHasOwnPropertyFailure");
1648 QVERIFY(object->property("result").value<bool>() == false);
1650 // now test other types in QML
1651 QObject *child = object->findChild<QObject*>("typeObj");
1652 QVERIFY(child != 0);
1653 QMetaObject::invokeMethod(child, "testHasOwnPropertySuccess");
1654 QCOMPARE(child->property("valueTypeHasOwnProperty").toBool(), true);
1655 QCOMPARE(child->property("valueTypeHasOwnProperty2").toBool(), true);
1656 QCOMPARE(child->property("variantTypeHasOwnProperty").toBool(), true);
1657 QCOMPARE(child->property("stringTypeHasOwnProperty").toBool(), true);
1658 QCOMPARE(child->property("listTypeHasOwnProperty").toBool(), true);
1659 QCOMPARE(child->property("emptyListTypeHasOwnProperty").toBool(), true);
1660 QCOMPARE(child->property("enumTypeHasOwnProperty").toBool(), true);
1661 QCOMPARE(child->property("typenameHasOwnProperty").toBool(), true);
1662 QCOMPARE(child->property("typenameHasOwnProperty2").toBool(), true);
1663 QCOMPARE(child->property("singletonTypeTypeHasOwnProperty").toBool(), true);
1664 QCOMPARE(child->property("singletonTypePropertyTypeHasOwnProperty").toBool(), true);
1666 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1667 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureOne");
1668 QCOMPARE(child->property("enumNonValueHasOwnProperty").toBool(), false);
1669 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1670 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureTwo");
1671 QCOMPARE(child->property("singletonTypeNonPropertyHasOwnProperty").toBool(), false);
1672 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1673 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureThree");
1674 QCOMPARE(child->property("listAtInvalidHasOwnProperty").toBool(), false);
1680 Tests bindings that indirectly cause their own deletion work.
1682 This test is best run under valgrind to ensure no invalid memory access occur.
1684 void tst_qqmlecmascript::selfDeletingBinding()
1687 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.qml"));
1688 QObject *object = component.create();
1689 QVERIFY(object != 0);
1690 object->setProperty("triggerDelete", true);
1695 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.2.qml"));
1696 QObject *object = component.create();
1697 QVERIFY(object != 0);
1698 object->setProperty("triggerDelete", true);
1704 Test that extended object properties can be accessed.
1706 This test a regression where this used to crash. The issue was specificially
1707 for extended objects that did not include a synthesized meta object (so non-root
1708 and no synthesiszed properties).
1710 void tst_qqmlecmascript::extendedObjectPropertyLookup()
1712 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup.qml"));
1713 QObject *object = component.create();
1714 QVERIFY(object != 0);
1719 Test that extended object properties can be accessed correctly.
1721 void tst_qqmlecmascript::extendedObjectPropertyLookup2()
1723 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup2.qml"));
1724 QObject *object = component.create();
1725 QVERIFY(object != 0);
1727 QVariant returnValue;
1728 QVERIFY(QMetaObject::invokeMethod(object, "getValue", Q_RETURN_ARG(QVariant, returnValue)));
1729 QCOMPARE(returnValue.toInt(), 42);
1734 Test file/lineNumbers for binding/Script errors.
1736 void tst_qqmlecmascript::scriptErrors()
1738 QQmlComponent component(&engine, testFileUrl("scriptErrors.qml"));
1739 QString url = component.url().toString();
1741 QString warning1 = url.left(url.length() - 3) + "js:2: Error: Invalid write to global property \"a\"";
1742 QString warning2 = url + ":5: ReferenceError: a is not defined";
1743 QString warning3 = url.left(url.length() - 3) + "js:4: Error: Invalid write to global property \"a\"";
1744 QString warning4 = url + ":13: ReferenceError: a is not defined";
1745 QString warning5 = url + ":11: ReferenceError: a is not defined";
1746 QString warning6 = url + ":10: Unable to assign [undefined] to int";
1747 QString warning7 = url + ":15: Error: Cannot assign to read-only property \"trueProperty\"";
1748 QString warning8 = url + ":16: Error: Cannot assign to non-existent property \"fakeProperty\"";
1750 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1751 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1752 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1753 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
1754 QTest::ignoreMessage(QtWarningMsg, warning6.toLatin1().constData());
1755 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1756 QVERIFY(object != 0);
1758 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
1759 emit object->basicSignal();
1761 QTest::ignoreMessage(QtWarningMsg, warning7.toLatin1().constData());
1762 emit object->anotherBasicSignal();
1764 QTest::ignoreMessage(QtWarningMsg, warning8.toLatin1().constData());
1765 emit object->thirdBasicSignal();
1771 Test file/lineNumbers for inline functions.
1773 void tst_qqmlecmascript::functionErrors()
1775 QQmlComponent component(&engine, testFileUrl("functionErrors.qml"));
1776 QString url = component.url().toString();
1778 QString warning = url + ":5: Error: Invalid write to global property \"a\"";
1780 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1782 QObject *object = component.create();
1783 QVERIFY(object != 0);
1786 // test that if an exception occurs while invoking js function from cpp, it is reported as expected.
1787 QQmlComponent componentTwo(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
1788 url = componentTwo.url().toString();
1789 object = componentTwo.create();
1790 QVERIFY(object != 0);
1792 QString srpname = object->property("srp_name").toString();
1794 warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srpname
1795 + QLatin1String(" is not a function");
1796 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed.
1797 QMetaObject::invokeMethod(object, "retrieveScarceResource");
1802 Test various errors that can occur when assigning a property from script
1804 void tst_qqmlecmascript::propertyAssignmentErrors()
1806 QQmlComponent component(&engine, testFileUrl("propertyAssignmentErrors.qml"));
1808 QString url = component.url().toString();
1810 QObject *object = component.create();
1811 QVERIFY(object != 0);
1813 QCOMPARE(object->property("test1").toBool(), true);
1814 QCOMPARE(object->property("test2").toBool(), true);
1820 Test bindings still work when the reeval is triggered from within
1823 void tst_qqmlecmascript::signalTriggeredBindings()
1825 QQmlComponent component(&engine, testFileUrl("signalTriggeredBindings.qml"));
1826 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1827 QVERIFY(object != 0);
1829 QCOMPARE(object->property("base").toReal(), 50.);
1830 QCOMPARE(object->property("test1").toReal(), 50.);
1831 QCOMPARE(object->property("test2").toReal(), 50.);
1833 object->basicSignal();
1835 QCOMPARE(object->property("base").toReal(), 200.);
1836 QCOMPARE(object->property("test1").toReal(), 200.);
1837 QCOMPARE(object->property("test2").toReal(), 200.);
1839 object->argumentSignal(10, QString(), 10, MyQmlObject::EnumValue4, Qt::RightButton);
1841 QCOMPARE(object->property("base").toReal(), 400.);
1842 QCOMPARE(object->property("test1").toReal(), 400.);
1843 QCOMPARE(object->property("test2").toReal(), 400.);
1849 Test that list properties can be iterated from ECMAScript
1851 void tst_qqmlecmascript::listProperties()
1853 QQmlComponent component(&engine, testFileUrl("listProperties.qml"));
1854 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1855 QVERIFY(object != 0);
1857 QCOMPARE(object->property("test1").toInt(), 21);
1858 QCOMPARE(object->property("test2").toInt(), 2);
1859 QCOMPARE(object->property("test3").toBool(), true);
1860 QCOMPARE(object->property("test4").toBool(), true);
1865 void tst_qqmlecmascript::exceptionClearsOnReeval()
1867 QQmlComponent component(&engine, testFileUrl("exceptionClearsOnReeval.qml"));
1868 QString url = component.url().toString();
1870 QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null";
1872 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1873 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1874 QVERIFY(object != 0);
1876 QCOMPARE(object->property("test").toBool(), false);
1878 MyQmlObject object2;
1879 MyQmlObject object3;
1880 object2.setObjectProperty(&object3);
1881 object->setObjectProperty(&object2);
1883 QCOMPARE(object->property("test").toBool(), true);
1888 void tst_qqmlecmascript::exceptionSlotProducesWarning()
1890 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning.qml"));
1891 QString url = component.url().toString();
1893 QString warning = component.url().toString() + ":6: Error: JS exception";
1895 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1896 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1897 QVERIFY(object != 0);
1901 void tst_qqmlecmascript::exceptionBindingProducesWarning()
1903 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning2.qml"));
1904 QString url = component.url().toString();
1906 QString warning = component.url().toString() + ":5: Error: JS exception";
1908 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1909 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1910 QVERIFY(object != 0);
1914 void tst_qqmlecmascript::compileInvalidBinding()
1916 // QTBUG-23387: ensure that invalid bindings don't cause a crash.
1917 QQmlComponent component(&engine, testFileUrl("v8bindingException.qml"));
1918 QObject *object = component.create();
1919 QVERIFY(object != 0);
1923 static int transientErrorsMsgCount = 0;
1924 static void transientErrorsMsgHandler(QtMsgType, const char *)
1926 ++transientErrorsMsgCount;
1929 // Check that transient binding errors are not displayed
1930 void tst_qqmlecmascript::transientErrors()
1933 QQmlComponent component(&engine, testFileUrl("transientErrors.qml"));
1935 transientErrorsMsgCount = 0;
1936 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1938 QObject *object = component.create();
1939 QVERIFY(object != 0);
1941 qInstallMsgHandler(old);
1943 QCOMPARE(transientErrorsMsgCount, 0);
1948 // One binding erroring multiple times, but then resolving
1950 QQmlComponent component(&engine, testFileUrl("transientErrors.2.qml"));
1952 transientErrorsMsgCount = 0;
1953 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1955 QObject *object = component.create();
1956 QVERIFY(object != 0);
1958 qInstallMsgHandler(old);
1960 QCOMPARE(transientErrorsMsgCount, 0);
1966 // Check that errors during shutdown are minimized
1967 void tst_qqmlecmascript::shutdownErrors()
1969 QQmlComponent component(&engine, testFileUrl("shutdownErrors.qml"));
1970 QObject *object = component.create();
1971 QVERIFY(object != 0);
1973 transientErrorsMsgCount = 0;
1974 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1978 qInstallMsgHandler(old);
1979 QCOMPARE(transientErrorsMsgCount, 0);
1982 void tst_qqmlecmascript::compositePropertyType()
1984 QQmlComponent component(&engine, testFileUrl("compositePropertyType.qml"));
1986 QTest::ignoreMessage(QtDebugMsg, "hello world");
1987 QObject *object = qobject_cast<QObject *>(component.create());
1992 void tst_qqmlecmascript::jsObject()
1994 QQmlComponent component(&engine, testFileUrl("jsObject.qml"));
1995 QObject *object = component.create();
1996 QVERIFY(object != 0);
1998 QCOMPARE(object->property("test").toInt(), 92);
2003 void tst_qqmlecmascript::undefinedResetsProperty()
2006 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.qml"));
2007 QObject *object = component.create();
2008 QVERIFY(object != 0);
2010 QCOMPARE(object->property("resettableProperty").toInt(), 92);
2012 object->setProperty("setUndefined", true);
2014 QCOMPARE(object->property("resettableProperty").toInt(), 13);
2016 object->setProperty("setUndefined", false);
2018 QCOMPARE(object->property("resettableProperty").toInt(), 92);
2023 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.2.qml"));
2024 QObject *object = component.create();
2025 QVERIFY(object != 0);
2027 QCOMPARE(object->property("resettableProperty").toInt(), 19);
2029 QMetaObject::invokeMethod(object, "doReset");
2031 QCOMPARE(object->property("resettableProperty").toInt(), 13);
2037 // Aliases to variant properties should work
2038 void tst_qqmlecmascript::qtbug_22464()
2040 QQmlComponent component(&engine, testFileUrl("qtbug_22464.qml"));
2041 QObject *object = component.create();
2042 QVERIFY(object != 0);
2044 QCOMPARE(object->property("test").toBool(), true);
2049 void tst_qqmlecmascript::qtbug_21580()
2051 QQmlComponent component(&engine, testFileUrl("qtbug_21580.qml"));
2053 QObject *object = component.create();
2054 QVERIFY(object != 0);
2056 QCOMPARE(object->property("test").toBool(), true);
2061 // Causes a v8 binding, but not all v8 bindings to be destroyed during evaluation
2062 void tst_qqmlecmascript::singleV8BindingDestroyedDuringEvaluation()
2064 QQmlComponent component(&engine, testFileUrl("singleV8BindingDestroyedDuringEvaluation.qml"));
2066 QObject *object = component.create();
2067 QVERIFY(object != 0);
2072 void tst_qqmlecmascript::bug1()
2074 QQmlComponent component(&engine, testFileUrl("bug.1.qml"));
2075 QObject *object = component.create();
2076 QVERIFY(object != 0);
2078 QCOMPARE(object->property("test").toInt(), 14);
2080 object->setProperty("a", 11);
2082 QCOMPARE(object->property("test").toInt(), 3);
2084 object->setProperty("b", true);
2086 QCOMPARE(object->property("test").toInt(), 9);
2091 #ifndef QT_NO_WIDGETS
2092 void tst_qqmlecmascript::bug2()
2094 QQmlComponent component(&engine);
2095 component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl());
2097 QObject *object = component.create();
2098 QVERIFY(object != 0);
2104 // Don't crash in createObject when the component has errors.
2105 void tst_qqmlecmascript::dynamicCreationCrash()
2107 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
2108 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2109 QVERIFY(object != 0);
2111 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2112 QMetaObject::invokeMethod(object, "dontCrash");
2113 QObject *created = object->objectProperty();
2114 QVERIFY(created == 0);
2119 // ownership transferred to JS, ensure that GC runs the dtor
2120 void tst_qqmlecmascript::dynamicCreationOwnership()
2123 int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
2125 // allow the engine to go out of scope too.
2127 QQmlEngine dcoEngine;
2128 QQmlComponent component(&dcoEngine, testFileUrl("dynamicCreationOwnership.qml"));
2129 QObject *object = component.create();
2130 QVERIFY(object != 0);
2131 MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo");
2132 QVERIFY(mdcdo != 0);
2133 mdcdo->setDtorCount(&dtorCount);
2135 for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
2136 QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
2138 // we do this once manually, but it should be done automatically
2139 // when the engine goes out of scope (since it should gc in dtor)
2140 QMetaObject::invokeMethod(object, "performGc");
2143 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2144 QCoreApplication::processEvents();
2150 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2151 QCoreApplication::processEvents();
2152 QCOMPARE(dtorCount, expectedDtorCount);
2155 void tst_qqmlecmascript::regExpBug()
2159 QQmlComponent component(&engine, testFileUrl("regExp.qml"));
2160 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2161 QVERIFY(object != 0);
2162 QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]"));
2168 QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString());
2169 QQmlComponent component(&engine, testFileUrl("regExp.2.qml"));
2170 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2171 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2173 QCOMPARE(component.errorString(), err);
2177 static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source)
2179 QString functionSource = QLatin1String("(function(object) { return ") +
2180 QLatin1String(source) + QLatin1String(" })");
2182 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2185 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2186 if (function.IsEmpty())
2188 v8::Handle<v8::Value> args[] = { o };
2189 function->Call(engine->global(), 1, args);
2190 return tc.HasCaught();
2193 static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o,
2194 const char *source, v8::Handle<v8::Value> result)
2196 QString functionSource = QLatin1String("(function(object) { return ") +
2197 QLatin1String(source) + QLatin1String(" })");
2199 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2202 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2203 if (function.IsEmpty())
2205 v8::Handle<v8::Value> args[] = { o };
2207 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2212 return value->StrictEquals(result);
2215 static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o,
2218 QString functionSource = QLatin1String("(function(object) { return ") +
2219 QLatin1String(source) + QLatin1String(" })");
2221 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2223 return v8::Handle<v8::Value>();
2224 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2225 if (function.IsEmpty())
2226 return v8::Handle<v8::Value>();
2227 v8::Handle<v8::Value> args[] = { o };
2229 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2232 return v8::Handle<v8::Value>();
2236 #define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
2237 #define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result)
2238 #define EVALUATE(source) evaluate(engine, object, source)
2240 void tst_qqmlecmascript::callQtInvokables()
2242 // This object has JS ownership, as the call to method_NoArgs_QObject() in this test will return
2243 // it, which will set the indestructible flag to false.
2244 MyInvokableObject *o = new MyInvokableObject();
2246 QQmlEngine qmlengine;
2247 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine);
2249 QV8Engine *engine = ep->v8engine();
2251 v8::HandleScope handle_scope;
2252 v8::Context::Scope scope(engine->context());
2254 v8::Local<v8::Object> object = engine->newQObject(o)->ToObject();
2256 // Non-existent methods
2258 QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
2259 QCOMPARE(o->error(), false);
2260 QCOMPARE(o->invoked(), -1);
2261 QCOMPARE(o->actuals().count(), 0);
2264 QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)"));
2265 QCOMPARE(o->error(), false);
2266 QCOMPARE(o->invoked(), -1);
2267 QCOMPARE(o->actuals().count(), 0);
2269 // Insufficient arguments
2271 QVERIFY(EVALUATE_ERROR("object.method_int()"));
2272 QCOMPARE(o->error(), false);
2273 QCOMPARE(o->invoked(), -1);
2274 QCOMPARE(o->actuals().count(), 0);
2277 QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
2278 QCOMPARE(o->error(), false);
2279 QCOMPARE(o->invoked(), -1);
2280 QCOMPARE(o->actuals().count(), 0);
2282 // Excessive arguments
2284 QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", v8::Undefined()));
2285 QCOMPARE(o->error(), false);
2286 QCOMPARE(o->invoked(), 8);
2287 QCOMPARE(o->actuals().count(), 1);
2288 QCOMPARE(o->actuals().at(0), QVariant(10));
2291 QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", v8::Undefined()));
2292 QCOMPARE(o->error(), false);
2293 QCOMPARE(o->invoked(), 9);
2294 QCOMPARE(o->actuals().count(), 2);
2295 QCOMPARE(o->actuals().at(0), QVariant(10));
2296 QCOMPARE(o->actuals().at(1), QVariant(11));
2298 // Test return types
2300 QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", v8::Undefined()));
2301 QCOMPARE(o->error(), false);
2302 QCOMPARE(o->invoked(), 0);
2303 QCOMPARE(o->actuals().count(), 0);
2306 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", v8::Integer::New(6)));
2307 QCOMPARE(o->error(), false);
2308 QCOMPARE(o->invoked(), 1);
2309 QCOMPARE(o->actuals().count(), 0);
2312 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", v8::Number::New(19.75)));
2313 QCOMPARE(o->error(), false);
2314 QCOMPARE(o->invoked(), 2);
2315 QCOMPARE(o->actuals().count(), 0);
2319 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QPointF()");
2320 QVERIFY(!ret.IsEmpty());
2321 QCOMPARE(engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5)));
2322 QCOMPARE(o->error(), false);
2323 QCOMPARE(o->invoked(), 3);
2324 QCOMPARE(o->actuals().count(), 0);
2329 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QObject()");
2330 QCOMPARE(engine->toQObject(ret), (QObject *)o);
2331 QCOMPARE(o->error(), false);
2332 QCOMPARE(o->invoked(), 4);
2333 QCOMPARE(o->actuals().count(), 0);
2337 QVERIFY(EVALUATE_ERROR("object.method_NoArgs_unknown()"));
2338 QCOMPARE(o->error(), false);
2339 QCOMPARE(o->invoked(), -1);
2340 QCOMPARE(o->actuals().count(), 0);
2344 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QScriptValue()");
2345 QVERIFY(ret->IsString());
2346 QCOMPARE(engine->toString(ret), QString("Hello world"));
2347 QCOMPARE(o->error(), false);
2348 QCOMPARE(o->invoked(), 6);
2349 QCOMPARE(o->actuals().count(), 0);
2353 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks")));
2354 QCOMPARE(o->error(), false);
2355 QCOMPARE(o->invoked(), 7);
2356 QCOMPARE(o->actuals().count(), 0);
2360 QVERIFY(EVALUATE_VALUE("object.method_int(94)", v8::Undefined()));
2361 QCOMPARE(o->error(), false);
2362 QCOMPARE(o->invoked(), 8);
2363 QCOMPARE(o->actuals().count(), 1);
2364 QCOMPARE(o->actuals().at(0), QVariant(94));
2367 QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", v8::Undefined()));
2368 QCOMPARE(o->error(), false);
2369 QCOMPARE(o->invoked(), 8);
2370 QCOMPARE(o->actuals().count(), 1);
2371 QCOMPARE(o->actuals().at(0), QVariant(94));
2374 QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", v8::Undefined()));
2375 QCOMPARE(o->error(), false);
2376 QCOMPARE(o->invoked(), 8);
2377 QCOMPARE(o->actuals().count(), 1);
2378 QCOMPARE(o->actuals().at(0), QVariant(0));
2381 QVERIFY(EVALUATE_VALUE("object.method_int(null)", v8::Undefined()));
2382 QCOMPARE(o->error(), false);
2383 QCOMPARE(o->invoked(), 8);
2384 QCOMPARE(o->actuals().count(), 1);
2385 QCOMPARE(o->actuals().at(0), QVariant(0));
2388 QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", v8::Undefined()));
2389 QCOMPARE(o->error(), false);
2390 QCOMPARE(o->invoked(), 8);
2391 QCOMPARE(o->actuals().count(), 1);
2392 QCOMPARE(o->actuals().at(0), QVariant(0));
2395 QVERIFY(EVALUATE_VALUE("object.method_int(object)", v8::Undefined()));
2396 QCOMPARE(o->error(), false);
2397 QCOMPARE(o->invoked(), 8);
2398 QCOMPARE(o->actuals().count(), 1);
2399 QCOMPARE(o->actuals().at(0), QVariant(0));
2402 QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", v8::Undefined()));
2403 QCOMPARE(o->error(), false);
2404 QCOMPARE(o->invoked(), 9);
2405 QCOMPARE(o->actuals().count(), 2);
2406 QCOMPARE(o->actuals().at(0), QVariant(122));
2407 QCOMPARE(o->actuals().at(1), QVariant(9));
2410 QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", v8::Undefined()));
2411 QCOMPARE(o->error(), false);
2412 QCOMPARE(o->invoked(), 10);
2413 QCOMPARE(o->actuals().count(), 1);
2414 QCOMPARE(o->actuals().at(0), QVariant(94.3));
2417 QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", v8::Undefined()));
2418 QCOMPARE(o->error(), false);
2419 QCOMPARE(o->invoked(), 10);
2420 QCOMPARE(o->actuals().count(), 1);
2421 QCOMPARE(o->actuals().at(0), QVariant(94.3));
2424 QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", v8::Undefined()));
2425 QCOMPARE(o->error(), false);
2426 QCOMPARE(o->invoked(), 10);
2427 QCOMPARE(o->actuals().count(), 1);
2428 QVERIFY(qIsNaN(o->actuals().at(0).toDouble()));
2431 QVERIFY(EVALUATE_VALUE("object.method_real(null)", v8::Undefined()));
2432 QCOMPARE(o->error(), false);
2433 QCOMPARE(o->invoked(), 10);
2434 QCOMPARE(o->actuals().count(), 1);
2435 QCOMPARE(o->actuals().at(0), QVariant(0));
2438 QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", v8::Undefined()));
2439 QCOMPARE(o->error(), false);
2440 QCOMPARE(o->invoked(), 10);
2441 QCOMPARE(o->actuals().count(), 1);
2442 QVERIFY(qIsNaN(o->actuals().at(0).toDouble()));
2445 QVERIFY(EVALUATE_VALUE("object.method_real(object)", v8::Undefined()));
2446 QCOMPARE(o->error(), false);
2447 QCOMPARE(o->invoked(), 10);
2448 QCOMPARE(o->actuals().count(), 1);
2449 QVERIFY(qIsNaN(o->actuals().at(0).toDouble()));
2452 QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", v8::Undefined()));
2453 QCOMPARE(o->error(), false);
2454 QCOMPARE(o->invoked(), 11);
2455 QCOMPARE(o->actuals().count(), 1);
2456 QCOMPARE(o->actuals().at(0), QVariant("Hello world"));
2459 QVERIFY(EVALUATE_VALUE("object.method_QString(19)", v8::Undefined()));
2460 QCOMPARE(o->error(), false);
2461 QCOMPARE(o->invoked(), 11);
2462 QCOMPARE(o->actuals().count(), 1);
2463 QCOMPARE(o->actuals().at(0), QVariant("19"));
2467 QString expected = "MyInvokableObject(0x" + QString::number((quintptr)o, 16) + ")";
2468 QVERIFY(EVALUATE_VALUE("object.method_QString(object)", v8::Undefined()));
2469 QCOMPARE(o->error(), false);
2470 QCOMPARE(o->invoked(), 11);
2471 QCOMPARE(o->actuals().count(), 1);
2472 QCOMPARE(o->actuals().at(0), QVariant(expected));
2476 QVERIFY(EVALUATE_VALUE("object.method_QString(null)", v8::Undefined()));
2477 QCOMPARE(o->error(), false);
2478 QCOMPARE(o->invoked(), 11);
2479 QCOMPARE(o->actuals().count(), 1);
2480 QCOMPARE(o->actuals().at(0), QVariant(QString()));
2483 QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", v8::Undefined()));
2484 QCOMPARE(o->error(), false);
2485 QCOMPARE(o->invoked(), 11);
2486 QCOMPARE(o->actuals().count(), 1);
2487 QCOMPARE(o->actuals().at(0), QVariant(QString()));
2490 QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", v8::Undefined()));
2491 QCOMPARE(o->error(), false);
2492 QCOMPARE(o->invoked(), 12);
2493 QCOMPARE(o->actuals().count(), 1);
2494 QCOMPARE(o->actuals().at(0), QVariant(QPointF()));
2497 QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", v8::Undefined()));
2498 QCOMPARE(o->error(), false);
2499 QCOMPARE(o->invoked(), 12);
2500 QCOMPARE(o->actuals().count(), 1);
2501 QCOMPARE(o->actuals().at(0), QVariant(QPointF()));
2504 QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", v8::Undefined()));
2505 QCOMPARE(o->error(), false);
2506 QCOMPARE(o->invoked(), 12);
2507 QCOMPARE(o->actuals().count(), 1);
2508 QCOMPARE(o->actuals().at(0), QVariant(QPointF()));
2511 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", v8::Undefined()));
2512 QCOMPARE(o->error(), false);
2513 QCOMPARE(o->invoked(), 12);
2514 QCOMPARE(o->actuals().count(), 1);
2515 QCOMPARE(o->actuals().at(0), QVariant(QPointF()));
2518 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", v8::Undefined()));
2519 QCOMPARE(o->error(), false);
2520 QCOMPARE(o->invoked(), 12);
2521 QCOMPARE(o->actuals().count(), 1);
2522 QCOMPARE(o->actuals().at(0), QVariant(QPointF(99.3, -10.2)));
2525 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", v8::Undefined()));
2526 QCOMPARE(o->error(), false);
2527 QCOMPARE(o->invoked(), 12);
2528 QCOMPARE(o->actuals().count(), 1);
2529 QCOMPARE(o->actuals().at(0), QVariant(QPointF(9, 12)));
2532 QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", v8::Undefined()));
2533 QCOMPARE(o->error(), false);
2534 QCOMPARE(o->invoked(), 13);
2535 QCOMPARE(o->actuals().count(), 1);
2536 QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)0));
2539 QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", v8::Undefined()));
2540 QCOMPARE(o->error(), false);
2541 QCOMPARE(o->invoked(), 13);
2542 QCOMPARE(o->actuals().count(), 1);
2543 QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)0));
2546 QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", v8::Undefined()));
2547 QCOMPARE(o->error(), false);
2548 QCOMPARE(o->invoked(), 13);
2549 QCOMPARE(o->actuals().count(), 1);
2550 QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)0));
2553 QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", v8::Undefined()));
2554 QCOMPARE(o->error(), false);
2555 QCOMPARE(o->invoked(), 13);
2556 QCOMPARE(o->actuals().count(), 1);
2557 QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)0));
2560 QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", v8::Undefined()));
2561 QCOMPARE(o->error(), false);
2562 QCOMPARE(o->invoked(), 13);
2563 QCOMPARE(o->actuals().count(), 1);
2564 QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)o));
2567 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined()));
2568 QCOMPARE(o->error(), false);
2569 QCOMPARE(o->invoked(), 14);
2570 QCOMPARE(o->actuals().count(), 1);
2571 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(0)).isNull());
2574 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined()));
2575 QCOMPARE(o->error(), false);
2576 QCOMPARE(o->invoked(), 14);
2577 QCOMPARE(o->actuals().count(), 1);
2578 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(0)).isUndefined());
2581 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined()));
2582 QCOMPARE(o->error(), false);
2583 QCOMPARE(o->invoked(), 14);
2584 QCOMPARE(o->actuals().count(), 1);
2585 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(0)).strictlyEquals(QJSValue(19)));
2588 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined()));
2589 QCOMPARE(o->error(), false);
2590 QCOMPARE(o->invoked(), 14);
2591 QCOMPARE(o->actuals().count(), 1);
2592 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(0)).isArray());
2595 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined()));
2596 QCOMPARE(o->error(), false);
2597 QCOMPARE(o->invoked(), 15);
2598 QCOMPARE(o->actuals().count(), 2);
2599 QCOMPARE(o->actuals().at(0), QVariant(4));
2600 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(1)).isNull());
2603 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined()));
2604 QCOMPARE(o->error(), false);
2605 QCOMPARE(o->invoked(), 15);
2606 QCOMPARE(o->actuals().count(), 2);
2607 QCOMPARE(o->actuals().at(0), QVariant(8));
2608 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(1)).isUndefined());
2611 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined()));
2612 QCOMPARE(o->error(), false);
2613 QCOMPARE(o->invoked(), 15);
2614 QCOMPARE(o->actuals().count(), 2);
2615 QCOMPARE(o->actuals().at(0), QVariant(3));
2616 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(1)).strictlyEquals(QJSValue(19)));
2619 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined()));
2620 QCOMPARE(o->error(), false);
2621 QCOMPARE(o->invoked(), 15);
2622 QCOMPARE(o->actuals().count(), 2);
2623 QCOMPARE(o->actuals().at(0), QVariant(44));
2624 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(1)).isArray());
2627 QVERIFY(EVALUATE_ERROR("object.method_overload()"));
2628 QCOMPARE(o->error(), false);
2629 QCOMPARE(o->invoked(), -1);
2630 QCOMPARE(o->actuals().count(), 0);
2633 QVERIFY(EVALUATE_VALUE("object.method_overload(10)", v8::Undefined()));
2634 QCOMPARE(o->error(), false);
2635 QCOMPARE(o->invoked(), 16);
2636 QCOMPARE(o->actuals().count(), 1);
2637 QCOMPARE(o->actuals().at(0), QVariant(10));
2640 QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", v8::Undefined()));
2641 QCOMPARE(o->error(), false);
2642 QCOMPARE(o->invoked(), 17);
2643 QCOMPARE(o->actuals().count(), 2);
2644 QCOMPARE(o->actuals().at(0), QVariant(10));
2645 QCOMPARE(o->actuals().at(1), QVariant(11));
2648 QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", v8::Undefined()));
2649 QCOMPARE(o->error(), false);
2650 QCOMPARE(o->invoked(), 18);
2651 QCOMPARE(o->actuals().count(), 1);
2652 QCOMPARE(o->actuals().at(0), QVariant(QString("Hello")));
2655 QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", v8::Undefined()));
2656 QCOMPARE(o->error(), false);
2657 QCOMPARE(o->invoked(), 19);
2658 QCOMPARE(o->actuals().count(), 1);
2659 QCOMPARE(o->actuals().at(0), QVariant(9));
2662 QVERIFY(EVALUATE_VALUE("object.method_default(10)", v8::Integer::New(19)));
2663 QCOMPARE(o->error(), false);
2664 QCOMPARE(o->invoked(), 20);
2665 QCOMPARE(o->actuals().count(), 2);
2666 QCOMPARE(o->actuals().at(0), QVariant(10));
2667 QCOMPARE(o->actuals().at(1), QVariant(19));
2670 QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", v8::Integer::New(13)));
2671 QCOMPARE(o->error(), false);
2672 QCOMPARE(o->invoked(), 20);
2673 QCOMPARE(o->actuals().count(), 2);
2674 QCOMPARE(o->actuals().at(0), QVariant(10));
2675 QCOMPARE(o->actuals().at(1), QVariant(13));
2678 QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", v8::Undefined()));
2679 QCOMPARE(o->error(), false);
2680 QCOMPARE(o->invoked(), -3);
2681 QCOMPARE(o->actuals().count(), 1);
2682 QCOMPARE(o->actuals().at(0), QVariant(9));
2685 QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", v8::Undefined()));
2686 QCOMPARE(o->error(), false);
2687 QCOMPARE(o->invoked(), 21);
2688 QCOMPARE(o->actuals().count(), 2);
2689 QCOMPARE(o->actuals().at(0), QVariant(9));
2690 QCOMPARE(o->actuals().at(1), QVariant());
2693 QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", v8::Undefined()));
2694 QCOMPARE(o->error(), false);
2695 QCOMPARE(o->invoked(), 21);
2696 QCOMPARE(o->actuals().count(), 2);
2697 QCOMPARE(o->actuals().at(0), QVariant(QString("Hello")));
2698 QCOMPARE(o->actuals().at(1), QVariant(QString("World")));
2701 QVERIFY(EVALUATE_VALUE("object.method_QJsonObject({foo:123})", v8::Undefined()));
2702 QCOMPARE(o->error(), false);
2703 QCOMPARE(o->invoked(), 22);
2704 QCOMPARE(o->actuals().count(), 1);
2705 QCOMPARE(qvariant_cast<QJsonObject>(o->actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2708 QVERIFY(EVALUATE_VALUE("object.method_QJsonArray([123])", v8::Undefined()));
2709 QCOMPARE(o->error(), false);
2710 QCOMPARE(o->invoked(), 23);
2711 QCOMPARE(o->actuals().count(), 1);
2712 QCOMPARE(qvariant_cast<QJsonArray>(o->actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2715 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(123)", v8::Undefined()));
2716 QCOMPARE(o->error(), false);
2717 QCOMPARE(o->invoked(), 24);
2718 QCOMPARE(o->actuals().count(), 1);
2719 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(123));
2722 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(42.35)", v8::Undefined()));
2723 QCOMPARE(o->error(), false);
2724 QCOMPARE(o->invoked(), 24);
2725 QCOMPARE(o->actuals().count(), 1);
2726 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(42.35));
2729 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue('ciao')", v8::Undefined()));
2730 QCOMPARE(o->error(), false);
2731 QCOMPARE(o->invoked(), 24);
2732 QCOMPARE(o->actuals().count(), 1);
2733 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QStringLiteral("ciao")));
2736 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(true)", v8::Undefined()));
2737 QCOMPARE(o->error(), false);
2738 QCOMPARE(o->invoked(), 24);
2739 QCOMPARE(o->actuals().count(), 1);
2740 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(true));
2743 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(false)", v8::Undefined()));
2744 QCOMPARE(o->error(), false);
2745 QCOMPARE(o->invoked(), 24);
2746 QCOMPARE(o->actuals().count(), 1);
2747 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(false));
2750 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(null)", v8::Undefined()));
2751 QCOMPARE(o->error(), false);
2752 QCOMPARE(o->invoked(), 24);
2753 QCOMPARE(o->actuals().count(), 1);
2754 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QJsonValue::Null));
2757 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(undefined)", v8::Undefined()));
2758 QCOMPARE(o->error(), false);
2759 QCOMPARE(o->invoked(), 24);
2760 QCOMPARE(o->actuals().count(), 1);
2761 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2764 QVERIFY(EVALUATE_VALUE("object.method_overload({foo:123})", v8::Undefined()));
2765 QCOMPARE(o->error(), false);
2766 QCOMPARE(o->invoked(), 25);
2767 QCOMPARE(o->actuals().count(), 1);
2768 QCOMPARE(qvariant_cast<QJsonObject>(o->actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2771 QVERIFY(EVALUATE_VALUE("object.method_overload([123])", v8::Undefined()));
2772 QCOMPARE(o->error(), false);
2773 QCOMPARE(o->invoked(), 26);
2774 QCOMPARE(o->actuals().count(), 1);
2775 QCOMPARE(qvariant_cast<QJsonArray>(o->actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2778 QVERIFY(EVALUATE_VALUE("object.method_overload(null)", v8::Undefined()));
2779 QCOMPARE(o->error(), false);
2780 QCOMPARE(o->invoked(), 27);
2781 QCOMPARE(o->actuals().count(), 1);
2782 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QJsonValue::Null));
2785 QVERIFY(EVALUATE_VALUE("object.method_overload(undefined)", v8::Undefined()));
2786 QCOMPARE(o->error(), false);
2787 QCOMPARE(o->invoked(), 27);
2788 QCOMPARE(o->actuals().count(), 1);
2789 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2792 QVERIFY(EVALUATE_ERROR("object.method_unknown(null)"));
2793 QCOMPARE(o->error(), false);
2794 QCOMPARE(o->invoked(), -1);
2795 QCOMPARE(o->actuals().count(), 0);
2798 // QTBUG-13047 (check that you can pass registered object types as args)
2799 void tst_qqmlecmascript::invokableObjectArg()
2801 QQmlComponent component(&engine, testFileUrl("invokableObjectArg.qml"));
2803 QObject *o = component.create();
2805 MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o);
2807 QCOMPARE(qmlobject->myinvokableObject, qmlobject);
2812 // QTBUG-13047 (check that you can return registered object types from methods)
2813 void tst_qqmlecmascript::invokableObjectRet()
2815 QQmlComponent component(&engine, testFileUrl("invokableObjectRet.qml"));
2817 QObject *o = component.create();
2819 QCOMPARE(o->property("test").toBool(), true);
2823 void tst_qqmlecmascript::invokableEnumRet()
2825 QQmlComponent component(&engine, testFileUrl("invokableEnumRet.qml"));
2827 QObject *o = component.create();
2829 QCOMPARE(o->property("test").toBool(), true);
2834 void tst_qqmlecmascript::listToVariant()
2836 QQmlComponent component(&engine, testFileUrl("listToVariant.qml"));
2838 MyQmlContainer container;
2840 QQmlContext context(engine.rootContext());
2841 context.setContextObject(&container);
2843 QObject *object = component.create(&context);
2844 QVERIFY(object != 0);
2846 QVariant v = object->property("test");
2847 QCOMPARE(v.userType(), qMetaTypeId<QQmlListReference>());
2848 QVERIFY(qvariant_cast<QQmlListReference>(v).object() == &container);
2854 Q_DECLARE_METATYPE(QQmlListProperty<MyQmlObject>)
2855 void tst_qqmlecmascript::listAssignment()
2857 QQmlComponent component(&engine, testFileUrl("listAssignment.qml"));
2858 QObject *obj = component.create();
2859 QCOMPARE(obj->property("list1length").toInt(), 2);
2860 QQmlListProperty<MyQmlObject> list1 = obj->property("list1").value<QQmlListProperty<MyQmlObject> >();
2861 QQmlListProperty<MyQmlObject> list2 = obj->property("list2").value<QQmlListProperty<MyQmlObject> >();
2862 QCOMPARE(list1.count(&list1), list2.count(&list2));
2863 QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0));
2864 QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1));
2869 void tst_qqmlecmascript::multiEngineObject()
2872 obj.setStringProperty("Howdy planet");
2875 e1.rootContext()->setContextProperty("thing", &obj);
2876 QQmlComponent c1(&e1, testFileUrl("multiEngineObject.qml"));
2879 e2.rootContext()->setContextProperty("thing", &obj);
2880 QQmlComponent c2(&e2, testFileUrl("multiEngineObject.qml"));
2882 QObject *o1 = c1.create();
2883 QObject *o2 = c2.create();
2885 QCOMPARE(o1->property("test").toString(), QString("Howdy planet"));
2886 QCOMPARE(o2->property("test").toString(), QString("Howdy planet"));
2892 // Test that references to QObjects are cleanup when the object is destroyed
2893 void tst_qqmlecmascript::deletedObject()
2895 QQmlComponent component(&engine, testFileUrl("deletedObject.qml"));
2897 QObject *object = component.create();
2899 QCOMPARE(object->property("test1").toBool(), true);
2900 QCOMPARE(object->property("test2").toBool(), true);
2901 QCOMPARE(object->property("test3").toBool(), true);
2902 QCOMPARE(object->property("test4").toBool(), true);
2907 void tst_qqmlecmascript::attachedPropertyScope()
2909 QQmlComponent component(&engine, testFileUrl("attachedPropertyScope.qml"));
2911 QObject *object = component.create();
2912 QVERIFY(object != 0);
2914 MyQmlAttachedObject *attached =
2915 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
2916 QVERIFY(attached != 0);
2918 QCOMPARE(object->property("value2").toInt(), 0);
2920 attached->emitMySignal();
2922 QCOMPARE(object->property("value2").toInt(), 9);
2927 void tst_qqmlecmascript::scriptConnect()
2930 QQmlComponent component(&engine, testFileUrl("scriptConnect.1.qml"));
2932 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2933 QVERIFY(object != 0);
2935 QCOMPARE(object->property("test").toBool(), false);
2936 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2937 QCOMPARE(object->property("test").toBool(), true);
2943 QQmlComponent component(&engine, testFileUrl("scriptConnect.2.qml"));
2945 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2946 QVERIFY(object != 0);
2948 QCOMPARE(object->property("test").toBool(), false);
2949 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2950 QCOMPARE(object->property("test").toBool(), true);
2956 QQmlComponent component(&engine, testFileUrl("scriptConnect.3.qml"));
2958 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2959 QVERIFY(object != 0);
2961 QCOMPARE(object->property("test").toBool(), false);
2962 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2963 QCOMPARE(object->property("test").toBool(), true);
2969 QQmlComponent component(&engine, testFileUrl("scriptConnect.4.qml"));
2971 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2972 QVERIFY(object != 0);
2974 QCOMPARE(object->methodCalled(), false);
2975 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2976 QCOMPARE(object->methodCalled(), true);
2982 QQmlComponent component(&engine, testFileUrl("scriptConnect.5.qml"));
2984 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2985 QVERIFY(object != 0);
2987 QCOMPARE(object->methodCalled(), false);
2988 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2989 QCOMPARE(object->methodCalled(), true);
2995 QQmlComponent component(&engine, testFileUrl("scriptConnect.6.qml"));
2997 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2998 QVERIFY(object != 0);
3000 QCOMPARE(object->property("test").toInt(), 0);
3001 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3002 QCOMPARE(object->property("test").toInt(), 2);
3008 void tst_qqmlecmascript::scriptDisconnect()
3011 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.1.qml"));
3013 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3014 QVERIFY(object != 0);
3016 QCOMPARE(object->property("test").toInt(), 0);
3017 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3018 QCOMPARE(object->property("test").toInt(), 1);
3019 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3020 QCOMPARE(object->property("test").toInt(), 2);
3021 emit object->basicSignal();
3022 QCOMPARE(object->property("test").toInt(), 2);
3023 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3024 QCOMPARE(object->property("test").toInt(), 2);
3030 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.2.qml"));
3032 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3033 QVERIFY(object != 0);
3035 QCOMPARE(object->property("test").toInt(), 0);
3036 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3037 QCOMPARE(object->property("test").toInt(), 1);
3038 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3039 QCOMPARE(object->property("test").toInt(), 2);
3040 emit object->basicSignal();
3041 QCOMPARE(object->property("test").toInt(), 2);
3042 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3043 QCOMPARE(object->property("test").toInt(), 2);
3049 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.3.qml"));
3051 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3052 QVERIFY(object != 0);
3054 QCOMPARE(object->property("test").toInt(), 0);
3055 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3056 QCOMPARE(object->property("test").toInt(), 1);
3057 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3058 QCOMPARE(object->property("test").toInt(), 2);
3059 emit object->basicSignal();
3060 QCOMPARE(object->property("test").toInt(), 2);
3061 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3062 QCOMPARE(object->property("test").toInt(), 3);
3067 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.4.qml"));
3069 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3070 QVERIFY(object != 0);
3072 QCOMPARE(object->property("test").toInt(), 0);
3073 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3074 QCOMPARE(object->property("test").toInt(), 1);
3075 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3076 QCOMPARE(object->property("test").toInt(), 2);
3077 emit object->basicSignal();
3078 QCOMPARE(object->property("test").toInt(), 2);
3079 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3080 QCOMPARE(object->property("test").toInt(), 3);
3086 class OwnershipObject : public QObject
3090 OwnershipObject() { object = new QObject; }
3092 QPointer<QObject> object;
3095 QObject *getObject() { return object; }
3098 void tst_qqmlecmascript::ownership()
3100 OwnershipObject own;
3101 QQmlContext *context = new QQmlContext(engine.rootContext());
3102 context->setContextObject(&own);
3105 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3107 QVERIFY(own.object != 0);
3109 QObject *object = component.create(context);
3111 engine.collectGarbage();
3113 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3114 QCoreApplication::processEvents();
3116 QVERIFY(own.object == 0);
3121 own.object = new QObject(&own);
3124 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3126 QVERIFY(own.object != 0);
3128 QObject *object = component.create(context);
3130 engine.collectGarbage();
3132 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3133 QCoreApplication::processEvents();
3135 QVERIFY(own.object != 0);
3143 class CppOwnershipReturnValue : public QObject
3147 CppOwnershipReturnValue() : value(0) {}
3148 ~CppOwnershipReturnValue() { delete value; }
3150 Q_INVOKABLE QObject *create() {
3151 value = new QObject;
3152 QQmlEngine::setObjectOwnership(value, QQmlEngine::CppOwnership);
3156 Q_INVOKABLE MyQmlObject *createQmlObject() {
3157 MyQmlObject *rv = new MyQmlObject;
3162 QPointer<QObject> value;
3166 // Test setObjectOwnership(CppOwnership) works even when there is no QQmlData
3167 void tst_qqmlecmascript::cppOwnershipReturnValue()
3169 CppOwnershipReturnValue source;
3173 engine.rootContext()->setContextProperty("source", &source);
3175 QVERIFY(source.value == 0);
3177 QQmlComponent component(&engine);
3178 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl());
3180 QObject *object = component.create();
3182 QVERIFY(object != 0);
3183 QVERIFY(source.value != 0);
3188 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3189 QCoreApplication::processEvents();
3191 QVERIFY(source.value != 0);
3195 void tst_qqmlecmascript::ownershipCustomReturnValue()
3197 CppOwnershipReturnValue source;
3201 engine.rootContext()->setContextProperty("source", &source);
3203 QVERIFY(source.value == 0);
3205 QQmlComponent component(&engine);
3206 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl());
3208 QObject *object = component.create();
3210 QVERIFY(object != 0);
3211 QVERIFY(source.value != 0);
3216 engine.collectGarbage();
3217 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3218 QCoreApplication::processEvents();
3220 QVERIFY(source.value == 0);
3223 //the return value from getObject will be JS ownership,
3224 //unless strong Cpp ownership has been set
3225 class OwnershipChangingObject : public QObject
3229 OwnershipChangingObject(): object(0) { }
3231 QPointer<QObject> object;
3234 QObject *getObject() { return object; }
3235 void setObject(QObject *obj) { object = obj; }
3238 void tst_qqmlecmascript::ownershipRootObject()
3240 OwnershipChangingObject own;
3241 QQmlContext *context = new QQmlContext(engine.rootContext());
3242 context->setContextObject(&own);
3244 QQmlComponent component(&engine, testFileUrl("ownershipRootObject.qml"));
3245 QQmlGuard<QObject> object = component.create(context);
3248 engine.collectGarbage();
3250 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3251 QCoreApplication::processEvents();
3253 QVERIFY(own.object != 0);
3259 void tst_qqmlecmascript::ownershipConsistency()
3261 OwnershipChangingObject own;
3262 QQmlContext *context = new QQmlContext(engine.rootContext());
3263 context->setContextObject(&own);
3265 QString expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":19: Error: Invalid attempt to destroy() an indestructible object");
3266 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3267 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":15: Error: Invalid attempt to destroy() an indestructible object");
3268 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3269 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":6: Error: Invalid attempt to destroy() an indestructible object");
3270 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3271 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":10: Error: Invalid attempt to destroy() an indestructible object");
3272 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3274 QQmlComponent component(&engine, testFileUrl("ownershipConsistency.qml"));
3275 QQmlGuard<QObject> object = component.create(context);
3278 engine.collectGarbage();
3280 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3281 QCoreApplication::processEvents();
3283 QVERIFY(own.object != 0);
3289 void tst_qqmlecmascript::ownershipQmlIncubated()
3291 QQmlComponent component(&engine, testFileUrl("ownershipQmlIncubated.qml"));
3292 QObject *object = component.create();
3295 QTRY_VERIFY(object->property("incubatedItem").value<QObject*>() != 0);
3297 QMetaObject::invokeMethod(object, "deleteIncubatedItem");
3299 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3300 QCoreApplication::processEvents();
3302 QVERIFY(object->property("incubatedItem").value<QObject*>() == 0);
3307 class QListQObjectMethodsObject : public QObject
3311 QListQObjectMethodsObject() {
3312 m_objects.append(new MyQmlObject());
3313 m_objects.append(new MyQmlObject());
3316 ~QListQObjectMethodsObject() {
3317 qDeleteAll(m_objects);
3321 QList<QObject *> getObjects() { return m_objects; }
3324 QList<QObject *> m_objects;
3327 // Tests that returning a QList<QObject*> from a method works
3328 void tst_qqmlecmascript::qlistqobjectMethods()
3330 QListQObjectMethodsObject obj;
3331 QQmlContext *context = new QQmlContext(engine.rootContext());
3332 context->setContextObject(&obj);
3334 QQmlComponent component(&engine, testFileUrl("qlistqobjectMethods.qml"));
3336 QObject *object = component.create(context);
3338 QCOMPARE(object->property("test").toInt(), 2);
3339 QCOMPARE(object->property("test2").toBool(), true);
3346 void tst_qqmlecmascript::strictlyEquals()
3348 QQmlComponent component(&engine, testFileUrl("strictlyEquals.qml"));
3350 QObject *object = component.create();
3351 QVERIFY(object != 0);
3353 QCOMPARE(object->property("test1").toBool(), true);
3354 QCOMPARE(object->property("test2").toBool(), true);
3355 QCOMPARE(object->property("test3").toBool(), true);
3356 QCOMPARE(object->property("test4").toBool(), true);
3357 QCOMPARE(object->property("test5").toBool(), true);
3358 QCOMPARE(object->property("test6").toBool(), true);
3359 QCOMPARE(object->property("test7").toBool(), true);
3360 QCOMPARE(object->property("test8").toBool(), true);
3365 void tst_qqmlecmascript::compiled()
3367 QQmlComponent component(&engine, testFileUrl("compiled.qml"));
3369 QObject *object = component.create();
3370 QVERIFY(object != 0);
3372 QCOMPARE(object->property("test1").toReal(), qreal(15.7));
3373 QCOMPARE(object->property("test2").toReal(), qreal(-6.7));
3374 QCOMPARE(object->property("test3").toBool(), true);
3375 QCOMPARE(object->property("test4").toBool(), false);
3376 QCOMPARE(object->property("test5").toBool(), false);
3377 QCOMPARE(object->property("test6").toBool(), true);
3379 QCOMPARE(object->property("test7").toInt(), 185);
3380 QCOMPARE(object->property("test8").toInt(), 167);
3381 QCOMPARE(object->property("test9").toBool(), true);
3382 QCOMPARE(object->property("test10").toBool(), false);
3383 QCOMPARE(object->property("test11").toBool(), false);
3384 QCOMPARE(object->property("test12").toBool(), true);
3386 QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld"));
3387 QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World"));
3388 QCOMPARE(object->property("test15").toBool(), false);
3389 QCOMPARE(object->property("test16").toBool(), true);
3391 QCOMPARE(object->property("test17").toInt(), 5);
3392 QCOMPARE(object->property("test18").toReal(), qreal(176));
3393 QCOMPARE(object->property("test19").toInt(), 7);
3394 QCOMPARE(object->property("test20").toReal(), qreal(6.7));
3395 QCOMPARE(object->property("test21").toString(), QLatin1String("6.7"));
3396 QCOMPARE(object->property("test22").toString(), QLatin1String("!"));
3397 QCOMPARE(object->property("test23").toBool(), true);
3398 QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33));
3399 QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA));
3404 // Test that numbers assigned in bindings as strings work consistently
3405 void tst_qqmlecmascript::numberAssignment()
3407 QQmlComponent component(&engine, testFileUrl("numberAssignment.qml"));
3409 QObject *object = component.create();
3410 QVERIFY(object != 0);
3412 QCOMPARE(object->property("test1"), QVariant((qreal)6.7));
3413 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3414 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3415 QCOMPARE(object->property("test3"), QVariant((qreal)6));
3416 QCOMPARE(object->property("test4"), QVariant((qreal)6));
3418 QCOMPARE(object->property("test5"), QVariant((int)7));
3419 QCOMPARE(object->property("test6"), QVariant((int)7));
3420 QCOMPARE(object->property("test7"), QVariant((int)6));
3421 QCOMPARE(object->property("test8"), QVariant((int)6));
3423 QCOMPARE(object->property("test9"), QVariant((unsigned int)7));
3424 QCOMPARE(object->property("test10"), QVariant((unsigned int)7));
3425 QCOMPARE(object->property("test11"), QVariant((unsigned int)6));
3426 QCOMPARE(object->property("test12"), QVariant((unsigned int)6));
3431 void tst_qqmlecmascript::propertySplicing()
3433 QQmlComponent component(&engine, testFileUrl("propertySplicing.qml"));
3435 QObject *object = component.create();
3436 QVERIFY(object != 0);
3438 QCOMPARE(object->property("test").toBool(), true);
3444 void tst_qqmlecmascript::signalWithUnknownTypes()
3446 QQmlComponent component(&engine, testFileUrl("signalWithUnknownTypes.qml"));
3448 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3449 QVERIFY(object != 0);
3451 MyQmlObject::MyType type;
3452 type.value = 0x8971123;
3453 emit object->signalWithUnknownType(type);
3455 MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant());
3457 QCOMPARE(result.value, type.value);
3459 MyQmlObject::MyOtherType othertype;
3460 othertype.value = 17;
3461 emit object->signalWithCompletelyUnknownType(othertype);
3463 QVERIFY(!object->variant().isValid());
3468 void tst_qqmlecmascript::signalWithJSValueInVariant_data()
3470 QTest::addColumn<QString>("expression");
3471 QTest::addColumn<QString>("compare");
3473 QString compareStrict("(function(a, b) { return a === b; })");
3474 QTest::newRow("true") << "true" << compareStrict;
3475 QTest::newRow("undefined") << "undefined" << compareStrict;
3476 QTest::newRow("null") << "null" << compareStrict;
3477 QTest::newRow("123") << "123" << compareStrict;
3478 QTest::newRow("'ciao'") << "'ciao'" << compareStrict;
3480 QString comparePropertiesStrict(
3482 " if (typeof b != 'object')"
3484 " var props = Object.getOwnPropertyNames(b);"
3485 " for (var i = 0; i < props.length; ++i) {"
3486 " var p = props[i];"
3487 " return arguments.callee(a[p], b[p]);"
3490 QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })" << comparePropertiesStrict;
3491 QTest::newRow("[10,20,30]") << "[10,20,30]" << comparePropertiesStrict;
3494 void tst_qqmlecmascript::signalWithJSValueInVariant()
3496 QFETCH(QString, expression);
3497 QFETCH(QString, compare);
3499 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3500 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3501 QVERIFY(object != 0);
3503 QJSValue value = engine.evaluate(expression);
3504 QVERIFY(!value.isError());
3505 object->setProperty("expression", expression);
3506 object->setProperty("compare", compare);
3507 object->setProperty("pass", false);
3509 emit object->signalWithVariant(QVariant::fromValue(value));
3510 QVERIFY(object->property("pass").toBool());
3513 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines_data()
3515 signalWithJSValueInVariant_data();
3518 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines()
3520 QFETCH(QString, expression);
3521 QFETCH(QString, compare);
3523 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3524 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3525 QVERIFY(object != 0);
3528 QJSValue value = engine2.evaluate(expression);
3529 QVERIFY(!value.isError());
3530 object->setProperty("expression", expression);
3531 object->setProperty("compare", compare);
3532 object->setProperty("pass", false);
3534 QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine.");
3535 emit object->signalWithVariant(QVariant::fromValue(value));
3536 QVERIFY(!object->property("pass").toBool());
3539 void tst_qqmlecmascript::signalWithQJSValue_data()
3541 signalWithJSValueInVariant_data();
3544 void tst_qqmlecmascript::signalWithQJSValue()
3546 QFETCH(QString, expression);
3547 QFETCH(QString, compare);
3549 QQmlComponent component(&engine, testFileUrl("signalWithQJSValue.qml"));
3550 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3551 QVERIFY(object != 0);
3553 QJSValue value = engine.evaluate(expression);
3554 QVERIFY(!value.isError());
3555 object->setProperty("expression", expression);
3556 object->setProperty("compare", compare);
3557 object->setProperty("pass", false);
3559 emit object->signalWithQJSValue(value);
3561 QVERIFY(object->property("pass").toBool());
3562 QVERIFY(object->qjsvalue().strictlyEquals(value));
3565 void tst_qqmlecmascript::singletonType_data()
3567 QTest::addColumn<QUrl>("testfile");
3568 QTest::addColumn<QString>("errorMessage");
3569 QTest::addColumn<QStringList>("warningMessages");
3570 QTest::addColumn<QStringList>("readProperties");
3571 QTest::addColumn<QVariantList>("readExpectedValues");
3572 QTest::addColumn<QStringList>("writeProperties");
3573 QTest::addColumn<QVariantList>("writeValues");
3574 QTest::addColumn<QStringList>("readBackProperties");
3575 QTest::addColumn<QVariantList>("readBackExpectedValues");
3577 QTest::newRow("qobject, register + read + method [no qualifier]")
3578 << testFileUrl("singletontype/qobjectSingletonTypeNoQualifier.qml")
3581 << (QStringList() << "qobjectPropertyTest" << "qobjectMethodTest")
3582 << (QVariantList() << 20 << 1)
3588 QTest::newRow("script, register + read [no qualifier]")
3589 << testFileUrl("singletontype/scriptSingletonTypeNoQualifier.qml")
3592 << (QStringList() << "scriptTest")
3593 << (QVariantList() << 13)
3599 QTest::newRow("qobject, register + read + method")
3600 << testFileUrl("singletontype/qobjectSingletonType.qml")
3603 << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
3604 << "qobjectMinorVersionMethodTest" << "qobjectMinorVersionTest"
3605 << "qobjectMajorVersionTest" << "qobjectParentedTest")
3606 << (QVariantList() << 20 << 20 << 2 << 1 << 20 << 20 << 26)
3612 QTest::newRow("script, register + read")
3613 << testFileUrl("singletontype/scriptSingletonType.qml")
3616 << (QStringList() << "scriptTest")
3617 << (QVariantList() << 14) // will have incremented, since we create a new engine each row in this test.
3623 QTest::newRow("qobject, writing + readonly constraints")
3624 << testFileUrl("singletontype/qobjectSingletonTypeWriting.qml")
3626 << (QStringList() << QString(testFileUrl("singletontype/qobjectSingletonTypeWriting.qml").toString() + QLatin1String(":15: Error: Cannot assign to read-only property \"qobjectTestProperty\"")))
3627 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3628 << (QVariantList() << 20 << 50 << 10)
3629 << (QStringList() << "firstProperty" << "secondProperty")
3630 << (QVariantList() << 30 << 30)
3631 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3632 << (QVariantList() << 20 << 30 << 30);
3634 QTest::newRow("script, writing + readonly constraints")
3635 << testFileUrl("singletontype/scriptSingletonTypeWriting.qml")
3637 << (QStringList() << QString(testFileUrl("singletontype/scriptSingletonTypeWriting.qml").toString() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\"")))
3638 << (QStringList() << "readBack" << "unchanged")
3639 << (QVariantList() << 15 << 42)
3640 << (QStringList() << "firstProperty" << "secondProperty")
3641 << (QVariantList() << 30 << 30)
3642 << (QStringList() << "readBack" << "unchanged")
3643 << (QVariantList() << 30 << 42);
3645 QTest::newRow("qobject singleton Type enum values in JS")
3646 << testFileUrl("singletontype/qobjectSingletonTypeEnums.qml")
3649 << (QStringList() << "enumValue" << "enumMethod")
3650 << (QVariantList() << 42 << 30)
3656 QTest::newRow("qobject, invalid major version fail")
3657 << testFileUrl("singletontype/singletonTypeMajorVersionFail.qml")
3658 << QString("QQmlComponent: Component is not ready")
3667 QTest::newRow("qobject, invalid minor version fail")
3668 << testFileUrl("singletontype/singletonTypeMinorVersionFail.qml")
3669 << QString("QQmlComponent: Component is not ready")
3678 QTest::newRow("qobject, multiple in namespace")
3679 << testFileUrl("singletontype/singletonTypeMultiple.qml")
3682 << (QStringList() << "first" << "second")
3683 << (QVariantList() << 35 << 42)
3690 void tst_qqmlecmascript::singletonType()
3692 QFETCH(QUrl, testfile);
3693 QFETCH(QString, errorMessage);
3694 QFETCH(QStringList, warningMessages);
3695 QFETCH(QStringList, readProperties);
3696 QFETCH(QVariantList, readExpectedValues);
3697 QFETCH(QStringList, writeProperties);
3698 QFETCH(QVariantList, writeValues);
3699 QFETCH(QStringList, readBackProperties);
3700 QFETCH(QVariantList, readBackExpectedValues);
3702 QQmlEngine cleanEngine; // so tests don't interfere which each other, as singleton types are engine-singletons only.
3703 QQmlComponent component(&cleanEngine, testfile);
3705 if (!errorMessage.isEmpty())
3706 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3708 if (warningMessages.size())
3709 foreach (const QString &warning, warningMessages)
3710 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3712 QObject *object = component.create();
3713 if (!errorMessage.isEmpty()) {
3714 QVERIFY(object == 0);
3716 QVERIFY(object != 0);
3717 for (int i = 0; i < readProperties.size(); ++i)
3718 QCOMPARE(object->property(readProperties.at(i).toLatin1().constData()), readExpectedValues.at(i));
3719 for (int i = 0; i < writeProperties.size(); ++i)
3720 QVERIFY(object->setProperty(writeProperties.at(i).toLatin1().constData(), writeValues.at(i)));
3721 for (int i = 0; i < readBackProperties.size(); ++i)
3722 QCOMPARE(object->property(readBackProperties.at(i).toLatin1().constData()), readBackExpectedValues.at(i));
3727 void tst_qqmlecmascript::singletonTypeCaching_data()
3729 QTest::addColumn<QUrl>("testfile");
3730 QTest::addColumn<QStringList>("readProperties");
3732 QTest::newRow("qobject, caching + read")
3733 << testFileUrl("singletontype/qobjectSingletonTypeCaching.qml")
3734 << (QStringList() << "existingUriTest" << "qobjectParentedTest");
3736 QTest::newRow("script, caching + read")
3737 << testFileUrl("singletontype/scriptSingletonTypeCaching.qml")
3738 << (QStringList() << "scriptTest");
3741 void tst_qqmlecmascript::singletonTypeCaching()
3743 QFETCH(QUrl, testfile);
3744 QFETCH(QStringList, readProperties);
3746 // ensure that the singleton type instances are cached per-engine.
3748 QQmlEngine cleanEngine;
3749 QQmlComponent component(&cleanEngine, testfile);
3750 QObject *object = component.create();
3751 QVERIFY(object != 0);
3752 QList<QVariant> firstValues;
3753 QMetaObject::invokeMethod(object, "modifyValues");
3754 for (int i = 0; i < readProperties.size(); ++i)
3755 firstValues << object->property(readProperties.at(i).toLatin1().constData());
3758 QQmlComponent component2(&cleanEngine, testfile);
3759 QObject *object2 = component2.create();
3760 QVERIFY(object2 != 0);
3761 for (int i = 0; i < readProperties.size(); ++i)
3762 QCOMPARE(object2->property(readProperties.at(i).toLatin1().constData()), firstValues.at(i)); // cached, shouldn't have changed.
3766 void tst_qqmlecmascript::singletonTypeImportOrder()
3768 QQmlComponent component(&engine, testFileUrl("singletontype/singletonTypeImportOrder.qml"));
3769 QObject *object = component.create();
3771 QVERIFY(object->property("v") == 1);
3775 void tst_qqmlecmascript::singletonTypeResolution()
3777 QQmlComponent component(&engine, testFileUrl("singletontype/singletonTypeResolution.qml"));
3778 QObject *object = component.create();
3780 QVERIFY(object->property("success") == true);
3784 void tst_qqmlecmascript::importScripts_data()
3786 QTest::addColumn<QUrl>("testfile");
3787 QTest::addColumn<QString>("errorMessage");
3788 QTest::addColumn<QStringList>("warningMessages");
3789 QTest::addColumn<QStringList>("propertyNames");
3790 QTest::addColumn<QVariantList>("propertyValues");
3792 QTest::newRow("basic functionality")
3793 << testFileUrl("jsimport/testImport.qml")
3796 << (QStringList() << QLatin1String("importedScriptStringValue")
3797 << QLatin1String("importedScriptFunctionValue")
3798 << QLatin1String("importedModuleAttachedPropertyValue")
3799 << QLatin1String("importedModuleEnumValue"))
3800 << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3805 QTest::newRow("import scoping")
3806 << testFileUrl("jsimport/testImportScoping.qml")
3809 << (QStringList() << QLatin1String("componentError"))
3810 << (QVariantList() << QVariant(5));
3812 QTest::newRow("parent scope shouldn't be inherited by import with imports")
3813 << testFileUrl("jsimportfail/failOne.qml")
3815 << (QStringList() << QString(testFileUrl("jsimportfail/failOne.qml").toString() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
3816 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3817 << (QVariantList() << QVariant(QString()));
3819 QTest::newRow("javascript imports in an import should be private to the import scope")
3820 << testFileUrl("jsimportfail/failTwo.qml")
3822 << (QStringList() << QString(testFileUrl("jsimportfail/failTwo.qml").toString() + QLatin1String(":6: ReferenceError: ImportOneJs is not defined")))
3823 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3824 << (QVariantList() << QVariant(QString()));
3826 QTest::newRow("module imports in an import should be private to the import scope")
3827 << testFileUrl("jsimportfail/failThree.qml")
3829 << (QStringList() << QString(testFileUrl("jsimportfail/failThree.qml").toString() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
3830 << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
3831 << (QVariantList() << QVariant(false));
3833 QTest::newRow("typenames in an import should be private to the import scope")
3834 << testFileUrl("jsimportfail/failFour.qml")
3836 << (QStringList() << QString(testFileUrl("jsimportfail/failFour.qml").toString() + QLatin1String(":6: ReferenceError: JsQtTest is not defined")))
3837 << (QStringList() << QLatin1String("importedModuleEnumValue"))
3838 << (QVariantList() << QVariant(0));
3840 QTest::newRow("import with imports has it's own activation scope")
3841 << testFileUrl("jsimportfail/failFive.qml")
3843 << (QStringList() << QString(testFileUrl("jsimportfail/importWithImports.js").toString() + QLatin1String(":8: ReferenceError: Component is not defined")))
3844 << (QStringList() << QLatin1String("componentError"))
3845 << (QVariantList() << QVariant(0));
3847 QTest::newRow("import pragma library script")
3848 << testFileUrl("jsimport/testImportPragmaLibrary.qml")
3851 << (QStringList() << QLatin1String("testValue"))
3852 << (QVariantList() << QVariant(31));
3854 QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3855 << testFileUrl("jsimportfail/testImportPragmaLibrary.qml")
3857 << (QStringList() << QString(testFileUrl("jsimportfail/importPragmaLibrary.js").toString() + QLatin1String(":6: ReferenceError: Component is not defined")))
3858 << (QStringList() << QLatin1String("testValue"))
3859 << (QVariantList() << QVariant(0));
3861 QTest::newRow("import pragma library script which has an import")
3862 << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
3865 << (QStringList() << QLatin1String("testValue"))
3866 << (QVariantList() << QVariant(55));
3868 QTest::newRow("import pragma library script which has a pragma library import")
3869 << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3872 << (QStringList() << QLatin1String("testValue"))
3873 << (QVariantList() << QVariant(18));
3875 QTest::newRow("import singleton type into js import")
3876 << testFileUrl("jsimport/testImportSingletonType.qml")
3879 << (QStringList() << QLatin1String("testValue"))
3880 << (QVariantList() << QVariant(20));
3882 QTest::newRow("import module which exports a script")
3883 << testFileUrl("jsimport/testJsImport.qml")
3886 << (QStringList() << QLatin1String("importedScriptStringValue")
3887 << QLatin1String("renamedScriptStringValue")
3888 << QLatin1String("reimportedScriptStringValue"))
3889 << (QVariantList() << QVariant(QString("Hello"))
3890 << QVariant(QString("Hello"))
3891 << QVariant(QString("Hello")));
3893 QTest::newRow("import module which exports a script which imports a remote module")
3894 << testFileUrl("jsimport/testJsRemoteImport.qml")
3897 << (QStringList() << QLatin1String("importedScriptStringValue")
3898 << QLatin1String("renamedScriptStringValue")
3899 << QLatin1String("reimportedScriptStringValue"))
3900 << (QVariantList() << QVariant(QString("Hello"))
3901 << QVariant(QString("Hello"))
3902 << QVariant(QString("Hello")));
3904 QTest::newRow("malformed import statement")
3905 << testFileUrl("jsimportfail/malformedImport.qml")
3907 << (QStringList() << testFileUrl("jsimportfail/malformedImport.js").toString() + QLatin1String(":1: SyntaxError: Unexpected token ."))
3911 QTest::newRow("malformed file name")
3912 << testFileUrl("jsimportfail/malformedFile.qml")
3914 << (QStringList() << testFileUrl("jsimportfail/malformedFile.js").toString() + QLatin1String(":1:9: Imported file must be a script"))
3918 QTest::newRow("missing file qualifier")
3919 << testFileUrl("jsimportfail/missingFileQualifier.qml")
3921 << (QStringList() << testFileUrl("jsimportfail/missingFileQualifier.js").toString() + QLatin1String(":1:1: File import requires a qualifier"))
3925 QTest::newRow("malformed file qualifier")
3926 << testFileUrl("jsimportfail/malformedFileQualifier.qml")
3928 << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.js").toString() + QLatin1String(":1:20: File import requires a qualifier"))
3932 QTest::newRow("malformed module qualifier 2")
3933 << testFileUrl("jsimportfail/malformedFileQualifier.2.qml")
3935 << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.2.js").toString() + QLatin1String(":1:1: Invalid import qualifier"))
3939 QTest::newRow("malformed module uri")
3940 << testFileUrl("jsimportfail/malformedModule.qml")
3942 << (QStringList() << testFileUrl("jsimportfail/malformedModule.js").toString() + QLatin1String(":1:17: Invalid module URI"))
3946 QTest::newRow("missing module version")
3947 << testFileUrl("jsimportfail/missingModuleVersion.qml")
3949 << (QStringList() << testFileUrl("jsimportfail/missingModuleVersion.js").toString() + QLatin1String(":1:17: Module import requires a version"))
3953 QTest::newRow("malformed module version")
3954 << testFileUrl("jsimportfail/malformedModuleVersion.qml")
3956 << (QStringList() << testFileUrl("jsimportfail/malformedModuleVersion.js").toString() + QLatin1String(":1:17: Module import requires a version"))
3960 QTest::newRow("missing module qualifier")
3961 << testFileUrl("jsimportfail/missingModuleQualifier.qml")
3963 << (QStringList() << testFileUrl("jsimportfail/missingModuleQualifier.js").toString() + QLatin1String(":1:1: Module import requires a qualifier"))
3967 QTest::newRow("malformed module qualifier")
3968 << testFileUrl("jsimportfail/malformedModuleQualifier.qml")
3970 << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.js").toString() + QLatin1String(":1:21: Module import requires a qualifier"))
3974 QTest::newRow("malformed module qualifier 2")
3975 << testFileUrl("jsimportfail/malformedModuleQualifier.2.qml")
3977 << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.2.js").toString() + QLatin1String(":1:1: Invalid import qualifier"))
3982 void tst_qqmlecmascript::importScripts()
3984 QFETCH(QUrl, testfile);
3985 QFETCH(QString, errorMessage);
3986 QFETCH(QStringList, warningMessages);
3987 QFETCH(QStringList, propertyNames);
3988 QFETCH(QVariantList, propertyValues);
3990 TestHTTPServer server(8111);
3991 QVERIFY(server.isValid());
3992 server.serveDirectory(dataDirectory() + "/remote");
3994 QStringList importPathList = engine.importPathList();
3996 QString remotePath(QLatin1String("http://127.0.0.1:8111/"));
3997 engine.addImportPath(remotePath);
3999 QQmlComponent component(&engine, testfile);
4001 if (!errorMessage.isEmpty())
4002 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
4004 if (warningMessages.size())
4005 foreach (const QString &warning, warningMessages)
4006 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
4008 QTRY_VERIFY(component.isReady());
4010 QObject *object = component.create();
4011 if (!errorMessage.isEmpty()) {
4012 QVERIFY(object == 0);
4014 QVERIFY(object != 0);
4015 for (int i = 0; i < propertyNames.size(); ++i)
4016 QCOMPARE(object->property(propertyNames.at(i).toLatin1().constData()), propertyValues.at(i));
4020 engine.setImportPathList(importPathList);
4023 void tst_qqmlecmascript::scarceResources_other()
4025 /* These tests require knowledge of state, since we test values after
4026 performing signal or function invocation. */
4028 QPixmap origPixmap(100, 100);
4029 origPixmap.fill(Qt::blue);
4030 QString srp_name, expectedWarning;
4031 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4032 ScarceResourceObject *eo = 0;
4034 QObject *object = 0;
4036 /* property var semantics */
4038 // test that scarce resources are handled properly in signal invocation
4039 QQmlComponent varComponentTen(&engine, testFileUrl("scarceResourceSignal.var.qml"));
4040 object = varComponentTen.create();
4041 srsc = object->findChild<QObject*>("srsc");
4043 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
4044 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
4045 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4046 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4047 QMetaObject::invokeMethod(srsc, "testSignal");
4048 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
4049 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
4050 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4051 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
4052 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
4053 QVERIFY(srsc->property("scarceResourceCopy").isValid());
4054 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4055 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4056 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
4057 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4060 // test that scarce resources are handled properly from js functions in qml files
4061 QQmlComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml"));
4062 object = varComponentEleven.create();
4063 QVERIFY(object != 0);
4064 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4065 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4066 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4067 QMetaObject::invokeMethod(object, "retrieveScarceResource");
4068 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
4069 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4070 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4071 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
4072 QMetaObject::invokeMethod(object, "releaseScarceResource");
4073 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
4074 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4075 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4076 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4079 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
4080 QQmlComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
4081 object = varComponentTwelve.create();
4082 QVERIFY(object != 0);
4083 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4084 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4085 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4086 srp_name = object->property("srp_name").toString();
4087 expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
4088 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
4089 QMetaObject::invokeMethod(object, "retrieveScarceResource");
4090 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
4091 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4092 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4093 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4096 // test that if an Item which has JS ownership but has a scarce resource property is garbage collected,
4097 // that the scarce resource is removed from the engine's list of scarce resources to clean up.
4098 QQmlComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml"));
4099 object = varComponentThirteen.create();
4100 QVERIFY(object != 0);
4101 QVERIFY(!object->property("varProperty").isValid()); // not assigned yet
4102 QMetaObject::invokeMethod(object, "assignVarProperty");
4103 QVERIFY(ep->scarceResources.isEmpty()); // the scarce resource is a VME property.
4104 QMetaObject::invokeMethod(object, "deassignVarProperty");
4105 QVERIFY(ep->scarceResources.isEmpty()); // should still be empty; the resource should have been released on gc.
4108 /* property variant semantics */
4110 // test that scarce resources are handled properly in signal invocation
4111 QQmlComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml"));
4112 object = variantComponentTen.create();
4113 QVERIFY(object != 0);
4114 srsc = object->findChild<QObject*>("srsc");
4116 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
4117 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
4118 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4119 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4120 QMetaObject::invokeMethod(srsc, "testSignal");
4121 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
4122 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
4123 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4124 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
4125 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
4126 QVERIFY(srsc->property("scarceResourceCopy").isValid());
4127 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4128 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4129 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
4130 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4133 // test that scarce resources are handled properly from js functions in qml files
4134 QQmlComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml"));
4135 object = variantComponentEleven.create();
4136 QVERIFY(object != 0);
4137 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4138 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4139 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4140 QMetaObject::invokeMethod(object, "retrieveScarceResource");
4141 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
4142 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4143 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4144 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
4145 QMetaObject::invokeMethod(object, "releaseScarceResource");
4146 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
4147 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4148 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4149 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4152 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
4153 QQmlComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml"));
4154 object = variantComponentTwelve.create();
4155 QVERIFY(object != 0);
4156 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4157 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4158 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4159 srp_name = object->property("srp_name").toString();
4160 expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
4161 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
4162 QMetaObject::invokeMethod(object, "retrieveScarceResource");
4163 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
4164 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4165 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4166 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4170 void tst_qqmlecmascript::scarceResources_data()
4172 QTest::addColumn<QUrl>("qmlFile");
4173 QTest::addColumn<bool>("readDetachStatus");
4174 QTest::addColumn<bool>("expectedDetachStatus");
4175 QTest::addColumn<QStringList>("propertyNames");
4176 QTest::addColumn<QVariantList>("expectedValidity");
4177 QTest::addColumn<QVariantList>("expectedValues");
4178 QTest::addColumn<QStringList>("expectedErrors");
4180 QPixmap origPixmap(100, 100);
4181 origPixmap.fill(Qt::blue);
4183 /* property var semantics */
4185 // in the following three cases, the instance created from the component
4186 // has a property which is a copy of the scarce resource; hence, the
4187 // resource should NOT be detached prior to deletion of the object instance,
4188 // unless the resource is destroyed explicitly.
4189 QTest::newRow("var: import scarce resource copy directly")
4190 << testFileUrl("scarceResourceCopy.var.qml")
4192 << false // won't be detached, because assigned to property and not explicitly released
4193 << (QStringList() << QLatin1String("scarceResourceCopy"))
4194 << (QList<QVariant>() << true)
4195 << (QList<QVariant>() << origPixmap)
4198 QTest::newRow("var: import scarce resource copy from JS")
4199 << testFileUrl("scarceResourceCopyFromJs.var.qml")
4201 << false // won't be detached, because assigned to property and not explicitly released
4202 << (QStringList() << QLatin1String("scarceResourceCopy"))
4203 << (QList<QVariant>() << true)
4204 << (QList<QVariant>() << origPixmap)
4207 QTest::newRow("var: import released scarce resource copy from JS")
4208 << testFileUrl("scarceResourceDestroyedCopy.var.qml")
4210 << true // explicitly released, so it will be detached
4211 << (QStringList() << QLatin1String("scarceResourceCopy"))
4212 << (QList<QVariant>() << false)
4213 << (QList<QVariant>() << QVariant())
4216 // in the following three cases, no other copy should exist in memory,
4217 // and so it should be detached (unless explicitly preserved).
4218 QTest::newRow("var: import auto-release SR from JS in binding side-effect")
4219 << testFileUrl("scarceResourceTest.var.qml")
4221 << true // auto released, so it will be detached
4222 << (QStringList() << QLatin1String("scarceResourceTest"))
4223 << (QList<QVariant>() << true)
4224 << (QList<QVariant>() << QVariant(100))
4226 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4227 << testFileUrl("scarceResourceTestPreserve.var.qml")
4229 << false // won't be detached because we explicitly preserve it
4230 << (QStringList() << QLatin1String("scarceResourceTest"))
4231 << (QList<QVariant>() << true)
4232 << (QList<QVariant>() << QVariant(100))
4234 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4235 << testFileUrl("scarceResourceTestMultiple.var.qml")
4237 << true // will be detached because all resources were released manually or automatically.
4238 << (QStringList() << QLatin1String("scarceResourceTest"))
4239 << (QList<QVariant>() << true)
4240 << (QList<QVariant>() << QVariant(100))
4243 // In the following three cases, test that scarce resources are handled
4244 // correctly for imports.
4245 QTest::newRow("var: import with no binding")
4246 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4247 << false // cannot check detach status.
4250 << QList<QVariant>()
4251 << QList<QVariant>()
4253 QTest::newRow("var: import with binding without explicit preserve")
4254 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4257 << (QStringList() << QLatin1String("scarceResourceCopy"))
4258 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4259 << (QList<QVariant>() << QVariant())
4261 QTest::newRow("var: import with explicit release after binding evaluation")
4262 << testFileUrl("scarceResourceCopyImport.var.qml")
4265 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4266 << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated.
4267 << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true))
4269 QTest::newRow("var: import with different js objects")
4270 << testFileUrl("scarceResourceCopyImportDifferent.var.qml")
4273 << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4274 << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4275 << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false))
4277 QTest::newRow("var: import with different js objects and explicit release")
4278 << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml")
4281 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4282 << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4283 << (QList<QVariant>() << QVariant(origPixmap) << QVariant())
4285 QTest::newRow("var: import with same js objects and explicit release")
4286 << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml")
4289 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4290 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4291 << (QList<QVariant>() << QVariant() << QVariant())
4293 QTest::newRow("var: binding with same js objects and explicit release")
4294 << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml")
4297 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4298 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4299 << (QList<QVariant>() << QVariant() << QVariant())
4303 /* property variant semantics */
4305 // in the following three cases, the instance created from the component
4306 // has a property which is a copy of the scarce resource; hence, the
4307 // resource should NOT be detached prior to deletion of the object instance,
4308 // unless the resource is destroyed explicitly.
4309 QTest::newRow("variant: import scarce resource copy directly")
4310 << testFileUrl("scarceResourceCopy.variant.qml")
4312 << false // won't be detached, because assigned to property and not explicitly released
4313 << (QStringList() << QLatin1String("scarceResourceCopy"))
4314 << (QList<QVariant>() << true)
4315 << (QList<QVariant>() << origPixmap)
4318 QTest::newRow("variant: import scarce resource copy from JS")
4319 << testFileUrl("scarceResourceCopyFromJs.variant.qml")
4321 << false // won't be detached, because assigned to property and not explicitly released
4322 << (QStringList() << QLatin1String("scarceResourceCopy"))
4323 << (QList<QVariant>() << true)
4324 << (QList<QVariant>() << origPixmap)
4327 QTest::newRow("variant: import released scarce resource copy from JS")
4328 << testFileUrl("scarceResourceDestroyedCopy.variant.qml")
4330 << true // explicitly released, so it will be detached
4331 << (QStringList() << QLatin1String("scarceResourceCopy"))
4332 << (QList<QVariant>() << false)
4333 << (QList<QVariant>() << QVariant())
4336 // in the following three cases, no other copy should exist in memory,
4337 // and so it should be detached (unless explicitly preserved).
4338 QTest::newRow("variant: import auto-release SR from JS in binding side-effect")
4339 << testFileUrl("scarceResourceTest.variant.qml")
4341 << true // auto released, so it will be detached
4342 << (QStringList() << QLatin1String("scarceResourceTest"))
4343 << (QList<QVariant>() << true)
4344 << (QList<QVariant>() << QVariant(100))
4346 QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect")
4347 << testFileUrl("scarceResourceTestPreserve.variant.qml")
4349 << false // won't be detached because we explicitly preserve it
4350 << (QStringList() << QLatin1String("scarceResourceTest"))
4351 << (QList<QVariant>() << true)
4352 << (QList<QVariant>() << QVariant(100))
4354 QTest::newRow("variant: import multiple scarce resources")
4355 << testFileUrl("scarceResourceTestMultiple.variant.qml")
4357 << true // will be detached because all resources were released manually or automatically.
4358 << (QStringList() << QLatin1String("scarceResourceTest"))
4359 << (QList<QVariant>() << true)
4360 << (QList<QVariant>() << QVariant(100))
4363 // In the following three cases, test that scarce resources are handled
4364 // correctly for imports.
4365 QTest::newRow("variant: import with no binding")
4366 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4367 << false // cannot check detach status.
4370 << QList<QVariant>()
4371 << QList<QVariant>()
4373 QTest::newRow("variant: import with binding without explicit preserve")
4374 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4377 << (QStringList() << QLatin1String("scarceResourceCopy"))
4378 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4379 << (QList<QVariant>() << QVariant())
4381 QTest::newRow("variant: import with explicit release after binding evaluation")
4382 << testFileUrl("scarceResourceCopyImport.variant.qml")
4385 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo"))
4386 << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies.
4387 << (QList<QVariant>() << origPixmap << origPixmap << QVariant())
4391 void tst_qqmlecmascript::scarceResources()
4393 QFETCH(QUrl, qmlFile);
4394 QFETCH(bool, readDetachStatus);
4395 QFETCH(bool, expectedDetachStatus);
4396 QFETCH(QStringList, propertyNames);
4397 QFETCH(QVariantList, expectedValidity);
4398 QFETCH(QVariantList, expectedValues);
4399 QFETCH(QStringList, expectedErrors);
4401 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4402 ScarceResourceObject *eo = 0;
4403 QObject *object = 0;
4405 QQmlComponent c(&engine, qmlFile);
4406 object = c.create();
4407 QVERIFY(object != 0);
4408 for (int i = 0; i < propertyNames.size(); ++i) {
4409 QString prop = propertyNames.at(i);
4410 bool validity = expectedValidity.at(i).toBool();
4411 QVariant value = expectedValues.at(i);
4413 QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity);
4414 if (value.type() == QVariant::Int) {
4415 QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt());
4416 } else if (value.type() == QVariant::Pixmap) {
4417 QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>());
4421 if (readDetachStatus) {
4422 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4423 QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
4426 QVERIFY(ep->scarceResources.isEmpty());
4430 void tst_qqmlecmascript::propertyChangeSlots()
4432 // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
4433 QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml"));
4434 QObject *object = component.create();
4435 QVERIFY(object != 0);
4438 // ensure that invalid property names fail properly.
4439 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4440 QQmlComponent e1(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.1.qml"));
4441 QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
4442 QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
4443 object = e1.create();
4444 QVERIFY(object == 0);
4447 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4448 QQmlComponent e2(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.2.qml"));
4449 expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
4450 QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
4451 object = e2.create();
4452 QVERIFY(object == 0);
4455 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4456 QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml"));
4457 expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
4458 QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
4459 object = e3.create();
4460 QVERIFY(object == 0);
4463 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4464 QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml"));
4465 expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
4466 QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
4467 object = e4.create();
4468 QVERIFY(object == 0);
4472 void tst_qqmlecmascript::propertyVar_data()
4474 QTest::addColumn<QUrl>("qmlFile");
4477 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyVar.1.qml");
4478 QTest::newRow("non-bindable object changed") << testFileUrl("propertyVar.2.qml");
4479 QTest::newRow("primitive changed") << testFileUrl("propertyVar.3.qml");
4480 QTest::newRow("javascript array modification") << testFileUrl("propertyVar.4.qml");
4481 QTest::newRow("javascript map modification") << testFileUrl("propertyVar.5.qml");
4482 QTest::newRow("javascript array assignment") << testFileUrl("propertyVar.6.qml");
4483 QTest::newRow("javascript map assignment") << testFileUrl("propertyVar.7.qml");
4484 QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml");
4485 QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml");
4486 QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml");
4487 QTest::newRow("javascript function assignment") << testFileUrl("propertyVar.11.qml");
4488 QTest::newRow("javascript special assignment") << testFileUrl("propertyVar.12.qml");
4489 QTest::newRow("declarative binding assignment") << testFileUrl("propertyVar.13.qml");
4490 QTest::newRow("imperative binding assignment") << testFileUrl("propertyVar.14.qml");
4491 QTest::newRow("stored binding assignment") << testFileUrl("propertyVar.15.qml");
4492 QTest::newRow("function expression binding assignment") << testFileUrl("propertyVar.16.qml");
4495 void tst_qqmlecmascript::propertyVar()
4497 QFETCH(QUrl, qmlFile);
4499 QQmlComponent component(&engine, qmlFile);
4500 QObject *object = component.create();
4501 QVERIFY(object != 0);
4503 QCOMPARE(object->property("test").toBool(), true);
4508 void tst_qqmlecmascript::propertyQJSValue_data()
4510 QTest::addColumn<QUrl>("qmlFile");
4513 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyQJSValue.1.qml");
4514 QTest::newRow("non-bindable object changed") << testFileUrl("propertyQJSValue.2.qml");
4515 QTest::newRow("primitive changed") << testFileUrl("propertyQJSValue.3.qml");
4516 QTest::newRow("javascript array modification") << testFileUrl("propertyQJSValue.4.qml");
4517 QTest::newRow("javascript map modification") << testFileUrl("propertyQJSValue.5.qml");
4518 QTest::newRow("javascript array assignment") << testFileUrl("propertyQJSValue.6.qml");
4519 QTest::newRow("javascript map assignment") << testFileUrl("propertyQJSValue.7.qml");
4520 QTest::newRow("literal property assignment") << testFileUrl("propertyQJSValue.8.qml");
4521 QTest::newRow("qobject property assignment") << testFileUrl("propertyQJSValue.9.qml");
4522 QTest::newRow("base class var property assignment") << testFileUrl("propertyQJSValue.10.qml");
4523 QTest::newRow("javascript function assignment") << testFileUrl("propertyQJSValue.11.qml");
4524 QTest::newRow("javascript special assignment") << testFileUrl("propertyQJSValue.12.qml");
4525 QTest::newRow("declarative binding assignment") << testFileUrl("propertyQJSValue.13.qml");
4526 QTest::newRow("imperative binding assignment") << testFileUrl("propertyQJSValue.14.qml");
4527 QTest::newRow("stored binding assignment") << testFileUrl("propertyQJSValue.15.qml");
4528 QTest::newRow("javascript function binding") << testFileUrl("propertyQJSValue.16.qml");
4530 QTest::newRow("reset property") << testFileUrl("propertyQJSValue.reset.qml");
4531 QTest::newRow("reset property in binding") << testFileUrl("propertyQJSValue.bindingreset.qml");
4534 void tst_qqmlecmascript::propertyQJSValue()
4536 QFETCH(QUrl, qmlFile);
4538 QQmlComponent component(&engine, qmlFile);
4539 QObject *object = component.create();
4540 QVERIFY(object != 0);
4542 QCOMPARE(object->property("test").toBool(), true);
4547 // Tests that we can write QVariant values to var properties from C++
4548 void tst_qqmlecmascript::propertyVarCpp()
4550 QObject *object = 0;
4552 // ensure that writing to and reading from a var property from cpp works as required.
4553 // Literal values stored in var properties can be read and written as QVariants
4554 // of a specific type, whereas object values are read as QVariantMaps.
4555 QQmlComponent component(&engine, testFileUrl("propertyVarCpp.qml"));
4556 object = component.create();
4557 QVERIFY(object != 0);
4558 // assign int to property var that currently has int assigned
4559 QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
4560 QCOMPARE(object->property("varBound"), QVariant(15));
4561 QCOMPARE(object->property("intBound"), QVariant(15));
4562 QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
4563 QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
4564 // assign string to property var that current has bool assigned
4565 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
4566 QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
4567 QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
4568 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
4569 // now enforce behaviour when accessing JavaScript objects from cpp.
4570 QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
4574 static void gc(QQmlEngine &engine)
4576 engine.collectGarbage();
4577 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4578 QCoreApplication::processEvents();
4581 void tst_qqmlecmascript::propertyVarOwnership()
4583 // Referenced JS objects are not collected
4585 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.qml"));
4586 QObject *object = component.create();
4587 QVERIFY(object != 0);
4588 QCOMPARE(object->property("test").toBool(), false);
4589 QMetaObject::invokeMethod(object, "runTest");
4590 QCOMPARE(object->property("test").toBool(), true);
4593 // Referenced JS objects are not collected
4595 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml"));
4596 QObject *object = component.create();
4597 QVERIFY(object != 0);
4598 QCOMPARE(object->property("test").toBool(), false);
4599 QMetaObject::invokeMethod(object, "runTest");
4600 QCOMPARE(object->property("test").toBool(), true);
4603 // Qt objects are not collected until they've been dereferenced
4605 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml"));
4606 QObject *object = component.create();
4607 QVERIFY(object != 0);
4609 QCOMPARE(object->property("test2").toBool(), false);
4610 QCOMPARE(object->property("test2").toBool(), false);
4612 QMetaObject::invokeMethod(object, "runTest");
4613 QCOMPARE(object->property("test1").toBool(), true);
4615 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4616 QVERIFY(!referencedObject.isNull());
4618 QVERIFY(!referencedObject.isNull());
4620 QMetaObject::invokeMethod(object, "runTest2");
4621 QCOMPARE(object->property("test2").toBool(), true);
4623 QVERIFY(referencedObject.isNull());
4627 // Self reference does not prevent Qt object collection
4629 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml"));
4630 QObject *object = component.create();
4631 QVERIFY(object != 0);
4633 QCOMPARE(object->property("test").toBool(), true);
4635 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4636 QVERIFY(!referencedObject.isNull());
4638 QVERIFY(!referencedObject.isNull());
4640 QMetaObject::invokeMethod(object, "runTest");
4642 QVERIFY(referencedObject.isNull());
4646 // Garbage collection cannot result in attempted dereference of empty handle
4648 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.5.qml"));
4649 QObject *object = component.create();
4650 QVERIFY(object != 0);
4651 QMetaObject::invokeMethod(object, "runTest");
4652 QCOMPARE(object->property("test").toBool(), true);
4657 void tst_qqmlecmascript::propertyVarImplicitOwnership()
4659 // The childObject has a reference to a different QObject. We want to ensure
4660 // that the different item will not be cleaned up until required. IE, the childObject
4661 // has implicit ownership of the constructed QObject.
4662 QQmlComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml"));
4663 QObject *object = component.create();
4664 QVERIFY(object != 0);
4665 QMetaObject::invokeMethod(object, "assignCircular");
4666 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4667 QCoreApplication::processEvents();
4668 QObject *rootObject = object->property("vp").value<QObject*>();
4669 QVERIFY(rootObject != 0);
4670 QObject *childObject = rootObject->findChild<QObject*>("text");
4671 QVERIFY(childObject != 0);
4672 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4673 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4674 QMetaObject::invokeMethod(childObject, "constructQObject"); // creates a reference to a constructed QObject.
4675 QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
4676 QVERIFY(!qobjectGuard.isNull());
4677 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4678 QCoreApplication::processEvents();
4679 QVERIFY(!qobjectGuard.isNull());
4680 QMetaObject::invokeMethod(object, "deassignCircular");
4681 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4682 QCoreApplication::processEvents();
4683 QVERIFY(qobjectGuard.isNull()); // should have been collected now.
4687 void tst_qqmlecmascript::propertyVarReparent()
4689 // ensure that nothing breaks if we re-parent objects
4690 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4691 QObject *object = component.create();
4692 QVERIFY(object != 0);
4693 QMetaObject::invokeMethod(object, "assignVarProp");
4694 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4695 QCoreApplication::processEvents();
4696 QObject *rect = object->property("vp").value<QObject*>();
4697 QObject *text = rect->findChild<QObject*>("textOne");
4698 QObject *text2 = rect->findChild<QObject*>("textTwo");
4699 QWeakPointer<QObject> rectGuard(rect);
4700 QWeakPointer<QObject> textGuard(text);
4701 QWeakPointer<QObject> text2Guard(text2);
4702 QVERIFY(!rectGuard.isNull());
4703 QVERIFY(!textGuard.isNull());
4704 QVERIFY(!text2Guard.isNull());
4705 QCOMPARE(text->property("textCanary").toInt(), 11);
4706 QCOMPARE(text2->property("textCanary").toInt(), 12);
4707 // now construct an image which we will reparent.
4708 QMetaObject::invokeMethod(text2, "constructQObject");
4709 QObject *image = text2->property("vp").value<QObject*>();
4710 QWeakPointer<QObject> imageGuard(image);
4711 QVERIFY(!imageGuard.isNull());
4712 QCOMPARE(image->property("imageCanary").toInt(), 13);
4713 // now reparent the "Image" object (currently, it has JS ownership)
4714 image->setParent(text); // shouldn't be collected after deassignVp now, since has a parent.
4715 QMetaObject::invokeMethod(text2, "deassignVp");
4716 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4717 QCoreApplication::processEvents();
4718 QCOMPARE(text->property("textCanary").toInt(), 11);
4719 QCOMPARE(text2->property("textCanary").toInt(), 22);
4720 QVERIFY(!imageGuard.isNull()); // should still be alive.
4721 QCOMPARE(image->property("imageCanary").toInt(), 13); // still able to access var properties
4722 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4723 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4724 QCoreApplication::processEvents();
4725 QVERIFY(imageGuard.isNull()); // should now have been deleted, due to parent being deleted.
4729 void tst_qqmlecmascript::propertyVarReparentNullContext()
4731 // sometimes reparenting can cause problems
4732 // (eg, if the ctxt is collected, varproperties are no longer available)
4733 // this test ensures that no crash occurs in that situation.
4734 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4735 QObject *object = component.create();
4736 QVERIFY(object != 0);
4737 QMetaObject::invokeMethod(object, "assignVarProp");
4738 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4739 QCoreApplication::processEvents();
4740 QObject *rect = object->property("vp").value<QObject*>();
4741 QObject *text = rect->findChild<QObject*>("textOne");
4742 QObject *text2 = rect->findChild<QObject*>("textTwo");
4743 QWeakPointer<QObject> rectGuard(rect);
4744 QWeakPointer<QObject> textGuard(text);
4745 QWeakPointer<QObject> text2Guard(text2);
4746 QVERIFY(!rectGuard.isNull());
4747 QVERIFY(!textGuard.isNull());
4748 QVERIFY(!text2Guard.isNull());
4749 QCOMPARE(text->property("textCanary").toInt(), 11);
4750 QCOMPARE(text2->property("textCanary").toInt(), 12);
4751 // now construct an image which we will reparent.
4752 QMetaObject::invokeMethod(text2, "constructQObject");
4753 QObject *image = text2->property("vp").value<QObject*>();
4754 QWeakPointer<QObject> imageGuard(image);
4755 QVERIFY(!imageGuard.isNull());
4756 QCOMPARE(image->property("imageCanary").toInt(), 13);
4757 // now reparent the "Image" object (currently, it has JS ownership)
4758 image->setParent(object); // reparented to base object. after deassignVarProp, the ctxt will be invalid.
4759 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4760 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4761 QCoreApplication::processEvents();
4762 QVERIFY(!imageGuard.isNull()); // should still be alive.
4763 QVERIFY(!image->property("imageCanary").isValid()); // but varProperties won't be available (null context).
4765 QVERIFY(imageGuard.isNull()); // should now be dead.
4768 void tst_qqmlecmascript::propertyVarCircular()
4770 // enforce behaviour regarding circular references - ensure qdvmemo deletion.
4771 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.qml"));
4772 QObject *object = component.create();
4773 QVERIFY(object != 0);
4774 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4775 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4776 QCoreApplication::processEvents();
4777 QCOMPARE(object->property("canaryInt"), QVariant(5));
4778 QVariant canaryResourceVariant = object->property("canaryResource");
4779 QVERIFY(canaryResourceVariant.isValid());
4780 QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
4781 canaryResourceVariant = QVariant(); // invalidate it to remove one copy of the pixmap from memory.
4782 QMetaObject::invokeMethod(object, "deassignCanaryResource"); // remove one copy of the pixmap from memory
4783 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4784 QCoreApplication::processEvents();
4785 QVERIFY(!canaryResourcePixmap.isDetached()); // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
4786 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4787 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4788 QCoreApplication::processEvents();
4789 QCOMPARE(object->property("canaryInt"), QVariant(2));
4790 QCOMPARE(object->property("canaryResource"), QVariant(1));
4791 QVERIFY(canaryResourcePixmap.isDetached()); // now detached, since orig copy was member of qdvmemo which was deleted.
4795 void tst_qqmlecmascript::propertyVarCircular2()
4797 // track deletion of JS-owned parent item with Cpp-owned child
4798 // where the child has a var property referencing its parent.
4799 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4800 QObject *object = component.create();
4801 QVERIFY(object != 0);
4802 QMetaObject::invokeMethod(object, "assignCircular");
4803 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4804 QCoreApplication::processEvents();
4805 QObject *rootObject = object->property("vp").value<QObject*>();
4806 QVERIFY(rootObject != 0);
4807 QObject *childObject = rootObject->findChild<QObject*>("text");
4808 QVERIFY(childObject != 0);
4809 QWeakPointer<QObject> rootObjectTracker(rootObject);
4810 QVERIFY(!rootObjectTracker.isNull());
4811 QWeakPointer<QObject> childObjectTracker(childObject);
4812 QVERIFY(!childObjectTracker.isNull());
4814 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4815 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4816 QMetaObject::invokeMethod(object, "deassignCircular");
4817 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4818 QCoreApplication::processEvents();
4819 QVERIFY(rootObjectTracker.isNull()); // should have been collected
4820 QVERIFY(childObjectTracker.isNull()); // should have been collected
4824 void tst_qqmlecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
4826 *(int*)(parameter) += 1;
4827 qPersistentDispose(object);
4830 void tst_qqmlecmascript::propertyVarInheritance()
4832 int propertyVarWeakRefCallbackCount = 0;
4834 // enforce behaviour regarding element inheritance - ensure handle disposal.
4835 // The particular component under test here has a chain of references.
4836 QQmlComponent component(&engine, testFileUrl("propertyVar.inherit.qml"));
4837 QObject *object = component.create();
4838 QVERIFY(object != 0);
4839 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4840 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4841 QCoreApplication::processEvents();
4842 // we want to be able to track when the varProperties array of the last metaobject is disposed
4843 QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
4844 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*>();
4845 QQmlVMEMetaObject *icovmemo = QQmlVMEMetaObject::get(ico5);
4846 QQmlVMEMetaObject *ccovmemo = QQmlVMEMetaObject::get(cco5);
4847 v8::Persistent<v8::Value> icoCanaryHandle;
4848 v8::Persistent<v8::Value> ccoCanaryHandle;
4851 // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only
4852 // public function which can return us a handle to something in the varProperties array.
4853 icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ")));
4854 ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")));
4855 // we make them weak and invoke the gc, but we should not hit the weak-callback yet
4856 // as the varproperties array of each vmemo still references the resource.
4857 icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4858 ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4860 QVERIFY(propertyVarWeakRefCallbackCount == 0);
4862 // now we deassign the var prop, which should trigger collection of item subtrees.
4863 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4864 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4865 QCoreApplication::processEvents();
4866 // ensure that there are only weak handles to the underlying varProperties array remaining.
4868 QCOMPARE(propertyVarWeakRefCallbackCount, 2); // should have been called for both, since all refs should be weak.
4870 // since there are no parent vmemo's to keep implicit references alive, and the only handles
4871 // to what remains are weak, all varProperties arrays must have been collected.
4874 void tst_qqmlecmascript::propertyVarInheritance2()
4876 int propertyVarWeakRefCallbackCount = 0;
4878 // The particular component under test here does NOT have a chain of references; the
4879 // only link between rootObject and childObject is that rootObject is the parent of childObject.
4880 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4881 QObject *object = component.create();
4882 QVERIFY(object != 0);
4883 QMetaObject::invokeMethod(object, "assignCircular");
4884 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4885 QCoreApplication::processEvents();
4886 QObject *rootObject = object->property("vp").value<QObject*>();
4887 QVERIFY(rootObject != 0);
4888 QObject *childObject = rootObject->findChild<QObject*>("text");
4889 QVERIFY(childObject != 0);
4890 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4891 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4892 v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
4895 propertyVarWeakRefCallbackCount = 0; // reset callback count.
4896 childObjectVarArrayValueHandle = qPersistentNew(QQmlVMEMetaObject::get(childObject)->vmeProperty(childObject->metaObject()->indexOfProperty("vp")));
4897 childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4899 QVERIFY(propertyVarWeakRefCallbackCount == 0); // should not have been collected yet.
4900 QCOMPARE(childObject->property("vp").value<QObject*>(), rootObject);
4901 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4903 QMetaObject::invokeMethod(object, "deassignCircular");
4904 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4905 QCoreApplication::processEvents();
4906 QVERIFY(propertyVarWeakRefCallbackCount == 1); // should have been collected now.
4910 // Ensure that QObject type conversion works on binding assignment
4911 void tst_qqmlecmascript::elementAssign()
4913 QQmlComponent component(&engine, testFileUrl("elementAssign.qml"));
4915 QObject *object = component.create();
4916 QVERIFY(object != 0);
4918 QCOMPARE(object->property("test").toBool(), true);
4924 void tst_qqmlecmascript::objectPassThroughSignals()
4926 QQmlComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml"));
4928 QObject *object = component.create();
4929 QVERIFY(object != 0);
4931 QCOMPARE(object->property("test").toBool(), true);
4937 void tst_qqmlecmascript::objectConversion()
4939 QQmlComponent component(&engine, testFileUrl("objectConversion.qml"));
4941 QObject *object = component.create();
4942 QVERIFY(object != 0);
4944 QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
4945 QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
4952 void tst_qqmlecmascript::booleanConversion()
4954 QQmlComponent component(&engine, testFileUrl("booleanConversion.qml"));
4956 QObject *object = component.create();
4957 QVERIFY(object != 0);
4959 QCOMPARE(object->property("test_true1").toBool(), true);
4960 QCOMPARE(object->property("test_true2").toBool(), true);
4961 QCOMPARE(object->property("test_true3").toBool(), true);
4962 QCOMPARE(object->property("test_true4").toBool(), true);
4963 QCOMPARE(object->property("test_true5").toBool(), true);
4965 QCOMPARE(object->property("test_false1").toBool(), false);
4966 QCOMPARE(object->property("test_false2").toBool(), false);
4967 QCOMPARE(object->property("test_false3").toBool(), false);
4972 void tst_qqmlecmascript::handleReferenceManagement()
4977 // Linear QObject reference
4978 QQmlEngine hrmEngine;
4979 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml"));
4980 QObject *object = component.create();
4981 QVERIFY(object != 0);
4982 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4983 cro->setEngine(&hrmEngine);
4984 cro->setDtorCount(&dtorCount);
4985 QMetaObject::invokeMethod(object, "createReference");
4987 QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
4989 hrmEngine.collectGarbage();
4990 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4991 QCoreApplication::processEvents();
4992 QCOMPARE(dtorCount, 3);
4997 // Circular QObject reference
4998 QQmlEngine hrmEngine;
4999 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml"));
5000 QObject *object = component.create();
5001 QVERIFY(object != 0);
5002 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
5003 cro->setEngine(&hrmEngine);
5004 cro->setDtorCount(&dtorCount);
5005 QMetaObject::invokeMethod(object, "circularReference");
5007 QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
5009 hrmEngine.collectGarbage();
5010 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5011 QCoreApplication::processEvents();
5012 QCOMPARE(dtorCount, 3);
5017 // Linear handle reference
5018 QQmlEngine hrmEngine;
5019 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.1.qml"));
5020 QObject *object = component.create();
5021 QVERIFY(object != 0);
5022 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
5024 crh->setEngine(&hrmEngine);
5025 crh->setDtorCount(&dtorCount);
5026 QMetaObject::invokeMethod(object, "createReference");
5027 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
5028 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
5029 QVERIFY(first != 0);
5030 QVERIFY(second != 0);
5031 first->addReference(QQmlData::get(second)->v8object); // create reference
5032 // now we have to reparent second and make second owned by JS.
5033 second->setParent(0);
5034 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
5036 QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
5038 hrmEngine.collectGarbage();
5039 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5040 QCoreApplication::processEvents();
5041 QCOMPARE(dtorCount, 3);
5046 // Circular handle reference
5047 QQmlEngine hrmEngine;
5048 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.2.qml"));
5049 QObject *object = component.create();
5050 QVERIFY(object != 0);
5051 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
5053 crh->setEngine(&hrmEngine);
5054 crh->setDtorCount(&dtorCount);
5055 QMetaObject::invokeMethod(object, "circularReference");
5056 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
5057 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
5058 QVERIFY(first != 0);
5059 QVERIFY(second != 0);
5060 first->addReference(QQmlData::get(second)->v8object); // create circular reference
5061 second->addReference(QQmlData::get(first)->v8object); // note: must be weak.
5062 // now we have to reparent and change ownership, and unset the property references.
5063 first->setParent(0);
5064 second->setParent(0);
5065 QQmlEngine::setObjectOwnership(first, QQmlEngine::JavaScriptOwnership);
5066 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
5067 object->setProperty("first", QVariant::fromValue<QObject*>(0));
5068 object->setProperty("second", QVariant::fromValue<QObject*>(0));
5070 QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
5072 hrmEngine.collectGarbage();
5073 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5074 QCoreApplication::processEvents();
5075 QCOMPARE(dtorCount, 3);
5080 // multiple engine interaction - linear reference
5081 QQmlEngine hrmEngine1;
5082 QQmlEngine hrmEngine2;
5083 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
5084 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
5085 QObject *object1 = component1.create();
5086 QObject *object2 = component2.create();
5087 QVERIFY(object1 != 0);
5088 QVERIFY(object2 != 0);
5089 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
5090 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
5093 crh1->setEngine(&hrmEngine1);
5094 crh2->setEngine(&hrmEngine2);
5095 crh1->setDtorCount(&dtorCount);
5096 crh2->setDtorCount(&dtorCount);
5097 QMetaObject::invokeMethod(object1, "createReference");
5098 QMetaObject::invokeMethod(object2, "createReference");
5099 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
5100 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
5101 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
5102 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
5103 QVERIFY(first1 != 0);
5104 QVERIFY(second1 != 0);
5105 QVERIFY(first2 != 0);
5106 QVERIFY(second2 != 0);
5107 first1->addReference(QQmlData::get(second2)->v8object); // create reference across engines
5108 // now we have to reparent second2 and make second2 owned by JS.
5109 second2->setParent(0);
5110 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5112 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5113 QCoreApplication::processEvents();
5114 QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
5117 hrmEngine1.collectGarbage();
5118 hrmEngine2.collectGarbage();
5119 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5120 QCoreApplication::processEvents();
5121 QCOMPARE(dtorCount, 6);
5126 // multiple engine interaction - circular reference
5127 QQmlEngine hrmEngine1;
5128 QQmlEngine hrmEngine2;
5129 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
5130 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
5131 QObject *object1 = component1.create();
5132 QObject *object2 = component2.create();
5133 QVERIFY(object1 != 0);
5134 QVERIFY(object2 != 0);
5135 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
5136 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
5139 crh1->setEngine(&hrmEngine1);
5140 crh2->setEngine(&hrmEngine2);
5141 crh1->setDtorCount(&dtorCount);
5142 crh2->setDtorCount(&dtorCount);
5143 QMetaObject::invokeMethod(object1, "createReference");
5144 QMetaObject::invokeMethod(object2, "createReference");
5145 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
5146 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
5147 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
5148 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
5149 QVERIFY(first1 != 0);
5150 QVERIFY(second1 != 0);
5151 QVERIFY(first2 != 0);
5152 QVERIFY(second2 != 0);
5153 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
5154 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
5155 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
5156 first2->addReference(QQmlData::get(first1)->v8object); // close the loop - circular ref across engines
5157 // now we have to reparent and change ownership to JS, and remove property references.
5158 first1->setParent(0);
5159 second1->setParent(0);
5160 first2->setParent(0);
5161 second2->setParent(0);
5162 QQmlEngine::setObjectOwnership(first1, QQmlEngine::JavaScriptOwnership);
5163 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
5164 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
5165 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5166 object1->setProperty("first", QVariant::fromValue<QObject*>(0));
5167 object1->setProperty("second", QVariant::fromValue<QObject*>(0));
5168 object2->setProperty("first", QVariant::fromValue<QObject*>(0));
5169 object2->setProperty("second", QVariant::fromValue<QObject*>(0));
5171 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5172 QCoreApplication::processEvents();
5173 QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
5176 hrmEngine1.collectGarbage();
5177 hrmEngine2.collectGarbage();
5178 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5179 QCoreApplication::processEvents();
5180 QCOMPARE(dtorCount, 6);
5185 // multiple engine interaction - linear reference with engine deletion
5186 QQmlEngine *hrmEngine1 = new QQmlEngine;
5187 QQmlEngine *hrmEngine2 = new QQmlEngine;
5188 QQmlComponent component1(hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
5189 QQmlComponent component2(hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
5190 QObject *object1 = component1.create();
5191 QObject *object2 = component2.create();
5192 QVERIFY(object1 != 0);
5193 QVERIFY(object2 != 0);
5194 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
5195 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
5198 crh1->setEngine(hrmEngine1);
5199 crh2->setEngine(hrmEngine2);
5200 crh1->setDtorCount(&dtorCount);
5201 crh2->setDtorCount(&dtorCount);
5202 QMetaObject::invokeMethod(object1, "createReference");
5203 QMetaObject::invokeMethod(object2, "createReference");
5204 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
5205 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
5206 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
5207 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
5208 QVERIFY(first1 != 0);
5209 QVERIFY(second1 != 0);
5210 QVERIFY(first2 != 0);
5211 QVERIFY(second2 != 0);
5212 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
5213 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
5214 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
5215 // now we have to reparent and change ownership to JS.
5216 first1->setParent(crh1);
5217 second1->setParent(0);
5218 first2->setParent(0);
5219 second2->setParent(0);
5220 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
5221 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
5222 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5225 QCOMPARE(dtorCount, 0);
5226 delete hrmEngine2; // should trigger deletion of objects with JS ownership tracked by this engine
5228 QCOMPARE(dtorCount, 2); // first2 and second2 should have been deleted.
5232 QCOMPARE(dtorCount, 6); // deleting object1 and object2 should trigger deletion of first1 and first2.
5234 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5235 QCoreApplication::processEvents();
5236 QCOMPARE(dtorCount, 6); // all objects should have been cleaned up prior to deleting hrmEngine1.
5240 // Dynamic variant property reference keeps target alive
5241 QQmlEngine hrmEngine;
5242 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.qml"));
5243 QObject *object = component.create();
5244 QVERIFY(object != 0);
5245 QMetaObject::invokeMethod(object, "createReference");
5247 QMetaObject::invokeMethod(object, "ensureReference");
5249 QMetaObject::invokeMethod(object, "removeReference");
5251 QMetaObject::invokeMethod(object, "ensureDeletion");
5252 QCOMPARE(object->property("success").toBool(), true);
5257 // Dynamic Item property reference keeps target alive
5258 QQmlEngine hrmEngine;
5259 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.2.qml"));
5260 QObject *object = component.create();
5261 QVERIFY(object != 0);
5262 QMetaObject::invokeMethod(object, "createReference");
5264 QMetaObject::invokeMethod(object, "ensureReference");
5266 QMetaObject::invokeMethod(object, "removeReference");
5268 QMetaObject::invokeMethod(object, "ensureDeletion");
5269 QCOMPARE(object->property("success").toBool(), true);
5274 // Item property reference to deleted item doesn't crash
5275 QQmlEngine hrmEngine;
5276 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.3.qml"));
5277 QObject *object = component.create();
5278 QVERIFY(object != 0);
5279 QMetaObject::invokeMethod(object, "createReference");
5281 QMetaObject::invokeMethod(object, "ensureReference");
5283 QMetaObject::invokeMethod(object, "manuallyDelete");
5285 QMetaObject::invokeMethod(object, "ensureDeleted");
5286 QCOMPARE(object->property("success").toBool(), true);
5291 void tst_qqmlecmascript::stringArg()
5293 QQmlComponent component(&engine, testFileUrl("stringArg.qml"));
5294 QObject *object = component.create();
5295 QVERIFY(object != 0);
5296 QMetaObject::invokeMethod(object, "success");
5297 QVERIFY(object->property("returnValue").toBool());
5299 QString w1 = testFileUrl("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
5300 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5301 QMetaObject::invokeMethod(object, "failure");
5302 QVERIFY(object->property("returnValue").toBool());
5307 void tst_qqmlecmascript::readonlyDeclaration()
5309 QQmlComponent component(&engine, testFileUrl("readonlyDeclaration.qml"));
5311 QObject *object = component.create();
5312 QVERIFY(object != 0);
5314 QCOMPARE(object->property("test").toBool(), true);
5319 Q_DECLARE_METATYPE(QList<int>)
5320 Q_DECLARE_METATYPE(QList<qreal>)
5321 Q_DECLARE_METATYPE(QList<bool>)
5322 Q_DECLARE_METATYPE(QList<QString>)
5323 Q_DECLARE_METATYPE(QList<QUrl>)
5324 void tst_qqmlecmascript::sequenceConversionRead()
5327 QUrl qmlFile = testFileUrl("sequenceConversion.read.qml");
5328 QQmlComponent component(&engine, qmlFile);
5329 QObject *object = component.create();
5330 QVERIFY(object != 0);
5331 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5334 QMetaObject::invokeMethod(object, "readSequences");
5335 QList<int> intList; intList << 1 << 2 << 3 << 4;
5336 QCOMPARE(object->property("intListLength").toInt(), intList.length());
5337 QCOMPARE(object->property("intList").value<QList<int> >(), intList);
5338 QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
5339 QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
5340 QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
5341 QList<bool> boolList; boolList << true << false << true << false;
5342 QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
5343 QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
5344 QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5345 QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
5346 QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
5347 QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
5348 QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
5349 QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
5350 QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5351 QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
5352 QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
5354 QMetaObject::invokeMethod(object, "readSequenceElements");
5355 QCOMPARE(object->property("intVal").toInt(), 2);
5356 QCOMPARE(object->property("qrealVal").toReal(), 2.2);
5357 QCOMPARE(object->property("boolVal").toBool(), false);
5358 QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
5359 QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
5360 QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
5362 QMetaObject::invokeMethod(object, "enumerateSequenceElements");
5363 QCOMPARE(object->property("enumerationMatches").toBool(), true);
5365 intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
5366 QQmlProperty seqProp(seq, "intListProperty");
5367 QCOMPARE(seqProp.read().value<QList<int> >(), intList);
5368 QQmlProperty seqProp2(seq, "intListProperty", &engine);
5369 QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
5371 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5372 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5378 QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml");
5379 QQmlComponent component(&engine, qmlFile);
5380 QObject *object = component.create();
5381 QVERIFY(object != 0);
5382 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5385 // we haven't registered QList<QPoint> as a sequence type.
5386 QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5387 QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
5388 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5389 QTest::ignoreMessage(QtWarningMsg, warningTwo.toLatin1().constData());
5391 QMetaObject::invokeMethod(object, "performTest");
5393 // QList<QPoint> has not been registered as a sequence type.
5394 QCOMPARE(object->property("pointListLength").toInt(), 0);
5395 QVERIFY(!object->property("pointList").isValid());
5396 QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5397 QQmlProperty seqProp(seq, "pointListProperty", &engine);
5398 QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
5404 void tst_qqmlecmascript::sequenceConversionWrite()
5407 QUrl qmlFile = testFileUrl("sequenceConversion.write.qml");
5408 QQmlComponent component(&engine, qmlFile);
5409 QObject *object = component.create();
5410 QVERIFY(object != 0);
5411 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5414 QMetaObject::invokeMethod(object, "writeSequences");
5415 QCOMPARE(object->property("success").toBool(), true);
5417 QMetaObject::invokeMethod(object, "writeSequenceElements");
5418 QCOMPARE(object->property("success").toBool(), true);
5420 QMetaObject::invokeMethod(object, "writeOtherElements");
5421 QCOMPARE(object->property("success").toBool(), true);
5423 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5424 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5430 QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml");
5431 QQmlComponent component(&engine, qmlFile);
5432 QObject *object = component.create();
5433 QVERIFY(object != 0);
5434 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5437 // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
5438 QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to an unregistered type");
5439 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5441 QMetaObject::invokeMethod(object, "performTest");
5443 QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
5444 QCOMPARE(seq->pointListProperty(), pointList);
5450 void tst_qqmlecmascript::sequenceConversionArray()
5452 // ensure that in JS the returned sequences act just like normal JS Arrays.
5453 QUrl qmlFile = testFileUrl("sequenceConversion.array.qml");
5454 QQmlComponent component(&engine, qmlFile);
5455 QObject *object = component.create();
5456 QVERIFY(object != 0);
5457 QMetaObject::invokeMethod(object, "indexedAccess");
5458 QVERIFY(object->property("success").toBool());
5459 QMetaObject::invokeMethod(object, "arrayOperations");
5460 QVERIFY(object->property("success").toBool());
5461 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5462 QVERIFY(object->property("success").toBool());
5463 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5464 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5469 void tst_qqmlecmascript::sequenceConversionIndexes()
5471 // ensure that we gracefully fail if unsupported index values are specified.
5472 // Qt container classes only support non-negative, signed integer index values.
5473 QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
5474 QQmlComponent component(&engine, qmlFile);
5475 QObject *object = component.create();
5476 QVERIFY(object != 0);
5477 QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
5478 QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
5479 QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
5480 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
5481 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
5482 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
5483 QMetaObject::invokeMethod(object, "indexedAccess");
5484 QVERIFY(object->property("success").toBool());
5488 void tst_qqmlecmascript::sequenceConversionThreads()
5490 // ensure that sequence conversion operations work correctly in a worker thread
5491 // and that serialisation between the main and worker thread succeeds.
5492 QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml");
5493 QQmlComponent component(&engine, qmlFile);
5494 QObject *object = component.create();
5495 QVERIFY(object != 0);
5497 QMetaObject::invokeMethod(object, "testIntSequence");
5498 QTRY_VERIFY(object->property("finished").toBool());
5499 QVERIFY(object->property("success").toBool());
5501 QMetaObject::invokeMethod(object, "testQrealSequence");
5502 QTRY_VERIFY(object->property("finished").toBool());
5503 QVERIFY(object->property("success").toBool());
5505 QMetaObject::invokeMethod(object, "testBoolSequence");
5506 QTRY_VERIFY(object->property("finished").toBool());
5507 QVERIFY(object->property("success").toBool());
5509 QMetaObject::invokeMethod(object, "testStringSequence");
5510 QTRY_VERIFY(object->property("finished").toBool());
5511 QVERIFY(object->property("success").toBool());
5513 QMetaObject::invokeMethod(object, "testQStringSequence");
5514 QTRY_VERIFY(object->property("finished").toBool());
5515 QVERIFY(object->property("success").toBool());
5517 QMetaObject::invokeMethod(object, "testUrlSequence");
5518 QTRY_VERIFY(object->property("finished").toBool());
5519 QVERIFY(object->property("success").toBool());
5521 QMetaObject::invokeMethod(object, "testVariantSequence");
5522 QTRY_VERIFY(object->property("finished").toBool());
5523 QVERIFY(object->property("success").toBool());
5528 void tst_qqmlecmascript::sequenceConversionBindings()
5531 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml");
5532 QQmlComponent component(&engine, qmlFile);
5533 QObject *object = component.create();
5534 QVERIFY(object != 0);
5535 QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
5536 QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
5537 QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
5538 QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
5539 QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
5544 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml");
5545 QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
5546 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5547 QQmlComponent component(&engine, qmlFile);
5548 QObject *object = component.create();
5549 QVERIFY(object != 0);
5554 void tst_qqmlecmascript::sequenceConversionCopy()
5556 QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml");
5557 QQmlComponent component(&engine, qmlFile);
5558 QObject *object = component.create();
5559 QVERIFY(object != 0);
5560 QMetaObject::invokeMethod(object, "testCopySequences");
5561 QCOMPARE(object->property("success").toBool(), true);
5562 QMetaObject::invokeMethod(object, "readSequenceCopyElements");
5563 QCOMPARE(object->property("success").toBool(), true);
5564 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5565 QCOMPARE(object->property("success").toBool(), true);
5566 QMetaObject::invokeMethod(object, "testCopyParameters");
5567 QCOMPARE(object->property("success").toBool(), true);
5568 QMetaObject::invokeMethod(object, "testReferenceParameters");
5569 QCOMPARE(object->property("success").toBool(), true);
5573 void tst_qqmlecmascript::assignSequenceTypes()
5575 // test binding array to sequence type property
5577 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml"));
5578 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5579 QVERIFY(object != 0);
5580 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5581 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5582 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5583 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5584 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5585 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5589 // test binding literal to sequence type property
5591 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml"));
5592 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5593 QVERIFY(object != 0);
5594 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5595 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5596 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5597 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5598 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5599 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5603 // test binding single value to sequence type property
5605 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml"));
5606 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5607 QVERIFY(object != 0);
5608 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5609 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5610 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5611 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5615 // test assigning array to sequence type property in js function
5617 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml"));
5618 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5619 QVERIFY(object != 0);
5620 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5621 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5622 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5623 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5624 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5625 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5629 // test assigning literal to sequence type property in js function
5631 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml"));
5632 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5633 QVERIFY(object != 0);
5634 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5635 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5636 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5637 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5638 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5639 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5643 // test assigning single value to sequence type property in js function
5645 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml"));
5646 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5647 QVERIFY(object != 0);
5648 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5649 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5650 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5651 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5655 // test QList<QUrl> literal assignment and binding assignment causes url resolution when required
5657 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml"));
5658 QObject *object = component.create();
5659 QVERIFY(object != 0);
5660 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
5661 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
5662 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
5663 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
5664 MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco5"));
5665 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0);
5666 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5667 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5668 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5669 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5670 QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5675 // Test that assigning a null object works
5676 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
5677 void tst_qqmlecmascript::nullObjectBinding()
5679 QQmlComponent component(&engine, testFileUrl("nullObjectBinding.qml"));
5681 QObject *object = component.create();
5682 QVERIFY(object != 0);
5684 QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
5689 // Test that bindings don't evaluate once the engine has been destroyed
5690 void tst_qqmlecmascript::deletedEngine()
5692 QQmlEngine *engine = new QQmlEngine;
5693 QQmlComponent component(engine, testFileUrl("deletedEngine.qml"));
5695 QObject *object = component.create();
5696 QVERIFY(object != 0);
5698 QCOMPARE(object->property("a").toInt(), 39);
5699 object->setProperty("b", QVariant(9));
5700 QCOMPARE(object->property("a").toInt(), 117);
5704 QCOMPARE(object->property("a").toInt(), 117);
5705 object->setProperty("b", QVariant(10));
5706 QCOMPARE(object->property("a").toInt(), 117);
5711 // Test the crashing part of QTBUG-9705
5712 void tst_qqmlecmascript::libraryScriptAssert()
5714 QQmlComponent component(&engine, testFileUrl("libraryScriptAssert.qml"));
5716 QObject *object = component.create();
5717 QVERIFY(object != 0);
5722 void tst_qqmlecmascript::variantsAssignedUndefined()
5724 QQmlComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml"));
5726 QObject *object = component.create();
5727 QVERIFY(object != 0);
5729 QCOMPARE(object->property("test1").toInt(), 10);
5730 QCOMPARE(object->property("test2").toInt(), 11);
5732 object->setProperty("runTest", true);
5734 QCOMPARE(object->property("test1"), QVariant());
5735 QCOMPARE(object->property("test2"), QVariant());
5741 void tst_qqmlecmascript::qtbug_9792()
5743 QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml"));
5745 QQmlContext *context = new QQmlContext(engine.rootContext());
5747 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
5748 QVERIFY(object != 0);
5750 QTest::ignoreMessage(QtDebugMsg, "Hello world!");
5751 object->basicSignal();
5755 transientErrorsMsgCount = 0;
5756 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5758 object->basicSignal();
5760 qInstallMsgHandler(old);
5762 QCOMPARE(transientErrorsMsgCount, 0);
5767 // Verifies that QQmlGuard<>s used in the vmemetaobject are cleaned correctly
5768 void tst_qqmlecmascript::qtcreatorbug_1289()
5770 QQmlComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml"));
5772 QObject *o = component.create();
5775 QObject *nested = qvariant_cast<QObject *>(o->property("object"));
5776 QVERIFY(nested != 0);
5778 QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
5781 nested = qvariant_cast<QObject *>(o->property("object"));
5782 QVERIFY(nested == 0);
5784 // If the bug is present, the next line will crash
5788 // Test that we shut down without stupid warnings
5789 void tst_qqmlecmascript::noSpuriousWarningsAtShutdown()
5792 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml"));
5794 QObject *o = component.create();
5796 transientErrorsMsgCount = 0;
5797 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5801 qInstallMsgHandler(old);
5803 QCOMPARE(transientErrorsMsgCount, 0);
5808 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml"));
5810 QObject *o = component.create();
5812 transientErrorsMsgCount = 0;
5813 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5817 qInstallMsgHandler(old);
5819 QCOMPARE(transientErrorsMsgCount, 0);
5823 void tst_qqmlecmascript::canAssignNullToQObject()
5826 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml"));
5828 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5831 QVERIFY(o->objectProperty() != 0);
5833 o->setProperty("runTest", true);
5835 QVERIFY(o->objectProperty() == 0);
5841 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml"));
5843 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5846 QVERIFY(o->objectProperty() == 0);
5852 void tst_qqmlecmascript::functionAssignment_fromBinding()
5854 QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml"));
5856 QString url = component.url().toString();
5857 QString w1 = url + ":4:25: Unable to assign a function to a property of any type other than var.";
5858 QString w2 = url + ":5:25: Invalid use of Qt.binding() in a binding declaration.";
5859 QString w3 = url + ":6:21: Invalid use of Qt.binding() in a binding declaration.";
5860 QString w4 = url + ":7:15: Invalid use of Qt.binding() in a binding declaration.";
5861 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5862 QTest::ignoreMessage(QtWarningMsg, w2.toLatin1().constData());
5863 QTest::ignoreMessage(QtWarningMsg, w3.toLatin1().constData());
5864 QTest::ignoreMessage(QtWarningMsg, w4.toLatin1().constData());
5866 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5869 QVERIFY(!o->property("a").isValid());
5874 void tst_qqmlecmascript::functionAssignment_fromJS()
5876 QFETCH(QString, triggerProperty);
5878 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5879 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5881 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5883 QVERIFY(!o->property("a").isValid());
5885 o->setProperty("aNumber", QVariant(5));
5886 o->setProperty(triggerProperty.toUtf8().constData(), true);
5887 QCOMPARE(o->property("a"), QVariant(50));
5889 o->setProperty("aNumber", QVariant(10));
5890 QCOMPARE(o->property("a"), QVariant(100));
5895 void tst_qqmlecmascript::functionAssignment_fromJS_data()
5897 QTest::addColumn<QString>("triggerProperty");
5899 QTest::newRow("assign to property") << "assignToProperty";
5900 QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
5902 QTest::newRow("assign to value type") << "assignToValueType";
5904 QTest::newRow("use 'this'") << "assignWithThis";
5905 QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
5908 void tst_qqmlecmascript::functionAssignmentfromJS_invalid()
5910 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5911 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5913 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5915 QVERIFY(!o->property("a").isValid());
5917 o->setProperty("assignFuncWithoutReturn", true);
5918 QVERIFY(!o->property("a").isValid());
5920 QString url = component.url().toString();
5921 QString warning = url + ":67:17: Unable to assign QString to int";
5922 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5923 o->setProperty("assignWrongType", true);
5925 warning = url + ":71:29: Unable to assign QString to int";
5926 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5927 o->setProperty("assignWrongTypeToValueType", true);
5932 void tst_qqmlecmascript::functionAssignment_afterBinding()
5934 QQmlComponent component(&engine, testFileUrl("functionAssignment.3.qml"));
5936 QString url = component.url().toString();
5937 QString w1 = url + ":16: Error: Cannot assign JavaScript function to int";
5938 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5940 QObject *o = component.create();
5942 QCOMPARE(o->property("t1"), QVariant::fromValue<int>(4)); // should have bound
5943 QCOMPARE(o->property("t2"), QVariant::fromValue<int>(2)); // should not have changed
5948 void tst_qqmlecmascript::eval()
5950 QQmlComponent component(&engine, testFileUrl("eval.qml"));
5952 QObject *o = component.create();
5955 QCOMPARE(o->property("test1").toBool(), true);
5956 QCOMPARE(o->property("test2").toBool(), true);
5957 QCOMPARE(o->property("test3").toBool(), true);
5958 QCOMPARE(o->property("test4").toBool(), true);
5959 QCOMPARE(o->property("test5").toBool(), true);
5964 void tst_qqmlecmascript::function()
5966 QQmlComponent component(&engine, testFileUrl("function.qml"));
5968 QObject *o = component.create();
5971 QCOMPARE(o->property("test1").toBool(), true);
5972 QCOMPARE(o->property("test2").toBool(), true);
5973 QCOMPARE(o->property("test3").toBool(), true);
5978 void tst_qqmlecmascript::functionException()
5980 // QTBUG-24037 - shouldn't crash.
5981 QString errstr = testFileUrl("v8functionException.qml").toString() + QLatin1String(":13: SyntaxError: Unexpected token ILLEGAL");
5982 QTest::ignoreMessage(QtWarningMsg, qPrintable(errstr));
5983 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Exception occurred during compilation of function: dynamicSlot()");
5984 QQmlComponent component(&engine, testFileUrl("v8functionException.qml"));
5985 QObject *o = component.create();
5987 QMetaObject::invokeMethod(o, "dynamicSlot");
5991 // Test the "Qt.include" method
5992 void tst_qqmlecmascript::include()
5994 // Non-library relative include
5996 QQmlComponent component(&engine, testFileUrl("include.qml"));
5997 QObject *o = component.create();
6000 QCOMPARE(o->property("test0").toInt(), 99);
6001 QCOMPARE(o->property("test1").toBool(), true);
6002 QCOMPARE(o->property("test2").toBool(), true);
6003 QCOMPARE(o->property("test2_1").toBool(), true);
6004 QCOMPARE(o->property("test3").toBool(), true);
6005 QCOMPARE(o->property("test3_1").toBool(), true);
6010 // Library relative include
6012 QQmlComponent component(&engine, testFileUrl("include_shared.qml"));
6013 QObject *o = component.create();
6016 QCOMPARE(o->property("test0").toInt(), 99);
6017 QCOMPARE(o->property("test1").toBool(), true);
6018 QCOMPARE(o->property("test2").toBool(), true);
6019 QCOMPARE(o->property("test2_1").toBool(), true);
6020 QCOMPARE(o->property("test3").toBool(), true);
6021 QCOMPARE(o->property("test3_1").toBool(), true);
6028 QQmlComponent component(&engine, testFileUrl("include_callback.qml"));
6029 QObject *o = component.create();
6032 QCOMPARE(o->property("test1").toBool(), true);
6033 QCOMPARE(o->property("test2").toBool(), true);
6034 QCOMPARE(o->property("test3").toBool(), true);
6035 QCOMPARE(o->property("test4").toBool(), true);
6036 QCOMPARE(o->property("test5").toBool(), true);
6037 QCOMPARE(o->property("test6").toBool(), true);
6042 // Including file with ".pragma library"
6044 QQmlComponent component(&engine, testFileUrl("include_pragma.qml"));
6045 QObject *o = component.create();
6047 QCOMPARE(o->property("test1").toInt(), 100);
6054 TestHTTPServer server(8111);
6055 QVERIFY(server.isValid());
6056 server.serveDirectory(dataDirectory());
6058 QQmlComponent component(&engine, testFileUrl("include_remote.qml"));
6059 QObject *o = component.create();
6062 QTRY_VERIFY(o->property("done").toBool() == true);
6063 QTRY_VERIFY(o->property("done2").toBool() == true);
6065 QCOMPARE(o->property("test1").toBool(), true);
6066 QCOMPARE(o->property("test2").toBool(), true);
6067 QCOMPARE(o->property("test3").toBool(), true);
6068 QCOMPARE(o->property("test4").toBool(), true);
6069 QCOMPARE(o->property("test5").toBool(), true);
6071 QCOMPARE(o->property("test6").toBool(), true);
6072 QCOMPARE(o->property("test7").toBool(), true);
6073 QCOMPARE(o->property("test8").toBool(), true);
6074 QCOMPARE(o->property("test9").toBool(), true);
6075 QCOMPARE(o->property("test10").toBool(), true);
6082 TestHTTPServer server(8111);
6083 QVERIFY(server.isValid());
6084 server.serveDirectory(dataDirectory());
6086 QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml"));
6087 QObject *o = component.create();
6090 QTRY_VERIFY(o->property("done").toBool() == true);
6092 QCOMPARE(o->property("test1").toBool(), true);
6093 QCOMPARE(o->property("test2").toBool(), true);
6094 QCOMPARE(o->property("test3").toBool(), true);
6100 void tst_qqmlecmascript::signalHandlers()
6102 QQmlComponent component(&engine, testFileUrl("signalHandlers.qml"));
6103 QObject *o = component.create();
6106 QVERIFY(o->property("count").toInt() == 0);
6107 QMetaObject::invokeMethod(o, "testSignalCall");
6108 QCOMPARE(o->property("count").toInt(), 1);
6110 QMetaObject::invokeMethod(o, "testSignalHandlerCall");
6111 QCOMPARE(o->property("count").toInt(), 1);
6112 QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
6114 QVERIFY(o->property("funcCount").toInt() == 0);
6115 QMetaObject::invokeMethod(o, "testSignalConnection");
6116 QCOMPARE(o->property("funcCount").toInt(), 1);
6118 QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
6119 QCOMPARE(o->property("funcCount").toInt(), 2);
6121 QMetaObject::invokeMethod(o, "testSignalDefined");
6122 QCOMPARE(o->property("definedResult").toBool(), true);
6124 QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
6125 QCOMPARE(o->property("definedHandlerResult").toBool(), true);
6130 void tst_qqmlecmascript::qtbug_10696()
6132 QQmlComponent component(&engine, testFileUrl("qtbug_10696.qml"));
6133 QObject *o = component.create();
6138 void tst_qqmlecmascript::qtbug_11606()
6140 QQmlComponent component(&engine, testFileUrl("qtbug_11606.qml"));
6141 QObject *o = component.create();
6143 QCOMPARE(o->property("test").toBool(), true);
6147 void tst_qqmlecmascript::qtbug_11600()
6149 QQmlComponent component(&engine, testFileUrl("qtbug_11600.qml"));
6150 QObject *o = component.create();
6152 QCOMPARE(o->property("test").toBool(), true);
6156 void tst_qqmlecmascript::qtbug_21864()
6158 QQmlComponent component(&engine, testFileUrl("qtbug_21864.qml"));
6159 QObject *o = component.create();
6161 QCOMPARE(o->property("test").toBool(), true);
6165 void tst_qqmlecmascript::rewriteMultiLineStrings()
6169 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml"));
6170 QObject *o = component.create();
6172 QTRY_COMPARE(o->property("test").toBool(), true);
6177 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings_crlf.1.qml"));
6178 QObject *o = component.create();
6184 void tst_qqmlecmascript::qobjectConnectionListExceptionHandling()
6187 QQmlComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml"));
6188 QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined");
6189 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6190 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6191 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6192 QObject *o = component.create();
6194 QCOMPARE(o->property("test").toBool(), true);
6198 // Reading and writing non-scriptable properties should fail
6199 void tst_qqmlecmascript::nonscriptable()
6201 QQmlComponent component(&engine, testFileUrl("nonscriptable.qml"));
6202 QObject *o = component.create();
6204 QCOMPARE(o->property("readOk").toBool(), true);
6205 QCOMPARE(o->property("writeOk").toBool(), true);
6209 // deleteLater() should not be callable from QML
6210 void tst_qqmlecmascript::deleteLater()
6212 QQmlComponent component(&engine, testFileUrl("deleteLater.qml"));
6213 QObject *o = component.create();
6215 QCOMPARE(o->property("test").toBool(), true);
6219 // objectNameChanged() should be usable from QML
6220 void tst_qqmlecmascript::objectNameChangedSignal()
6222 QQmlComponent component(&engine, testFileUrl("objectNameChangedSignal.qml"));
6223 QObject *o = component.create();
6225 QCOMPARE(o->property("test").toBool(), false);
6226 o->setObjectName("obj");
6227 QCOMPARE(o->property("test").toBool(), true);
6231 // destroyed() should not be usable from QML
6232 void tst_qqmlecmascript::destroyedSignal()
6234 QQmlComponent component(&engine, testFileUrl("destroyedSignal.qml"));
6235 QVERIFY(component.isError());
6237 QString expectedErrorString = component.url().toString() + QLatin1String(":5:5: Cannot assign to non-existent property \"onDestroyed\"");
6238 QCOMPARE(component.errors().at(0).toString(), expectedErrorString);
6241 void tst_qqmlecmascript::in()
6243 QQmlComponent component(&engine, testFileUrl("in.qml"));
6244 QObject *o = component.create();
6246 QCOMPARE(o->property("test1").toBool(), true);
6247 QCOMPARE(o->property("test2").toBool(), true);
6251 void tst_qqmlecmascript::typeOf()
6253 QQmlComponent component(&engine, testFileUrl("typeOf.qml"));
6255 QObject *o = component.create();
6258 QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
6259 QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
6260 QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
6261 QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
6262 QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
6263 QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
6264 QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
6265 QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
6266 QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
6271 void tst_qqmlecmascript::qtbug_24448()
6273 QQmlComponent component(&engine, testFileUrl("qtbug_24448.qml"));
6274 QScopedPointer<QObject> o(component.create());
6276 QVERIFY(o->property("test").toBool());
6279 void tst_qqmlecmascript::sharedAttachedObject()
6281 QQmlComponent component(&engine, testFileUrl("sharedAttachedObject.qml"));
6282 QObject *o = component.create();
6284 QCOMPARE(o->property("test1").toBool(), true);
6285 QCOMPARE(o->property("test2").toBool(), true);
6290 void tst_qqmlecmascript::objectName()
6292 QQmlComponent component(&engine, testFileUrl("objectName.qml"));
6293 QObject *o = component.create();
6296 QCOMPARE(o->property("test1").toString(), QString("hello"));
6297 QCOMPARE(o->property("test2").toString(), QString("ell"));
6299 o->setObjectName("world");
6301 QCOMPARE(o->property("test1").toString(), QString("world"));
6302 QCOMPARE(o->property("test2").toString(), QString("orl"));
6307 void tst_qqmlecmascript::writeRemovesBinding()
6309 QQmlComponent component(&engine, testFileUrl("writeRemovesBinding.qml"));
6310 QObject *o = component.create();
6313 QCOMPARE(o->property("test").toBool(), true);
6318 // Test bindings assigned to alias properties actually assign to the alias' target
6319 void tst_qqmlecmascript::aliasBindingsAssignCorrectly()
6321 QQmlComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml"));
6322 QObject *o = component.create();
6325 QCOMPARE(o->property("test").toBool(), true);
6330 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
6331 void tst_qqmlecmascript::aliasBindingsOverrideTarget()
6334 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml"));
6335 QObject *o = component.create();
6338 QCOMPARE(o->property("test").toBool(), true);
6344 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml"));
6345 QObject *o = component.create();
6348 QCOMPARE(o->property("test").toBool(), true);
6354 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml"));
6355 QObject *o = component.create();
6358 QCOMPARE(o->property("test").toBool(), true);
6364 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
6365 void tst_qqmlecmascript::aliasWritesOverrideBindings()
6368 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml"));
6369 QObject *o = component.create();
6372 QCOMPARE(o->property("test").toBool(), true);
6378 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml"));
6379 QObject *o = component.create();
6382 QCOMPARE(o->property("test").toBool(), true);
6388 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml"));
6389 QObject *o = component.create();
6392 QCOMPARE(o->property("test").toBool(), true);
6398 // Allow an alais to a composite element
6400 void tst_qqmlecmascript::aliasToCompositeElement()
6402 QQmlComponent component(&engine, testFileUrl("aliasToCompositeElement.qml"));
6404 QObject *object = component.create();
6405 QVERIFY(object != 0);
6410 void tst_qqmlecmascript::qtbug_20344()
6412 QQmlComponent component(&engine, testFileUrl("qtbug_20344.qml"));
6414 QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
6415 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6417 QObject *object = component.create();
6418 QVERIFY(object != 0);
6423 void tst_qqmlecmascript::revisionErrors()
6426 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml"));
6427 QString url = component.url().toString();
6429 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6430 QString warning2 = url + ":11: ReferenceError: prop2 is not defined";
6431 QString warning3 = url + ":13: ReferenceError: method2 is not defined";
6433 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6434 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6435 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6436 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6437 QVERIFY(object != 0);
6441 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml"));
6442 QString url = component.url().toString();
6444 // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
6445 // method2, prop2 from MyRevisionedClass not available
6446 // method4, prop4 from MyRevisionedSubclass not available
6447 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6448 QString warning2 = url + ":14: ReferenceError: prop2 is not defined";
6449 QString warning3 = url + ":10: ReferenceError: prop4 is not defined";
6450 QString warning4 = url + ":16: ReferenceError: prop4 is not defined";
6451 QString warning5 = url + ":20: ReferenceError: method2 is not defined";
6453 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6454 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6455 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6456 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
6457 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
6458 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6459 QVERIFY(object != 0);
6463 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml"));
6464 QString url = component.url().toString();
6466 // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
6467 // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
6468 QString warning1 = url + ":30: ReferenceError: methodD is not defined";
6469 QString warning2 = url + ":10: ReferenceError: propD is not defined";
6470 QString warning3 = url + ":20: ReferenceError: propD is not defined";
6471 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6472 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6473 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6474 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6475 QVERIFY(object != 0);
6480 void tst_qqmlecmascript::revision()
6483 QQmlComponent component(&engine, testFileUrl("metaobjectRevision.qml"));
6484 QString url = component.url().toString();
6486 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6487 QVERIFY(object != 0);
6491 QQmlComponent component(&engine, testFileUrl("metaobjectRevision2.qml"));
6492 QString url = component.url().toString();
6494 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6495 QVERIFY(object != 0);
6499 QQmlComponent component(&engine, testFileUrl("metaobjectRevision3.qml"));
6500 QString url = component.url().toString();
6502 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6503 QVERIFY(object != 0);
6506 // Test that non-root classes can resolve revisioned methods
6508 QQmlComponent component(&engine, testFileUrl("metaobjectRevision4.qml"));
6510 QObject *object = component.create();
6511 QVERIFY(object != 0);
6512 QCOMPARE(object->property("test").toReal(), 11.);
6517 void tst_qqmlecmascript::realToInt()
6519 QQmlComponent component(&engine, testFileUrl("realToInt.qml"));
6520 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6521 QVERIFY(object != 0);
6523 QMetaObject::invokeMethod(object, "test1");
6524 QCOMPARE(object->value(), int(4));
6525 QMetaObject::invokeMethod(object, "test2");
6526 QCOMPARE(object->value(), int(8));
6529 void tst_qqmlecmascript::urlProperty()
6532 QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml"));
6533 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6534 QVERIFY(object != 0);
6535 object->setStringProperty("http://qt-project.org");
6536 QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
6537 QCOMPARE(object->intProperty(), 123);
6538 QCOMPARE(object->value(), 1);
6539 QCOMPARE(object->property("result").toBool(), true);
6543 void tst_qqmlecmascript::urlPropertyWithEncoding()
6546 QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml"));
6547 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6548 QVERIFY(object != 0);
6549 object->setStringProperty("http://qt-project.org");
6551 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6552 QCOMPARE(object->urlProperty(), encoded);
6553 QCOMPARE(object->value(), 0); // Interpreting URL as string yields canonicalised version
6554 QCOMPARE(object->property("result").toBool(), true);
6558 void tst_qqmlecmascript::urlListPropertyWithEncoding()
6561 QQmlComponent component(&engine, testFileUrl("urlListProperty.qml"));
6562 QObject *object = component.create();
6563 QVERIFY(object != 0);
6564 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
6565 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
6566 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
6567 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
6568 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0);
6570 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6571 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded));
6572 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded));
6573 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6574 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6579 void tst_qqmlecmascript::dynamicString()
6581 QQmlComponent component(&engine, testFileUrl("dynamicString.qml"));
6582 QObject *object = component.create();
6583 QVERIFY(object != 0);
6584 QCOMPARE(object->property("stringProperty").toString(),
6585 QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
6588 void tst_qqmlecmascript::deleteLaterObjectMethodCall()
6590 QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml"));
6591 QObject *object = component.create();
6592 QVERIFY(object != 0);
6595 void tst_qqmlecmascript::automaticSemicolon()
6597 QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml"));
6598 QObject *object = component.create();
6599 QVERIFY(object != 0);
6602 void tst_qqmlecmascript::unaryExpression()
6604 QQmlComponent component(&engine, testFileUrl("unaryExpression.qml"));
6605 QObject *object = component.create();
6606 QVERIFY(object != 0);
6609 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
6610 void tst_qqmlecmascript::doubleEvaluate()
6612 QQmlComponent component(&engine, testFileUrl("doubleEvaluate.qml"));
6613 QObject *object = component.create();
6614 QVERIFY(object != 0);
6615 WriteCounter *wc = qobject_cast<WriteCounter *>(object);
6617 QCOMPARE(wc->count(), 1);
6619 wc->setProperty("x", 9);
6621 QCOMPARE(wc->count(), 2);
6626 static QStringList messages;
6627 static void captureMsgHandler(QtMsgType, const char *msg)
6629 messages.append(QLatin1String(msg));
6632 void tst_qqmlecmascript::nonNotifyable()
6634 QV4Compiler::enableV4(false);
6635 QQmlComponent component(&engine, testFileUrl("nonNotifyable.qml"));
6636 QV4Compiler::enableV4(true);
6638 QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
6640 QObject *object = component.create();
6641 qInstallMsgHandler(old);
6643 QVERIFY(object != 0);
6645 QString expected1 = QLatin1String("QQmlExpression: Expression ") +
6646 component.url().toString() +
6647 QLatin1String(":5 depends on non-NOTIFYable properties:");
6648 QString expected2 = QLatin1String(" ") +
6649 QLatin1String(object->metaObject()->className()) +
6650 QLatin1String("::value");
6652 QCOMPARE(messages.length(), 2);
6653 QCOMPARE(messages.at(0), expected1);
6654 QCOMPARE(messages.at(1), expected2);
6659 void tst_qqmlecmascript::forInLoop()
6661 QQmlComponent component(&engine, testFileUrl("forInLoop.qml"));
6662 QObject *object = component.create();
6663 QVERIFY(object != 0);
6665 QMetaObject::invokeMethod(object, "listProperty");
6667 QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
6668 QCOMPARE(r.size(), 3);
6669 QCOMPARE(r[0],QLatin1String("0=obj1"));
6670 QCOMPARE(r[1],QLatin1String("1=obj2"));
6671 QCOMPARE(r[2],QLatin1String("2=obj3"));
6673 //TODO: should test for in loop for other objects (such as QObjects) as well.
6678 // An object the binding depends on is deleted while the binding is still running
6679 void tst_qqmlecmascript::deleteWhileBindingRunning()
6681 QQmlComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml"));
6682 QObject *object = component.create();
6683 QVERIFY(object != 0);
6687 void tst_qqmlecmascript::qtbug_22679()
6690 object.setStringProperty(QLatin1String("Please work correctly"));
6691 engine.rootContext()->setContextProperty("contextProp", &object);
6693 QQmlComponent component(&engine, testFileUrl("qtbug_22679.qml"));
6694 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6695 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6697 QObject *o = component.create();
6699 QCOMPARE(warningsSpy.count(), 0);
6703 void tst_qqmlecmascript::qtbug_22843_data()
6705 QTest::addColumn<bool>("library");
6707 QTest::newRow("without .pragma library") << false;
6708 QTest::newRow("with .pragma library") << true;
6711 void tst_qqmlecmascript::qtbug_22843()
6713 QFETCH(bool, library);
6715 QString fileName("qtbug_22843");
6717 fileName += QLatin1String(".library");
6718 fileName += QLatin1String(".qml");
6720 QQmlComponent component(&engine, testFileUrl(fileName));
6721 QString url = component.url().toString();
6722 QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )");
6723 QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'");
6725 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6726 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6727 for (int x = 0; x < 3; ++x) {
6728 warningsSpy.clear();
6729 // For libraries, only the first import attempt should produce a
6730 // SyntaxError warning; subsequent component creation should not
6731 // attempt to reload the script.
6732 bool expectSyntaxError = !library || (x == 0);
6733 if (expectSyntaxError)
6734 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
6735 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
6736 QObject *object = component.create();
6737 QVERIFY(object != 0);
6738 QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
6744 void tst_qqmlecmascript::switchStatement()
6747 QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml"));
6748 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6749 QVERIFY(object != 0);
6751 // `object->value()' is the number of executed statements
6753 object->setStringProperty("A");
6754 QCOMPARE(object->value(), 5);
6756 object->setStringProperty("S");
6757 QCOMPARE(object->value(), 3);
6759 object->setStringProperty("D");
6760 QCOMPARE(object->value(), 3);
6762 object->setStringProperty("F");
6763 QCOMPARE(object->value(), 4);
6765 object->setStringProperty("something else");
6766 QCOMPARE(object->value(), 1);
6770 QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml"));
6771 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6772 QVERIFY(object != 0);
6774 // `object->value()' is the number of executed statements
6776 object->setStringProperty("A");
6777 QCOMPARE(object->value(), 5);
6779 object->setStringProperty("S");
6780 QCOMPARE(object->value(), 3);
6782 object->setStringProperty("D");
6783 QCOMPARE(object->value(), 3);
6785 object->setStringProperty("F");
6786 QCOMPARE(object->value(), 3);
6788 object->setStringProperty("something else");
6789 QCOMPARE(object->value(), 4);
6793 QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml"));
6794 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6795 QVERIFY(object != 0);
6797 // `object->value()' is the number of executed statements
6799 object->setStringProperty("A");
6800 QCOMPARE(object->value(), 5);
6802 object->setStringProperty("S");
6803 QCOMPARE(object->value(), 3);
6805 object->setStringProperty("D");
6806 QCOMPARE(object->value(), 3);
6808 object->setStringProperty("F");
6809 QCOMPARE(object->value(), 3);
6811 object->setStringProperty("something else");
6812 QCOMPARE(object->value(), 6);
6816 QQmlComponent component(&engine, testFileUrl("switchStatement.4.qml"));
6818 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
6819 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6821 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6822 QVERIFY(object != 0);
6824 // `object->value()' is the number of executed statements
6826 object->setStringProperty("A");
6827 QCOMPARE(object->value(), 5);
6829 object->setStringProperty("S");
6830 QCOMPARE(object->value(), 3);
6832 object->setStringProperty("D");
6833 QCOMPARE(object->value(), 3);
6835 object->setStringProperty("F");
6836 QCOMPARE(object->value(), 3);
6838 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6840 object->setStringProperty("something else");
6844 QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml"));
6845 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6846 QVERIFY(object != 0);
6848 // `object->value()' is the number of executed statements
6850 object->setStringProperty("A");
6851 QCOMPARE(object->value(), 1);
6853 object->setStringProperty("S");
6854 QCOMPARE(object->value(), 1);
6856 object->setStringProperty("D");
6857 QCOMPARE(object->value(), 1);
6859 object->setStringProperty("F");
6860 QCOMPARE(object->value(), 1);
6862 object->setStringProperty("something else");
6863 QCOMPARE(object->value(), 1);
6867 QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml"));
6868 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6869 QVERIFY(object != 0);
6871 // `object->value()' is the number of executed statements
6873 object->setStringProperty("A");
6874 QCOMPARE(object->value(), 123);
6876 object->setStringProperty("S");
6877 QCOMPARE(object->value(), 123);
6879 object->setStringProperty("D");
6880 QCOMPARE(object->value(), 321);
6882 object->setStringProperty("F");
6883 QCOMPARE(object->value(), 321);
6885 object->setStringProperty("something else");
6886 QCOMPARE(object->value(), 0);
6890 void tst_qqmlecmascript::withStatement()
6893 QQmlComponent component(&engine, testFileUrl("withStatement.1.qml"));
6894 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6895 QVERIFY(object != 0);
6897 QCOMPARE(object->value(), 123);
6901 void tst_qqmlecmascript::tryStatement()
6904 QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml"));
6905 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6906 QVERIFY(object != 0);
6908 QCOMPARE(object->value(), 123);
6912 QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml"));
6913 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6914 QVERIFY(object != 0);
6916 QCOMPARE(object->value(), 321);
6920 QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml"));
6921 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6922 QVERIFY(object != 0);
6924 QCOMPARE(object->value(), 1);
6928 QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml"));
6929 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6930 QVERIFY(object != 0);
6932 QCOMPARE(object->value(), 1);
6936 class CppInvokableWithQObjectDerived : public QObject
6940 CppInvokableWithQObjectDerived() {}
6941 ~CppInvokableWithQObjectDerived() {}
6943 Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data)
6945 MyQmlObject *obj = new MyQmlObject();
6946 obj->setStringProperty(data);
6950 Q_INVOKABLE QString getStringProperty(MyQmlObject *obj)
6952 return obj->stringProperty();
6956 void tst_qqmlecmascript::invokableWithQObjectDerived()
6958 CppInvokableWithQObjectDerived invokable;
6962 engine.rootContext()->setContextProperty("invokable", &invokable);
6964 QQmlComponent component(&engine, testFileUrl("qobjectDerivedArgument.qml"));
6966 QObject *object = component.create();
6968 QVERIFY(object != 0);
6969 QVERIFY(object->property("result").value<bool>() == true);
6975 void tst_qqmlecmascript::realTypePrecision()
6977 // Properties and signal parameters of type real should have double precision.
6978 QQmlComponent component(&engine, testFileUrl("realTypePrecision.qml"));
6979 QScopedPointer<QObject> object(component.create());
6980 QVERIFY(object != 0);
6981 QCOMPARE(object->property("test").toDouble(), 1234567890.);
6982 QCOMPARE(object->property("test2").toDouble(), 1234567890.);
6983 QCOMPARE(object->property("test3").toDouble(), 1234567890.);
6984 QCOMPARE(object->property("test4").toDouble(), 1234567890.);
6985 QCOMPARE(object->property("test5").toDouble(), 1234567890.);
6986 QCOMPARE(object->property("test6").toDouble(), 1234567890.*2);
6989 void tst_qqmlecmascript::registeredFlagMethod()
6992 QQmlComponent component(&engine, testFileUrl("registeredFlagMethod.qml"));
6993 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6994 QVERIFY(object != 0);
6996 QCOMPARE(object->buttons(), 0);
6997 emit object->basicSignal();
6998 QCOMPARE(object->buttons(), Qt::RightButton);
7004 void tst_qqmlecmascript::replaceBinding()
7007 QQmlComponent c(&engine, testFileUrl("replaceBinding.qml"));
7008 QObject *obj = c.create();
7011 QVERIFY(obj->property("success").toBool());
7015 void tst_qqmlecmascript::deleteRootObjectInCreation()
7019 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.qml"));
7020 QObject *obj = c.create();
7022 QVERIFY(obj->property("rootIndestructible").toBool());
7023 QVERIFY(!obj->property("childDestructible").toBool());
7025 QVERIFY(obj->property("childDestructible").toBool());
7030 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.2.qml"));
7031 QObject *object = c.create();
7032 QVERIFY(object != 0);
7033 QVERIFY(object->property("testConditionsMet").toBool());
7038 void tst_qqmlecmascript::onDestruction()
7041 // Delete object manually to invoke the associated handlers,
7042 // prior to engine destruction.
7044 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
7045 QObject *obj = c.create();
7051 // In this case, the teardown of the engine causes deletion
7052 // of contexts and child items. This triggers the
7053 // onDestruction handler of a (previously .destroy()ed)
7054 // component instance. This shouldn't crash.
7056 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
7057 QObject *obj = c.create();
7062 struct EventProcessor : public QObject
7066 Q_INVOKABLE void process()
7068 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
7069 QCoreApplication::processEvents();
7073 void tst_qqmlecmascript::bindingSuppression()
7076 EventProcessor processor;
7077 engine.rootContext()->setContextProperty("pendingEvents", &processor);
7079 transientErrorsMsgCount = 0;
7080 QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
7082 QQmlComponent c(&engine, testFileUrl("bindingSuppression.qml"));
7083 QObject *obj = c.create();
7087 qInstallMsgHandler(old);
7088 QCOMPARE(transientErrorsMsgCount, 0);
7091 void tst_qqmlecmascript::signalEmitted()
7094 // calling destroy on the sibling.
7096 QQmlComponent c(&engine, testFileUrl("signalEmitted.2.qml"));
7097 QObject *obj = c.create();
7099 QTRY_VERIFY(obj->property("success").toBool());
7104 // allowing gc to clean up the sibling.
7106 QQmlComponent c(&engine, testFileUrl("signalEmitted.3.qml"));
7107 QObject *obj = c.create();
7109 gc(engine); // should collect c1.
7110 QTRY_VERIFY(obj->property("success").toBool());
7115 // allowing gc to clean up the sibling after manually destroying target.
7117 QQmlComponent c(&engine, testFileUrl("signalEmitted.4.qml"));
7118 QObject *obj = c.create();
7120 gc(engine); // should collect c1.
7121 QMetaObject::invokeMethod(obj, "destroyC2");
7122 QTRY_VERIFY(obj->property("success").toBool()); // handles events (incl. delete later).
7128 void tst_qqmlecmascript::threadSignal()
7131 QQmlComponent c(&engine, testFileUrl("threadSignal.qml"));
7132 QObject *object = c.create();
7133 QVERIFY(object != 0);
7134 QTRY_VERIFY(object->property("passed").toBool());
7138 QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml"));
7139 QObject *object = c.create();
7140 QVERIFY(object != 0);
7141 QSignalSpy doneSpy(object, SIGNAL(done(const QString &)));
7142 QMetaObject::invokeMethod(object, "doIt");
7143 QTRY_VERIFY(object->property("passed").toBool());
7144 QCOMPARE(doneSpy.count(), 1);
7149 // ensure that the qqmldata::destroyed() handler doesn't cause problems
7150 void tst_qqmlecmascript::qqmldataDestroyed()
7152 // gc cleans up a qobject, later the qqmldata destroyed handler will run.
7154 QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.qml"));
7155 QObject *object = c.create();
7156 QVERIFY(object != 0);
7157 // now gc causing the collection of the dynamically constructed object.
7158 engine.collectGarbage();
7159 engine.collectGarbage();
7160 // now process events to allow deletion (calling qqmldata::destroyed())
7161 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
7162 QCoreApplication::processEvents();
7167 // in this case, the object has CPP ownership, and the gc will
7168 // be triggered during its beginCreate stage.
7170 QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.2.qml"));
7171 QObject *object = c.create();
7172 QVERIFY(object != 0);
7173 QVERIFY(object->property("testConditionsMet").toBool());
7174 // the gc() within the handler will have triggered the weak
7175 // qobject reference callback. If that incorrectly disposes
7176 // the handle, when the qqmldata::destroyed() handler is
7177 // called due to object deletion we will see a crash.
7179 // shouldn't have crashed.
7183 void tst_qqmlecmascript::secondAlias()
7185 QQmlComponent c(&engine, testFileUrl("secondAlias.qml"));
7186 QObject *object = c.create();
7187 QVERIFY(object != 0);
7188 QCOMPARE(object->property("test").toInt(), 200);
7192 // An alias to a var property works
7193 void tst_qqmlecmascript::varAlias()
7195 QQmlComponent c(&engine, testFileUrl("varAlias.qml"));
7196 QObject *object = c.create();
7197 QVERIFY(object != 0);
7198 QCOMPARE(object->property("test").toInt(), 192);
7202 // Used to trigger an assert in the lazy meta object creation stage
7203 void tst_qqmlecmascript::overrideDataAssert()
7205 QQmlComponent c(&engine, testFileUrl("overrideDataAssert.qml"));
7206 QObject *object = c.create();
7207 QVERIFY(object != 0);
7208 object->metaObject();
7212 void tst_qqmlecmascript::fallbackBindings_data()
7214 QTest::addColumn<QString>("source");
7216 QTest::newRow("Property without fallback") << "fallbackBindings.1.qml";
7217 QTest::newRow("Property fallback") << "fallbackBindings.2.qml";
7218 QTest::newRow("SingletonType without fallback") << "fallbackBindings.3.qml";
7219 QTest::newRow("SingletonType fallback") << "fallbackBindings.4.qml";
7220 QTest::newRow("Attached without fallback") << "fallbackBindings.5.qml";
7221 QTest::newRow("Attached fallback") << "fallbackBindings.6.qml";
7222 QTest::newRow("Subproperty without fallback") << "fallbackBindings.7.qml";
7223 QTest::newRow("Subproperty fallback") << "fallbackBindings.8.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::propertyOverride()
7239 QQmlComponent component(&engine, testFileUrl("propertyOverride.qml"));
7240 QScopedPointer<QObject> object(component.create());
7241 QVERIFY(object != 0);
7243 QCOMPARE(object->property("success").toBool(), true);
7246 void tst_qqmlecmascript::sequenceSort_data()
7248 QTest::addColumn<QString>("function");
7249 QTest::addColumn<bool>("useComparer");
7251 QTest::newRow("qtbug_25269") << "test_qtbug_25269" << false;
7253 const char *types[] = { "alphabet", "numbers", "reals" };
7254 const char *sort[] = { "insertionSort", "quickSort" };
7256 for (size_t t=0 ; t < sizeof(types)/sizeof(types[0]) ; ++t) {
7257 for (size_t s=0 ; s < sizeof(sort)/sizeof(sort[0]) ; ++s) {
7258 for (int c=0 ; c < 2 ; ++c) {
7259 QString testName = QLatin1String(types[t]) + QLatin1String("_") + QLatin1String(sort[s]);
7260 QString fnName = QLatin1String("test_") + testName;
7261 bool useComparer = c != 0;
7262 testName += useComparer ? QLatin1String("[custom]") : QLatin1String("[default]");
7263 QTest::newRow(testName.toAscii().constData()) << fnName << useComparer;
7269 void tst_qqmlecmascript::sequenceSort()
7271 QFETCH(QString, function);
7272 QFETCH(bool, useComparer);
7274 QQmlComponent component(&engine, testFileUrl("sequenceSort.qml"));
7276 QObject *object = component.create();
7278 qDebug() << component.errorString();
7279 QVERIFY(object != 0);
7282 QMetaObject::invokeMethod(object, function.toAscii().constData(), Q_RETURN_ARG(QVariant, q), Q_ARG(QVariant, useComparer));
7283 QVERIFY(q.toBool() == true);
7288 void tst_qqmlecmascript::concatenatedStringPropertyAccess()
7290 QQmlComponent component(&engine, testFileUrl("concatenatedStringPropertyAccess.qml"));
7291 QObject *object = component.create();
7293 QVERIFY(object->property("success").toBool());
7297 QTEST_MAIN(tst_qqmlecmascript)
7299 #include "tst_qqmlecmascript.moc"