1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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();
286 void jsOwnedObjectsDeletedOnEngineDestroy();
289 static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
293 void tst_qqmlecmascript::initTestCase()
295 QQmlDataTest::initTestCase();
298 QString dataDir(dataDirectory() + QLatin1Char('/') + QLatin1String("lib"));
299 engine.addImportPath(dataDir);
302 void tst_qqmlecmascript::assignBasicTypes()
305 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.qml"));
306 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
307 QVERIFY(object != 0);
308 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
309 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
310 QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue);
311 QCOMPARE(object->stringProperty(), QString("Hello World!"));
312 QCOMPARE(object->uintProperty(), uint(10));
313 QCOMPARE(object->intProperty(), -19);
314 QCOMPARE((float)object->realProperty(), float(23.2));
315 QCOMPARE((float)object->doubleProperty(), float(-19.75));
316 QCOMPARE((float)object->floatProperty(), float(8.5));
317 QCOMPARE(object->colorProperty(), QColor("red"));
318 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
319 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
320 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
321 QCOMPARE(object->pointProperty(), QPoint(99,13));
322 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
323 QCOMPARE(object->sizeProperty(), QSize(99, 13));
324 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
325 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
326 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
327 QCOMPARE(object->boolProperty(), true);
328 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
329 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
330 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
334 QQmlComponent component(&engine, testFileUrl("assignBasicTypes.2.qml"));
335 MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
336 QVERIFY(object != 0);
337 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
338 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
339 QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue);
340 QCOMPARE(object->stringProperty(), QString("Hello World!"));
341 QCOMPARE(object->uintProperty(), uint(10));
342 QCOMPARE(object->intProperty(), -19);
343 QCOMPARE((float)object->realProperty(), float(23.2));
344 QCOMPARE((float)object->doubleProperty(), float(-19.75));
345 QCOMPARE((float)object->floatProperty(), float(8.5));
346 QCOMPARE(object->colorProperty(), QColor("red"));
347 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
348 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
349 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
350 QCOMPARE(object->pointProperty(), QPoint(99,13));
351 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
352 QCOMPARE(object->sizeProperty(), QSize(99, 13));
353 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
354 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
355 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
356 QCOMPARE(object->boolProperty(), true);
357 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
358 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
359 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
364 void tst_qqmlecmascript::assignDate_data()
366 QTest::addColumn<QUrl>("source");
368 QTest::newRow("Component.onComplete JS Parse") << testFileUrl("assignDate.qml");
369 QTest::newRow("Component.onComplete JS") << testFileUrl("assignDate.1.qml");
370 QTest::newRow("Binding JS") << testFileUrl("assignDate.2.qml");
371 QTest::newRow("Binding UTC") << testFileUrl("assignDate.3.qml");
372 QTest::newRow("Binding JS UTC") << testFileUrl("assignDate.4.qml");
373 QTest::newRow("Binding UTC+2") << testFileUrl("assignDate.5.qml");
374 QTest::newRow("Binding JS UTC+2 ") << testFileUrl("assignDate.6.qml");
377 void tst_qqmlecmascript::assignDate()
379 QFETCH(QUrl, source);
381 QQmlComponent component(&engine, source);
382 QScopedPointer<QObject> obj(component.create());
383 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
384 QVERIFY(object != 0);
386 // Dates received from JS are automatically converted to local time
387 QDate expectedDate(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 0), Qt::UTC).toLocalTime().date());
388 QDateTime expectedDateTime(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 1), Qt::UTC).toLocalTime());
389 QDateTime expectedDateTime2(QDateTime(QDate(2009, 5, 12), QTime(23, 59, 59), Qt::UTC).toLocalTime());
391 QCOMPARE(object->dateProperty(), expectedDate);
392 QCOMPARE(object->dateTimeProperty(), expectedDateTime);
393 QCOMPARE(object->dateTimeProperty2(), expectedDateTime2);
394 QCOMPARE(object->boolProperty(), true);
397 void tst_qqmlecmascript::exportDate_data()
399 QTest::addColumn<QUrl>("source");
400 QTest::addColumn<QDateTime>("datetime");
402 // Verify that we can export datetime information to QML and that consumers can access
403 // the data correctly provided they know the TZ info associated with the value
405 const QDate date(2009, 5, 12);
406 const QTime early(0, 0, 1);
407 const QTime late(23, 59, 59);
408 const int offset(((11 * 60) + 30) * 60);
410 QTest::newRow("Localtime early") << testFileUrl("exportDate.qml") << QDateTime(date, early, Qt::LocalTime);
411 QTest::newRow("Localtime late") << testFileUrl("exportDate.2.qml") << QDateTime(date, late, Qt::LocalTime);
412 QTest::newRow("UTC early") << testFileUrl("exportDate.3.qml") << QDateTime(date, early, Qt::UTC);
413 QTest::newRow("UTC late") << testFileUrl("exportDate.4.qml") << QDateTime(date, late, Qt::UTC);
415 QDateTime dt(date, early, Qt::OffsetFromUTC);
416 dt.setUtcOffset(offset);
417 QTest::newRow("+11:30 early") << testFileUrl("exportDate.5.qml") << dt;
420 QDateTime dt(date, late, Qt::OffsetFromUTC);
421 dt.setUtcOffset(offset);
422 QTest::newRow("+11:30 late") << testFileUrl("exportDate.6.qml") << dt;
425 QDateTime dt(date, early, Qt::OffsetFromUTC);
426 dt.setUtcOffset(-offset);
427 QTest::newRow("-11:30 early") << testFileUrl("exportDate.7.qml") << dt;
430 QDateTime dt(date, late, Qt::OffsetFromUTC);
431 dt.setUtcOffset(-offset);
432 QTest::newRow("-11:30 late") << testFileUrl("exportDate.8.qml") << dt;
436 void tst_qqmlecmascript::exportDate()
438 QFETCH(QUrl, source);
439 QFETCH(QDateTime, datetime);
441 DateTimeExporter exporter(datetime);
444 e.rootContext()->setContextProperty("datetimeExporter", &exporter);
446 QQmlComponent component(&e, source);
447 QScopedPointer<QObject> obj(component.create());
448 MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
449 QVERIFY(object != 0);
450 QCOMPARE(object->boolProperty(), true);
453 void tst_qqmlecmascript::idShortcutInvalidates()
456 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.qml"));
457 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
458 QVERIFY(object != 0);
459 QVERIFY(object->objectProperty() != 0);
460 delete object->objectProperty();
461 QVERIFY(object->objectProperty() == 0);
466 QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.1.qml"));
467 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
468 QVERIFY(object != 0);
469 QVERIFY(object->objectProperty() != 0);
470 delete object->objectProperty();
471 QVERIFY(object->objectProperty() == 0);
476 void tst_qqmlecmascript::boolPropertiesEvaluateAsBool()
479 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.1.qml"));
480 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
481 QVERIFY(object != 0);
482 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
486 QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.2.qml"));
487 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
488 QVERIFY(object != 0);
489 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
494 void tst_qqmlecmascript::signalAssignment()
497 QQmlComponent component(&engine, testFileUrl("signalAssignment.1.qml"));
498 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
499 QVERIFY(object != 0);
500 QCOMPARE(object->string(), QString());
501 emit object->basicSignal();
502 QCOMPARE(object->string(), QString("pass"));
507 QQmlComponent component(&engine, testFileUrl("signalAssignment.2.qml"));
508 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
509 QVERIFY(object != 0);
510 QCOMPARE(object->string(), QString());
511 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
512 QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
517 QQmlComponent component(&engine, testFileUrl("signalAssignment.3.qml"));
518 QVERIFY(component.isError());
519 QString expectedErrorString = component.url().toString() + QLatin1String(":4 Signal uses unnamed parameter followed by named parameter.\n");
520 QCOMPARE(component.errorString(), expectedErrorString);
524 QQmlComponent component(&engine, testFileUrl("signalAssignment.4.qml"));
525 QVERIFY(component.isError());
526 QString expectedErrorString = component.url().toString() + QLatin1String(":5 Signal parameter \"parseInt\" hides global variable.\n");
527 QCOMPARE(component.errorString(), expectedErrorString);
531 void tst_qqmlecmascript::signalArguments()
534 QQmlComponent component(&engine, testFileUrl("signalArguments.1.qml"));
535 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
536 QVERIFY(object != 0);
537 QCOMPARE(object->string(), QString());
538 emit object->basicSignal();
539 QCOMPARE(object->string(), QString("pass"));
540 QCOMPARE(object->property("argumentCount").toInt(), 0);
541 QCOMPARE(object->property("calleeCorrect").toBool(), true);
546 QQmlComponent component(&engine, testFileUrl("signalArguments.2.qml"));
547 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
548 QVERIFY(object != 0);
549 QCOMPARE(object->string(), QString());
550 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
551 QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
552 QCOMPARE(object->property("argumentCount").toInt(), 5);
553 QCOMPARE(object->property("calleeCorrect").toBool(), true);
558 void tst_qqmlecmascript::methods()
561 QQmlComponent component(&engine, testFileUrl("methods.1.qml"));
562 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
563 QVERIFY(object != 0);
564 QCOMPARE(object->methodCalled(), false);
565 QCOMPARE(object->methodIntCalled(), false);
566 emit object->basicSignal();
567 QCOMPARE(object->methodCalled(), true);
568 QCOMPARE(object->methodIntCalled(), false);
573 QQmlComponent component(&engine, testFileUrl("methods.2.qml"));
574 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
575 QVERIFY(object != 0);
576 QCOMPARE(object->methodCalled(), false);
577 QCOMPARE(object->methodIntCalled(), false);
578 emit object->basicSignal();
579 QCOMPARE(object->methodCalled(), false);
580 QCOMPARE(object->methodIntCalled(), true);
585 QQmlComponent component(&engine, testFileUrl("methods.3.qml"));
586 QObject *object = component.create();
587 QVERIFY(object != 0);
588 QCOMPARE(object->property("test").toInt(), 19);
593 QQmlComponent component(&engine, testFileUrl("methods.4.qml"));
594 QObject *object = component.create();
595 QVERIFY(object != 0);
596 QCOMPARE(object->property("test").toInt(), 19);
597 QCOMPARE(object->property("test2").toInt(), 17);
598 QCOMPARE(object->property("test3").toInt(), 16);
603 QQmlComponent component(&engine, testFileUrl("methods.5.qml"));
604 QObject *object = component.create();
605 QVERIFY(object != 0);
606 QCOMPARE(object->property("test").toInt(), 9);
611 void tst_qqmlecmascript::bindingLoop()
613 QQmlComponent component(&engine, testFileUrl("bindingLoop.qml"));
614 QString warning = component.url().toString() + ":9:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\"";
615 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
616 QObject *object = component.create();
617 QVERIFY(object != 0);
621 void tst_qqmlecmascript::basicExpressions_data()
623 QTest::addColumn<QString>("expression");
624 QTest::addColumn<QVariant>("result");
625 QTest::addColumn<bool>("nest");
627 QTest::newRow("Syntax error (self test)") << "{console.log({'a':1'}.a)}" << QVariant() << false;
628 QTest::newRow("Context property") << "a" << QVariant(1944) << false;
629 QTest::newRow("Context property") << "a" << QVariant(1944) << true;
630 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << false;
631 QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << true;
632 QTest::newRow("Overridden context property") << "b" << QVariant("Milk") << false;
633 QTest::newRow("Overridden context property") << "b" << QVariant("Cow") << true;
634 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << false;
635 QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << true;
636 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object2") << false;
637 QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object3") << true;
638 QTest::newRow("Default object property") << "horseLegs" << QVariant(4) << false;
639 QTest::newRow("Default object property") << "antLegs" << QVariant(6) << false;
640 QTest::newRow("Default object property") << "emuLegs" << QVariant(2) << false;
641 QTest::newRow("Nested default object property") << "horseLegs" << QVariant(4) << true;
642 QTest::newRow("Nested default object property") << "antLegs" << QVariant(7) << true;
643 QTest::newRow("Nested default object property") << "emuLegs" << QVariant(2) << true;
644 QTest::newRow("Nested default object property") << "humanLegs" << QVariant(2) << true;
645 QTest::newRow("Context property override default object property") << "millipedeLegs" << QVariant(100) << true;
648 void tst_qqmlecmascript::basicExpressions()
650 QFETCH(QString, expression);
651 QFETCH(QVariant, result);
657 MyDefaultObject1 default1;
658 MyDefaultObject3 default3;
659 object1.setStringProperty("Object1");
660 object2.setStringProperty("Object2");
661 object3.setStringProperty("Object3");
663 QQmlContext context(engine.rootContext());
664 QQmlContext nestedContext(&context);
666 context.setContextObject(&default1);
667 context.setContextProperty("a", QVariant(1944));
668 context.setContextProperty("b", QVariant("Milk"));
669 context.setContextProperty("object", &object1);
670 context.setContextProperty("objectOverride", &object2);
671 nestedContext.setContextObject(&default3);
672 nestedContext.setContextProperty("b", QVariant("Cow"));
673 nestedContext.setContextProperty("objectOverride", &object3);
674 nestedContext.setContextProperty("millipedeLegs", QVariant(100));
676 MyExpression expr(nest?&nestedContext:&context, expression);
677 QCOMPARE(expr.evaluate(), result);
680 void tst_qqmlecmascript::arrayExpressions()
686 QQmlContext context(engine.rootContext());
687 context.setContextProperty("a", &obj1);
688 context.setContextProperty("b", &obj2);
689 context.setContextProperty("c", &obj3);
691 MyExpression expr(&context, "[a, b, c, 10]");
692 QVariant result = expr.evaluate();
693 QCOMPARE(result.userType(), qMetaTypeId<QVariantList>());
694 QVariantList list = qvariant_cast<QVariantList>(result);
695 QCOMPARE(list.count(), 4);
696 QCOMPARE(list.at(0).value<QObject*>(), &obj1);
697 QCOMPARE(list.at(1).value<QObject*>(), &obj2);
698 QCOMPARE(list.at(2).value<QObject*>(), &obj3);
699 QCOMPARE(list.at(3).value<int>(), 10);
702 // Tests that modifying a context property will reevaluate expressions
703 void tst_qqmlecmascript::contextPropertiesTriggerReeval()
705 QQmlContext context(engine.rootContext());
708 MyQmlObject *object3 = new MyQmlObject;
710 object1.setStringProperty("Hello");
711 object2.setStringProperty("World");
713 context.setContextProperty("testProp", QVariant(1));
714 context.setContextProperty("testObj", &object1);
715 context.setContextProperty("testObj2", object3);
718 MyExpression expr(&context, "testProp + 1");
719 QCOMPARE(expr.changed, false);
720 QCOMPARE(expr.evaluate(), QVariant(2));
722 context.setContextProperty("testProp", QVariant(2));
723 QCOMPARE(expr.changed, true);
724 QCOMPARE(expr.evaluate(), QVariant(3));
728 MyExpression expr(&context, "testProp + testProp + testProp");
729 QCOMPARE(expr.changed, false);
730 QCOMPARE(expr.evaluate(), QVariant(6));
732 context.setContextProperty("testProp", QVariant(4));
733 QCOMPARE(expr.changed, true);
734 QCOMPARE(expr.evaluate(), QVariant(12));
738 MyExpression expr(&context, "testObj.stringProperty");
739 QCOMPARE(expr.changed, false);
740 QCOMPARE(expr.evaluate(), QVariant("Hello"));
742 context.setContextProperty("testObj", &object2);
743 QCOMPARE(expr.changed, true);
744 QCOMPARE(expr.evaluate(), QVariant("World"));
748 MyExpression expr(&context, "testObj.stringProperty /**/");
749 QCOMPARE(expr.changed, false);
750 QCOMPARE(expr.evaluate(), QVariant("World"));
752 context.setContextProperty("testObj", &object1);
753 QCOMPARE(expr.changed, true);
754 QCOMPARE(expr.evaluate(), QVariant("Hello"));
758 MyExpression expr(&context, "testObj2");
759 QCOMPARE(expr.changed, false);
760 QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3));
766 void tst_qqmlecmascript::objectPropertiesTriggerReeval()
768 QQmlContext context(engine.rootContext());
772 context.setContextProperty("testObj", &object1);
774 object1.setStringProperty(QLatin1String("Hello"));
775 object2.setStringProperty(QLatin1String("Dog"));
776 object3.setStringProperty(QLatin1String("Cat"));
779 MyExpression expr(&context, "testObj.stringProperty");
780 QCOMPARE(expr.changed, false);
781 QCOMPARE(expr.evaluate(), QVariant("Hello"));
783 object1.setStringProperty(QLatin1String("World"));
784 QCOMPARE(expr.changed, true);
785 QCOMPARE(expr.evaluate(), QVariant("World"));
789 MyExpression expr(&context, "testObj.objectProperty.stringProperty");
790 QCOMPARE(expr.changed, false);
791 QCOMPARE(expr.evaluate(), QVariant());
793 object1.setObjectProperty(&object2);
794 QCOMPARE(expr.changed, true);
795 expr.changed = false;
796 QCOMPARE(expr.evaluate(), QVariant("Dog"));
798 object1.setObjectProperty(&object3);
799 QCOMPARE(expr.changed, true);
800 expr.changed = false;
801 QCOMPARE(expr.evaluate(), QVariant("Cat"));
803 object1.setObjectProperty(0);
804 QCOMPARE(expr.changed, true);
805 expr.changed = false;
806 QCOMPARE(expr.evaluate(), QVariant());
808 object1.setObjectProperty(&object3);
809 QCOMPARE(expr.changed, true);
810 expr.changed = false;
811 QCOMPARE(expr.evaluate(), QVariant("Cat"));
813 object3.setStringProperty("Donkey");
814 QCOMPARE(expr.changed, true);
815 expr.changed = false;
816 QCOMPARE(expr.evaluate(), QVariant("Donkey"));
820 void tst_qqmlecmascript::deferredProperties()
822 QQmlComponent component(&engine, testFileUrl("deferredProperties.qml"));
823 MyDeferredObject *object =
824 qobject_cast<MyDeferredObject *>(component.create());
825 QVERIFY(object != 0);
826 QCOMPARE(object->value(), 0);
827 QVERIFY(object->objectProperty() == 0);
828 QVERIFY(object->objectProperty2() != 0);
829 qmlExecuteDeferred(object);
830 QCOMPARE(object->value(), 10);
831 QVERIFY(object->objectProperty() != 0);
832 MyQmlObject *qmlObject =
833 qobject_cast<MyQmlObject *>(object->objectProperty());
834 QVERIFY(qmlObject != 0);
835 QCOMPARE(qmlObject->value(), 10);
836 object->setValue(19);
837 QCOMPARE(qmlObject->value(), 19);
842 // Check errors on deferred properties are correctly emitted
843 void tst_qqmlecmascript::deferredPropertiesErrors()
845 QQmlComponent component(&engine, testFileUrl("deferredPropertiesErrors.qml"));
846 MyDeferredObject *object =
847 qobject_cast<MyDeferredObject *>(component.create());
848 QVERIFY(object != 0);
849 QCOMPARE(object->value(), 0);
850 QVERIFY(object->objectProperty() == 0);
851 QVERIFY(object->objectProperty2() == 0);
853 QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject*";
854 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
856 qmlExecuteDeferred(object);
861 void tst_qqmlecmascript::extensionObjects()
863 QQmlComponent component(&engine, testFileUrl("extensionObjects.qml"));
864 MyExtendedObject *object =
865 qobject_cast<MyExtendedObject *>(component.create());
866 QVERIFY(object != 0);
867 QCOMPARE(object->baseProperty(), 13);
868 QCOMPARE(object->coreProperty(), 9);
869 object->setProperty("extendedProperty", QVariant(11));
870 object->setProperty("baseExtendedProperty", QVariant(92));
871 QCOMPARE(object->coreProperty(), 11);
872 QCOMPARE(object->baseProperty(), 92);
874 MyExtendedObject *nested = qobject_cast<MyExtendedObject*>(qvariant_cast<QObject *>(object->property("nested")));
876 QCOMPARE(nested->baseProperty(), 13);
877 QCOMPARE(nested->coreProperty(), 9);
878 nested->setProperty("extendedProperty", QVariant(11));
879 nested->setProperty("baseExtendedProperty", QVariant(92));
880 QCOMPARE(nested->coreProperty(), 11);
881 QCOMPARE(nested->baseProperty(), 92);
886 void tst_qqmlecmascript::overrideExtensionProperties()
888 QQmlComponent component(&engine, testFileUrl("extensionObjectsPropertyOverride.qml"));
889 OverrideDefaultPropertyObject *object =
890 qobject_cast<OverrideDefaultPropertyObject *>(component.create());
891 QVERIFY(object != 0);
892 QVERIFY(object->secondProperty() != 0);
893 QVERIFY(object->firstProperty() == 0);
898 void tst_qqmlecmascript::attachedProperties()
901 QQmlComponent component(&engine, testFileUrl("attachedProperty.qml"));
902 QObject *object = component.create();
903 QVERIFY(object != 0);
904 QCOMPARE(object->property("a").toInt(), 19);
905 QCOMPARE(object->property("b").toInt(), 19);
906 QCOMPARE(object->property("c").toInt(), 19);
907 QCOMPARE(object->property("d").toInt(), 19);
912 QQmlComponent component(&engine, testFileUrl("attachedProperty.2.qml"));
913 QObject *object = component.create();
914 QVERIFY(object != 0);
915 QCOMPARE(object->property("a").toInt(), 26);
916 QCOMPARE(object->property("b").toInt(), 26);
917 QCOMPARE(object->property("c").toInt(), 26);
918 QCOMPARE(object->property("d").toInt(), 26);
924 QQmlComponent component(&engine, testFileUrl("writeAttachedProperty.qml"));
925 QObject *object = component.create();
926 QVERIFY(object != 0);
928 QMetaObject::invokeMethod(object, "writeValue2");
930 MyQmlAttachedObject *attached =
931 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
932 QVERIFY(attached != 0);
934 QCOMPARE(attached->value2(), 9);
939 void tst_qqmlecmascript::enums()
943 QQmlComponent component(&engine, testFileUrl("enums.1.qml"));
944 QObject *object = component.create();
945 QVERIFY(object != 0);
947 QCOMPARE(object->property("enumProperty").toInt(), (int)MyQmlObject::EnumValue2);
948 QCOMPARE(object->property("relatedEnumProperty").toInt(), (int)MyEnumContainer::RelatedValue);
949 QCOMPARE(object->property("unrelatedEnumProperty").toInt(), (int)MyEnumContainer::RelatedValue);
950 QCOMPARE(object->property("qtEnumProperty").toInt(), (int)Qt::CaseInsensitive);
951 QCOMPARE(object->property("a").toInt(), 0);
952 QCOMPARE(object->property("b").toInt(), 1);
953 QCOMPARE(object->property("c").toInt(), 2);
954 QCOMPARE(object->property("d").toInt(), 3);
955 QCOMPARE(object->property("e").toInt(), 0);
956 QCOMPARE(object->property("f").toInt(), 1);
957 QCOMPARE(object->property("g").toInt(), 2);
958 QCOMPARE(object->property("h").toInt(), 3);
959 QCOMPARE(object->property("i").toInt(), 19);
960 QCOMPARE(object->property("j").toInt(), 19);
961 QCOMPARE(object->property("k").toInt(), 42);
962 QCOMPARE(object->property("l").toInt(), 333);
966 // Non-existent enums
968 QUrl file = testFileUrl("enums.2.qml");
969 QString w1 = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'MyEnum' for property 'MyUnregisteredEnumTypeObject::enumProperty'");
970 QString w2 = QLatin1String("QQmlExpression: Expression ") + testFileUrl("enums.2.qml").toString() + QLatin1String(":9 depends on non-NOTIFYable properties:");
971 QString w3 = QLatin1String(" MyUnregisteredEnumTypeObject::enumProperty");
972 QString w4 = file.toString() + ":7: Unable to assign [undefined] to int";
973 QString w5 = file.toString() + ":8: Unable to assign [undefined] to int";
974 QString w6 = file.toString() + ":9: Unable to assign [undefined] to int";
975 QString w7 = file.toString() + ":13: Unable to assign [undefined] to [unknown property type]";
976 QString w8 = file.toString() + ":31: Unable to assign int to [unknown property type]";
977 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
978 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
979 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
980 QTest::ignoreMessage(QtWarningMsg, qPrintable(w4));
981 QTest::ignoreMessage(QtWarningMsg, qPrintable(w5));
982 QTest::ignoreMessage(QtWarningMsg, qPrintable(w6));
983 QTest::ignoreMessage(QtWarningMsg, qPrintable(w7));
984 QTest::ignoreMessage(QtWarningMsg, qPrintable(w8));
986 QQmlComponent component(&engine, testFileUrl("enums.2.qml"));
987 QObject *object = component.create();
988 QVERIFY(object != 0);
989 QCOMPARE(object->property("a").toInt(), 0);
990 QCOMPARE(object->property("b").toInt(), 0);
991 QCOMPARE(object->property("c").toInt(), 0);
993 QString w9 = file.toString() + ":18: Error: Cannot assign JavaScript function to [unknown property type]";
994 QTest::ignoreMessage(QtWarningMsg, qPrintable(w9));
995 QMetaObject::invokeMethod(object, "testAssignmentOne");
997 QString w10 = file.toString() + ":21: Error: Cannot assign [undefined] to [unknown property type]";
998 QTest::ignoreMessage(QtWarningMsg, qPrintable(w10));
999 QMetaObject::invokeMethod(object, "testAssignmentTwo");
1001 QString w11 = file.toString() + ":24: Error: Cannot assign [undefined] to [unknown property type]";
1002 QTest::ignoreMessage(QtWarningMsg, qPrintable(w11));
1003 QMetaObject::invokeMethod(object, "testAssignmentThree");
1005 QString w12 = file.toString() + ":34: Error: Cannot assign int to an unregistered type";
1006 QTest::ignoreMessage(QtWarningMsg, qPrintable(w12));
1007 QMetaObject::invokeMethod(object, "testAssignmentFour");
1011 // Enums as literals
1013 QQmlComponent component(&engine, testFileUrl("enums.3.qml"));
1014 QObject *object = component.create();
1015 QVERIFY(object != 0);
1017 // check the values are what we expect
1018 QCOMPARE(object->property("a").toInt(), 4);
1019 QCOMPARE(object->property("b").toInt(), 5);
1020 QCOMPARE(object->property("c").toInt(), 9);
1021 QCOMPARE(object->property("d").toInt(), 13);
1022 QCOMPARE(object->property("e").toInt(), 2);
1023 QCOMPARE(object->property("f").toInt(), 3);
1024 QCOMPARE(object->property("h").toInt(), 2);
1025 QCOMPARE(object->property("i").toInt(), 3);
1026 QCOMPARE(object->property("j").toInt(), -1);
1027 QCOMPARE(object->property("k").toInt(), 42);
1029 // count of change signals
1030 QCOMPARE(object->property("ac").toInt(), 0);
1031 QCOMPARE(object->property("bc").toInt(), 0);
1032 QCOMPARE(object->property("cc").toInt(), 0);
1033 QCOMPARE(object->property("dc").toInt(), 0);
1034 QCOMPARE(object->property("ec").toInt(), 0);
1035 QCOMPARE(object->property("fc").toInt(), 0);
1036 QCOMPARE(object->property("hc").toInt(), 1); // namespace -> binding
1037 QCOMPARE(object->property("ic").toInt(), 1); // namespace -> binding
1038 QCOMPARE(object->property("jc").toInt(), 0);
1039 QCOMPARE(object->property("kc").toInt(), 0);
1045 void tst_qqmlecmascript::valueTypeFunctions()
1047 QQmlComponent component(&engine, testFileUrl("valueTypeFunctions.qml"));
1048 MyTypeObject *obj = qobject_cast<MyTypeObject*>(component.create());
1050 QCOMPARE(obj->rectProperty(), QRect(0,0,100,100));
1051 QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5));
1057 Tests that writing a constant to a property with a binding on it disables the
1060 void tst_qqmlecmascript::constantsOverrideBindings()
1064 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.1.qml"));
1065 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1066 QVERIFY(object != 0);
1068 QCOMPARE(object->property("c2").toInt(), 0);
1069 object->setProperty("c1", QVariant(9));
1070 QCOMPARE(object->property("c2").toInt(), 9);
1072 emit object->basicSignal();
1074 QCOMPARE(object->property("c2").toInt(), 13);
1075 object->setProperty("c1", QVariant(8));
1076 QCOMPARE(object->property("c2").toInt(), 13);
1081 // During construction
1083 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.2.qml"));
1084 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1085 QVERIFY(object != 0);
1087 QCOMPARE(object->property("c1").toInt(), 0);
1088 QCOMPARE(object->property("c2").toInt(), 10);
1089 object->setProperty("c1", QVariant(9));
1090 QCOMPARE(object->property("c1").toInt(), 9);
1091 QCOMPARE(object->property("c2").toInt(), 10);
1099 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.3.qml"));
1100 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1101 QVERIFY(object != 0);
1103 QCOMPARE(object->property("c2").toInt(), 0);
1104 object->setProperty("c1", QVariant(9));
1105 QCOMPARE(object->property("c2").toInt(), 9);
1107 object->setProperty("c2", QVariant(13));
1108 QCOMPARE(object->property("c2").toInt(), 13);
1109 object->setProperty("c1", QVariant(7));
1110 QCOMPARE(object->property("c1").toInt(), 7);
1111 QCOMPARE(object->property("c2").toInt(), 13);
1119 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.4.qml"));
1120 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1121 QVERIFY(object != 0);
1123 QCOMPARE(object->property("c1").toInt(), 0);
1124 QCOMPARE(object->property("c3").toInt(), 10);
1125 object->setProperty("c1", QVariant(9));
1126 QCOMPARE(object->property("c1").toInt(), 9);
1127 QCOMPARE(object->property("c3").toInt(), 10);
1134 Tests that assigning a binding to a property that already has a binding causes
1135 the original binding to be disabled.
1137 void tst_qqmlecmascript::outerBindingOverridesInnerBinding()
1139 QQmlComponent component(&engine,
1140 testFileUrl("outerBindingOverridesInnerBinding.qml"));
1141 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1142 QVERIFY(object != 0);
1144 QCOMPARE(object->property("c1").toInt(), 0);
1145 QCOMPARE(object->property("c2").toInt(), 0);
1146 QCOMPARE(object->property("c3").toInt(), 0);
1148 object->setProperty("c1", QVariant(9));
1149 QCOMPARE(object->property("c1").toInt(), 9);
1150 QCOMPARE(object->property("c2").toInt(), 0);
1151 QCOMPARE(object->property("c3").toInt(), 0);
1153 object->setProperty("c3", QVariant(8));
1154 QCOMPARE(object->property("c1").toInt(), 9);
1155 QCOMPARE(object->property("c2").toInt(), 8);
1156 QCOMPARE(object->property("c3").toInt(), 8);
1162 Access a non-existent attached object.
1164 Tests for a regression where this used to crash.
1166 void tst_qqmlecmascript::nonExistentAttachedObject()
1168 QQmlComponent component(&engine, testFileUrl("nonExistentAttachedObject.qml"));
1170 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString";
1171 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
1173 QObject *object = component.create();
1174 QVERIFY(object != 0);
1179 void tst_qqmlecmascript::scope()
1182 QQmlComponent component(&engine, testFileUrl("scope.qml"));
1183 QObject *object = component.create();
1184 QVERIFY(object != 0);
1186 QCOMPARE(object->property("test1").toInt(), 1);
1187 QCOMPARE(object->property("test2").toInt(), 2);
1188 QCOMPARE(object->property("test3").toString(), QString("1Test"));
1189 QCOMPARE(object->property("test4").toString(), QString("2Test"));
1190 QCOMPARE(object->property("test5").toInt(), 1);
1191 QCOMPARE(object->property("test6").toInt(), 1);
1192 QCOMPARE(object->property("test7").toInt(), 2);
1193 QCOMPARE(object->property("test8").toInt(), 2);
1194 QCOMPARE(object->property("test9").toInt(), 1);
1195 QCOMPARE(object->property("test10").toInt(), 3);
1201 QQmlComponent component(&engine, testFileUrl("scope.2.qml"));
1202 QObject *object = component.create();
1203 QVERIFY(object != 0);
1205 QCOMPARE(object->property("test1").toInt(), 19);
1206 QCOMPARE(object->property("test2").toInt(), 19);
1207 QCOMPARE(object->property("test3").toInt(), 14);
1208 QCOMPARE(object->property("test4").toInt(), 14);
1209 QCOMPARE(object->property("test5").toInt(), 24);
1210 QCOMPARE(object->property("test6").toInt(), 24);
1216 QQmlComponent component(&engine, testFileUrl("scope.3.qml"));
1217 QObject *object = component.create();
1218 QVERIFY(object != 0);
1220 QCOMPARE(object->property("test1").toBool(), true);
1221 QCOMPARE(object->property("test2").toBool(), true);
1222 QCOMPARE(object->property("test3").toBool(), true);
1227 // Signal argument scope
1229 QQmlComponent component(&engine, testFileUrl("scope.4.qml"));
1230 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1231 QVERIFY(object != 0);
1233 QCOMPARE(object->property("test").toInt(), 0);
1234 QCOMPARE(object->property("test2").toString(), QString());
1236 emit object->argumentSignal(13, "Argument Scope", 9, MyQmlObject::EnumValue4, Qt::RightButton);
1238 QCOMPARE(object->property("test").toInt(), 13);
1239 QCOMPARE(object->property("test2").toString(), QString("Argument Scope"));
1245 QQmlComponent component(&engine, testFileUrl("scope.5.qml"));
1246 QObject *object = component.create();
1247 QVERIFY(object != 0);
1249 QCOMPARE(object->property("test1").toBool(), true);
1250 QCOMPARE(object->property("test2").toBool(), true);
1256 QQmlComponent component(&engine, testFileUrl("scope.6.qml"));
1257 QObject *object = component.create();
1258 QVERIFY(object != 0);
1260 QCOMPARE(object->property("test").toBool(), true);
1266 // In 4.7, non-library javascript files that had no imports shared the imports of their
1267 // importing context
1268 void tst_qqmlecmascript::importScope()
1270 QQmlComponent component(&engine, testFileUrl("importScope.qml"));
1271 QObject *o = component.create();
1274 QCOMPARE(o->property("test").toInt(), 240);
1280 Tests that "any" type passes through a synthesized signal parameter. This
1281 is essentially a test of QQmlMetaType::copy()
1283 void tst_qqmlecmascript::signalParameterTypes()
1285 QQmlComponent component(&engine, testFileUrl("signalParameterTypes.qml"));
1286 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1287 QVERIFY(object != 0);
1289 emit object->basicSignal();
1291 QCOMPARE(object->property("intProperty").toInt(), 10);
1292 QCOMPARE(object->property("realProperty").toReal(), 19.2);
1293 QVERIFY(object->property("colorProperty").value<QColor>() == QColor(255, 255, 0, 255));
1294 QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255)));
1295 QVERIFY(object->property("enumProperty") == MyQmlObject::EnumValue3);
1296 QVERIFY(object->property("qtEnumProperty") == Qt::LeftButton);
1302 Test that two JS objects for the same QObject compare as equal.
1304 void tst_qqmlecmascript::objectsCompareAsEqual()
1306 QQmlComponent component(&engine, testFileUrl("objectsCompareAsEqual.qml"));
1307 QObject *object = component.create();
1308 QVERIFY(object != 0);
1310 QCOMPARE(object->property("test1").toBool(), true);
1311 QCOMPARE(object->property("test2").toBool(), true);
1312 QCOMPARE(object->property("test3").toBool(), true);
1313 QCOMPARE(object->property("test4").toBool(), true);
1314 QCOMPARE(object->property("test5").toBool(), true);
1320 Confirm bindings and alias properties can coexist.
1322 Tests for a regression where the binding would not reevaluate.
1324 void tst_qqmlecmascript::aliasPropertyAndBinding()
1326 QQmlComponent component(&engine, testFileUrl("aliasPropertyAndBinding.qml"));
1327 QObject *object = component.create();
1328 QVERIFY(object != 0);
1330 QCOMPARE(object->property("c2").toInt(), 3);
1331 QCOMPARE(object->property("c3").toInt(), 3);
1333 object->setProperty("c2", QVariant(19));
1335 QCOMPARE(object->property("c2").toInt(), 19);
1336 QCOMPARE(object->property("c3").toInt(), 19);
1342 Ensure that we can write undefined value to an alias property,
1343 and that the aliased property is reset correctly if possible.
1345 void tst_qqmlecmascript::aliasPropertyReset()
1347 QObject *object = 0;
1349 // test that a manual write (of undefined) to a resettable aliased property succeeds
1350 QQmlComponent c1(&engine, testFileUrl("aliasreset/aliasPropertyReset.1.qml"));
1351 object = c1.create();
1352 QVERIFY(object != 0);
1353 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1354 QCOMPARE(object->property("aliasIsUndefined"), QVariant(false));
1355 QMetaObject::invokeMethod(object, "resetAliased");
1356 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1357 QCOMPARE(object->property("aliasIsUndefined"), QVariant(true));
1360 // test that a manual write (of undefined) to a resettable alias property succeeds
1361 QQmlComponent c2(&engine, testFileUrl("aliasreset/aliasPropertyReset.2.qml"));
1362 object = c2.create();
1363 QVERIFY(object != 0);
1364 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1365 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false));
1366 QMetaObject::invokeMethod(object, "resetAlias");
1367 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1368 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(true));
1371 // test that an alias to a bound property works correctly
1372 QQmlComponent c3(&engine, testFileUrl("aliasreset/aliasPropertyReset.3.qml"));
1373 object = c3.create();
1374 QVERIFY(object != 0);
1375 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1376 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false));
1377 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1378 QMetaObject::invokeMethod(object, "resetAlias");
1379 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1380 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(true));
1381 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1384 // test that a manual write (of undefined) to a resettable alias property
1385 // whose aliased property's object has been deleted, does not crash.
1386 QQmlComponent c4(&engine, testFileUrl("aliasreset/aliasPropertyReset.4.qml"));
1387 object = c4.create();
1388 QVERIFY(object != 0);
1389 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1390 QObject *loader = object->findChild<QObject*>("loader");
1391 QVERIFY(loader != 0);
1393 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // deletion should have caused value unset.
1394 QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash.
1395 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1396 QMetaObject::invokeMethod(object, "setAlias"); // shouldn't crash, and shouldn't change value (since it's no longer referencing anything).
1397 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1400 // test that binding an alias property to an undefined value works correctly
1401 QQmlComponent c5(&engine, testFileUrl("aliasreset/aliasPropertyReset.5.qml"));
1402 object = c5.create();
1403 QVERIFY(object != 0);
1404 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // bound to undefined value.
1407 // test that a manual write (of undefined) to a non-resettable property fails properly
1408 QUrl url = testFileUrl("aliasreset/aliasPropertyReset.error.1.qml");
1409 QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int");
1410 QQmlComponent e1(&engine, url);
1411 object = e1.create();
1412 QVERIFY(object != 0);
1413 QCOMPARE(object->property("intAlias").value<int>(), 12);
1414 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1415 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1416 QMetaObject::invokeMethod(object, "resetAlias");
1417 QCOMPARE(object->property("intAlias").value<int>(), 12);
1418 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1422 void tst_qqmlecmascript::componentCreation_data()
1424 QTest::addColumn<QString>("method");
1425 QTest::addColumn<QString>("creationError");
1426 QTest::addColumn<QString>("createdParent");
1428 QTest::newRow("url")
1432 QTest::newRow("urlMode")
1436 QTest::newRow("urlParent")
1440 QTest::newRow("urlNullParent")
1444 QTest::newRow("urlModeParent")
1448 QTest::newRow("urlModeNullParent")
1449 << "urlModeNullParent"
1452 QTest::newRow("invalidSecondArg")
1453 << "invalidSecondArg"
1454 << ":40: Error: Qt.createComponent(): Invalid arguments"
1456 QTest::newRow("invalidThirdArg")
1457 << "invalidThirdArg"
1458 << ":45: Error: Qt.createComponent(): Invalid parent object"
1460 QTest::newRow("invalidMode")
1462 << ":50: Error: Qt.createComponent(): Invalid arguments"
1467 Test using createComponent to dynamically generate a component.
1469 void tst_qqmlecmascript::componentCreation()
1471 QFETCH(QString, method);
1472 QFETCH(QString, creationError);
1473 QFETCH(QString, createdParent);
1475 QUrl testUrl(testFileUrl("componentCreation.qml"));
1477 if (!creationError.isEmpty()) {
1478 QString warning = testUrl.toString() + creationError;
1479 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1482 QQmlComponent component(&engine, testUrl);
1483 MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create());
1484 QVERIFY(object != 0);
1486 QMetaObject::invokeMethod(object, method.toUtf8());
1487 QQmlComponent *created = object->componentProperty();
1489 if (creationError.isEmpty()) {
1492 QObject *expectedParent;
1493 if (createdParent == QLatin1String("obj")) {
1494 expectedParent = object;
1495 } else if ((createdParent == QLatin1String("null")) || createdParent.isEmpty()) {
1498 QCOMPARE(created->parent(), expectedParent);
1502 void tst_qqmlecmascript::dynamicCreation_data()
1504 QTest::addColumn<QString>("method");
1505 QTest::addColumn<QString>("createdName");
1507 QTest::newRow("One") << "createOne" << "objectOne";
1508 QTest::newRow("Two") << "createTwo" << "objectTwo";
1509 QTest::newRow("Three") << "createThree" << "objectThree";
1513 Test using createQmlObject to dynamically generate an item
1514 Also using createComponent is tested.
1516 void tst_qqmlecmascript::dynamicCreation()
1518 QFETCH(QString, method);
1519 QFETCH(QString, createdName);
1521 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
1522 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1523 QVERIFY(object != 0);
1525 QMetaObject::invokeMethod(object, method.toUtf8());
1526 QObject *created = object->objectProperty();
1528 QCOMPARE(created->objectName(), createdName);
1534 Tests the destroy function
1536 void tst_qqmlecmascript::dynamicDestruction()
1539 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.qml"));
1540 QQmlGuard<MyQmlObject> object = qobject_cast<MyQmlObject*>(component.create());
1541 QVERIFY(object != 0);
1542 QQmlGuard<QObject> createdQmlObject = 0;
1544 QMetaObject::invokeMethod(object, "create");
1545 createdQmlObject = object->objectProperty();
1546 QVERIFY(createdQmlObject);
1547 QCOMPARE(createdQmlObject->objectName(), QString("emptyObject"));
1549 QMetaObject::invokeMethod(object, "killOther");
1550 QVERIFY(createdQmlObject);
1552 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1553 QCoreApplication::processEvents();
1554 QVERIFY(createdQmlObject);
1555 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1556 if (createdQmlObject) {
1558 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1559 QCoreApplication::processEvents();
1562 QVERIFY(!createdQmlObject);
1564 QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
1565 QMetaObject::invokeMethod(object, "killMe");
1567 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1568 QCoreApplication::processEvents();
1573 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.2.qml"));
1574 QObject *o = component.create();
1577 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1579 QMetaObject::invokeMethod(o, "create");
1581 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) != 0);
1583 QMetaObject::invokeMethod(o, "destroy");
1585 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1586 QCoreApplication::processEvents();
1588 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1595 QQmlGuard<QObject> createdQmlObject = 0;
1596 QQmlComponent component(&engine, testFileUrl("dynamicDeletion.3.qml"));
1597 QObject *o = component.create();
1599 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1600 QMetaObject::invokeMethod(o, "create");
1601 createdQmlObject = qvariant_cast<QObject*>(o->property("objectProperty"));
1602 QVERIFY(createdQmlObject);
1603 QMetaObject::invokeMethod(o, "destroy");
1604 QVERIFY(qvariant_cast<bool>(o->property("test")) == false);
1605 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1607 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1608 QCoreApplication::processEvents();
1610 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1611 QVERIFY(qvariant_cast<bool>(o->property("test")) == true);
1617 tests that id.toString() works
1619 void tst_qqmlecmascript::objectToString()
1621 QQmlComponent component(&engine, testFileUrl("qmlToString.qml"));
1622 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1623 QVERIFY(object != 0);
1624 QMetaObject::invokeMethod(object, "testToString");
1625 QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_"));
1626 QVERIFY(object->stringProperty().endsWith(", \"objName\")"));
1632 tests that id.hasOwnProperty() works
1634 void tst_qqmlecmascript::objectHasOwnProperty()
1636 QUrl url = testFileUrl("qmlHasOwnProperty.qml");
1637 QString warning1 = url.toString() + ":59: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1638 QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1639 QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1641 QQmlComponent component(&engine, url);
1642 QObject *object = component.create();
1643 QVERIFY(object != 0);
1645 // test QObjects in QML
1646 QMetaObject::invokeMethod(object, "testHasOwnPropertySuccess");
1647 QVERIFY(object->property("result").value<bool>() == true);
1648 QMetaObject::invokeMethod(object, "testHasOwnPropertyFailure");
1649 QVERIFY(object->property("result").value<bool>() == false);
1651 // now test other types in QML
1652 QObject *child = object->findChild<QObject*>("typeObj");
1653 QVERIFY(child != 0);
1654 QMetaObject::invokeMethod(child, "testHasOwnPropertySuccess");
1655 QCOMPARE(child->property("valueTypeHasOwnProperty").toBool(), true);
1656 QCOMPARE(child->property("valueTypeHasOwnProperty2").toBool(), true);
1657 QCOMPARE(child->property("variantTypeHasOwnProperty").toBool(), true);
1658 QCOMPARE(child->property("stringTypeHasOwnProperty").toBool(), true);
1659 QCOMPARE(child->property("listTypeHasOwnProperty").toBool(), true);
1660 QCOMPARE(child->property("emptyListTypeHasOwnProperty").toBool(), true);
1661 QCOMPARE(child->property("enumTypeHasOwnProperty").toBool(), true);
1662 QCOMPARE(child->property("typenameHasOwnProperty").toBool(), true);
1663 QCOMPARE(child->property("typenameHasOwnProperty2").toBool(), true);
1664 QCOMPARE(child->property("singletonTypeTypeHasOwnProperty").toBool(), true);
1665 QCOMPARE(child->property("singletonTypePropertyTypeHasOwnProperty").toBool(), true);
1667 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1668 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureOne");
1669 QCOMPARE(child->property("enumNonValueHasOwnProperty").toBool(), false);
1670 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1671 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureTwo");
1672 QCOMPARE(child->property("singletonTypeNonPropertyHasOwnProperty").toBool(), false);
1673 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1674 QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureThree");
1675 QCOMPARE(child->property("listAtInvalidHasOwnProperty").toBool(), false);
1681 Tests bindings that indirectly cause their own deletion work.
1683 This test is best run under valgrind to ensure no invalid memory access occur.
1685 void tst_qqmlecmascript::selfDeletingBinding()
1688 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.qml"));
1689 QObject *object = component.create();
1690 QVERIFY(object != 0);
1691 object->setProperty("triggerDelete", true);
1696 QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.2.qml"));
1697 QObject *object = component.create();
1698 QVERIFY(object != 0);
1699 object->setProperty("triggerDelete", true);
1705 Test that extended object properties can be accessed.
1707 This test a regression where this used to crash. The issue was specificially
1708 for extended objects that did not include a synthesized meta object (so non-root
1709 and no synthesiszed properties).
1711 void tst_qqmlecmascript::extendedObjectPropertyLookup()
1713 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup.qml"));
1714 QObject *object = component.create();
1715 QVERIFY(object != 0);
1720 Test that extended object properties can be accessed correctly.
1722 void tst_qqmlecmascript::extendedObjectPropertyLookup2()
1724 QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup2.qml"));
1725 QObject *object = component.create();
1726 QVERIFY(object != 0);
1728 QVariant returnValue;
1729 QVERIFY(QMetaObject::invokeMethod(object, "getValue", Q_RETURN_ARG(QVariant, returnValue)));
1730 QCOMPARE(returnValue.toInt(), 42);
1735 Test file/lineNumbers for binding/Script errors.
1737 void tst_qqmlecmascript::scriptErrors()
1739 QQmlComponent component(&engine, testFileUrl("scriptErrors.qml"));
1740 QString url = component.url().toString();
1742 QString warning1 = url.left(url.length() - 3) + "js:2: Error: Invalid write to global property \"a\"";
1743 QString warning2 = url + ":5: ReferenceError: a is not defined";
1744 QString warning3 = url.left(url.length() - 3) + "js:4: Error: Invalid write to global property \"a\"";
1745 QString warning4 = url + ":13: ReferenceError: a is not defined";
1746 QString warning5 = url + ":11: ReferenceError: a is not defined";
1747 QString warning6 = url + ":10: Unable to assign [undefined] to int";
1748 QString warning7 = url + ":15: Error: Cannot assign to read-only property \"trueProperty\"";
1749 QString warning8 = url + ":16: Error: Cannot assign to non-existent property \"fakeProperty\"";
1751 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1752 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1753 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1754 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
1755 QTest::ignoreMessage(QtWarningMsg, warning6.toLatin1().constData());
1756 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1757 QVERIFY(object != 0);
1759 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
1760 emit object->basicSignal();
1762 QTest::ignoreMessage(QtWarningMsg, warning7.toLatin1().constData());
1763 emit object->anotherBasicSignal();
1765 QTest::ignoreMessage(QtWarningMsg, warning8.toLatin1().constData());
1766 emit object->thirdBasicSignal();
1772 Test file/lineNumbers for inline functions.
1774 void tst_qqmlecmascript::functionErrors()
1776 QQmlComponent component(&engine, testFileUrl("functionErrors.qml"));
1777 QString url = component.url().toString();
1779 QString warning = url + ":5: Error: Invalid write to global property \"a\"";
1781 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1783 QObject *object = component.create();
1784 QVERIFY(object != 0);
1787 // test that if an exception occurs while invoking js function from cpp, it is reported as expected.
1788 QQmlComponent componentTwo(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
1789 url = componentTwo.url().toString();
1790 object = componentTwo.create();
1791 QVERIFY(object != 0);
1793 QString srpname = object->property("srp_name").toString();
1795 warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srpname
1796 + QLatin1String(" is not a function");
1797 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed.
1798 QMetaObject::invokeMethod(object, "retrieveScarceResource");
1803 Test various errors that can occur when assigning a property from script
1805 void tst_qqmlecmascript::propertyAssignmentErrors()
1807 QQmlComponent component(&engine, testFileUrl("propertyAssignmentErrors.qml"));
1809 QString url = component.url().toString();
1811 QObject *object = component.create();
1812 QVERIFY(object != 0);
1814 QCOMPARE(object->property("test1").toBool(), true);
1815 QCOMPARE(object->property("test2").toBool(), true);
1821 Test bindings still work when the reeval is triggered from within
1824 void tst_qqmlecmascript::signalTriggeredBindings()
1826 QQmlComponent component(&engine, testFileUrl("signalTriggeredBindings.qml"));
1827 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1828 QVERIFY(object != 0);
1830 QCOMPARE(object->property("base").toReal(), 50.);
1831 QCOMPARE(object->property("test1").toReal(), 50.);
1832 QCOMPARE(object->property("test2").toReal(), 50.);
1834 object->basicSignal();
1836 QCOMPARE(object->property("base").toReal(), 200.);
1837 QCOMPARE(object->property("test1").toReal(), 200.);
1838 QCOMPARE(object->property("test2").toReal(), 200.);
1840 object->argumentSignal(10, QString(), 10, MyQmlObject::EnumValue4, Qt::RightButton);
1842 QCOMPARE(object->property("base").toReal(), 400.);
1843 QCOMPARE(object->property("test1").toReal(), 400.);
1844 QCOMPARE(object->property("test2").toReal(), 400.);
1850 Test that list properties can be iterated from ECMAScript
1852 void tst_qqmlecmascript::listProperties()
1854 QQmlComponent component(&engine, testFileUrl("listProperties.qml"));
1855 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1856 QVERIFY(object != 0);
1858 QCOMPARE(object->property("test1").toInt(), 21);
1859 QCOMPARE(object->property("test2").toInt(), 2);
1860 QCOMPARE(object->property("test3").toBool(), true);
1861 QCOMPARE(object->property("test4").toBool(), true);
1866 void tst_qqmlecmascript::exceptionClearsOnReeval()
1868 QQmlComponent component(&engine, testFileUrl("exceptionClearsOnReeval.qml"));
1869 QString url = component.url().toString();
1871 QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null";
1873 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1874 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1875 QVERIFY(object != 0);
1877 QCOMPARE(object->property("test").toBool(), false);
1879 MyQmlObject object2;
1880 MyQmlObject object3;
1881 object2.setObjectProperty(&object3);
1882 object->setObjectProperty(&object2);
1884 QCOMPARE(object->property("test").toBool(), true);
1889 void tst_qqmlecmascript::exceptionSlotProducesWarning()
1891 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning.qml"));
1892 QString url = component.url().toString();
1894 QString warning = component.url().toString() + ":6: Error: JS exception";
1896 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1897 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1898 QVERIFY(object != 0);
1902 void tst_qqmlecmascript::exceptionBindingProducesWarning()
1904 QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning2.qml"));
1905 QString url = component.url().toString();
1907 QString warning = component.url().toString() + ":5: Error: JS exception";
1909 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1910 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1911 QVERIFY(object != 0);
1915 void tst_qqmlecmascript::compileInvalidBinding()
1917 // QTBUG-23387: ensure that invalid bindings don't cause a crash.
1918 QQmlComponent component(&engine, testFileUrl("v8bindingException.qml"));
1919 QObject *object = component.create();
1920 QVERIFY(object != 0);
1924 static int transientErrorsMsgCount = 0;
1925 static void transientErrorsMsgHandler(QtMsgType, const QMessageLogContext &, const QString &)
1927 ++transientErrorsMsgCount;
1930 // Check that transient binding errors are not displayed
1931 void tst_qqmlecmascript::transientErrors()
1934 QQmlComponent component(&engine, testFileUrl("transientErrors.qml"));
1936 transientErrorsMsgCount = 0;
1937 QtMessageHandler old = qInstallMessageHandler(transientErrorsMsgHandler);
1939 QObject *object = component.create();
1940 QVERIFY(object != 0);
1942 qInstallMessageHandler(old);
1944 QCOMPARE(transientErrorsMsgCount, 0);
1949 // One binding erroring multiple times, but then resolving
1951 QQmlComponent component(&engine, testFileUrl("transientErrors.2.qml"));
1953 transientErrorsMsgCount = 0;
1954 QtMessageHandler old = qInstallMessageHandler(transientErrorsMsgHandler);
1956 QObject *object = component.create();
1957 QVERIFY(object != 0);
1959 qInstallMessageHandler(old);
1961 QCOMPARE(transientErrorsMsgCount, 0);
1967 // Check that errors during shutdown are minimized
1968 void tst_qqmlecmascript::shutdownErrors()
1970 QQmlComponent component(&engine, testFileUrl("shutdownErrors.qml"));
1971 QObject *object = component.create();
1972 QVERIFY(object != 0);
1974 transientErrorsMsgCount = 0;
1975 QtMessageHandler old = qInstallMessageHandler(transientErrorsMsgHandler);
1979 qInstallMessageHandler(old);
1980 QCOMPARE(transientErrorsMsgCount, 0);
1983 void tst_qqmlecmascript::compositePropertyType()
1985 QQmlComponent component(&engine, testFileUrl("compositePropertyType.qml"));
1987 QTest::ignoreMessage(QtDebugMsg, "hello world");
1988 QObject *object = qobject_cast<QObject *>(component.create());
1993 void tst_qqmlecmascript::jsObject()
1995 QQmlComponent component(&engine, testFileUrl("jsObject.qml"));
1996 QObject *object = component.create();
1997 QVERIFY(object != 0);
1999 QCOMPARE(object->property("test").toInt(), 92);
2004 void tst_qqmlecmascript::undefinedResetsProperty()
2007 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.qml"));
2008 QObject *object = component.create();
2009 QVERIFY(object != 0);
2011 QCOMPARE(object->property("resettableProperty").toInt(), 92);
2013 object->setProperty("setUndefined", true);
2015 QCOMPARE(object->property("resettableProperty").toInt(), 13);
2017 object->setProperty("setUndefined", false);
2019 QCOMPARE(object->property("resettableProperty").toInt(), 92);
2024 QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.2.qml"));
2025 QObject *object = component.create();
2026 QVERIFY(object != 0);
2028 QCOMPARE(object->property("resettableProperty").toInt(), 19);
2030 QMetaObject::invokeMethod(object, "doReset");
2032 QCOMPARE(object->property("resettableProperty").toInt(), 13);
2038 // Aliases to variant properties should work
2039 void tst_qqmlecmascript::qtbug_22464()
2041 QQmlComponent component(&engine, testFileUrl("qtbug_22464.qml"));
2042 QObject *object = component.create();
2043 QVERIFY(object != 0);
2045 QCOMPARE(object->property("test").toBool(), true);
2050 void tst_qqmlecmascript::qtbug_21580()
2052 QQmlComponent component(&engine, testFileUrl("qtbug_21580.qml"));
2054 QObject *object = component.create();
2055 QVERIFY(object != 0);
2057 QCOMPARE(object->property("test").toBool(), true);
2062 // Causes a v8 binding, but not all v8 bindings to be destroyed during evaluation
2063 void tst_qqmlecmascript::singleV8BindingDestroyedDuringEvaluation()
2065 QQmlComponent component(&engine, testFileUrl("singleV8BindingDestroyedDuringEvaluation.qml"));
2067 QObject *object = component.create();
2068 QVERIFY(object != 0);
2073 void tst_qqmlecmascript::bug1()
2075 QQmlComponent component(&engine, testFileUrl("bug.1.qml"));
2076 QObject *object = component.create();
2077 QVERIFY(object != 0);
2079 QCOMPARE(object->property("test").toInt(), 14);
2081 object->setProperty("a", 11);
2083 QCOMPARE(object->property("test").toInt(), 3);
2085 object->setProperty("b", true);
2087 QCOMPARE(object->property("test").toInt(), 9);
2092 #ifndef QT_NO_WIDGETS
2093 void tst_qqmlecmascript::bug2()
2095 QQmlComponent component(&engine);
2096 component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl());
2098 QObject *object = component.create();
2099 QVERIFY(object != 0);
2105 // Don't crash in createObject when the component has errors.
2106 void tst_qqmlecmascript::dynamicCreationCrash()
2108 QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
2109 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2110 QVERIFY(object != 0);
2112 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2113 QMetaObject::invokeMethod(object, "dontCrash");
2114 QObject *created = object->objectProperty();
2115 QVERIFY(created == 0);
2120 // ownership transferred to JS, ensure that GC runs the dtor
2121 void tst_qqmlecmascript::dynamicCreationOwnership()
2124 int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
2126 // allow the engine to go out of scope too.
2128 QQmlEngine dcoEngine;
2129 QQmlComponent component(&dcoEngine, testFileUrl("dynamicCreationOwnership.qml"));
2130 QObject *object = component.create();
2131 QVERIFY(object != 0);
2132 MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo");
2133 QVERIFY(mdcdo != 0);
2134 mdcdo->setDtorCount(&dtorCount);
2136 for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
2137 QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
2139 // we do this once manually, but it should be done automatically
2140 // when the engine goes out of scope (since it should gc in dtor)
2141 QMetaObject::invokeMethod(object, "performGc");
2144 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2145 QCoreApplication::processEvents();
2151 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2152 QCoreApplication::processEvents();
2153 QCOMPARE(dtorCount, expectedDtorCount);
2156 void tst_qqmlecmascript::regExpBug()
2160 QQmlComponent component(&engine, testFileUrl("regExp.qml"));
2161 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2162 QVERIFY(object != 0);
2163 QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]"));
2169 QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString());
2170 QQmlComponent component(&engine, testFileUrl("regExp.2.qml"));
2171 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2172 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2174 QCOMPARE(component.errorString(), err);
2178 static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source)
2180 QString functionSource = QLatin1String("(function(object) { return ") +
2181 QLatin1String(source) + QLatin1String(" })");
2183 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2186 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2187 if (function.IsEmpty())
2189 v8::Handle<v8::Value> args[] = { o };
2190 function->Call(engine->global(), 1, args);
2191 return tc.HasCaught();
2194 static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o,
2195 const char *source, v8::Handle<v8::Value> result)
2197 QString functionSource = QLatin1String("(function(object) { return ") +
2198 QLatin1String(source) + QLatin1String(" })");
2200 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2203 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2204 if (function.IsEmpty())
2206 v8::Handle<v8::Value> args[] = { o };
2208 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2213 return value->StrictEquals(result);
2216 static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o,
2219 QString functionSource = QLatin1String("(function(object) { return ") +
2220 QLatin1String(source) + QLatin1String(" })");
2222 v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2224 return v8::Handle<v8::Value>();
2225 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2226 if (function.IsEmpty())
2227 return v8::Handle<v8::Value>();
2228 v8::Handle<v8::Value> args[] = { o };
2230 v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2233 return v8::Handle<v8::Value>();
2237 #define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
2238 #define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result)
2239 #define EVALUATE(source) evaluate(engine, object, source)
2241 void tst_qqmlecmascript::callQtInvokables()
2243 // This object has JS ownership, as the call to method_NoArgs_QObject() in this test will return
2244 // it, which will set the indestructible flag to false.
2245 MyInvokableObject *o = new MyInvokableObject();
2247 QQmlEngine qmlengine;
2248 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine);
2250 QV8Engine *engine = ep->v8engine();
2252 v8::HandleScope handle_scope;
2253 v8::Context::Scope scope(engine->context());
2255 v8::Local<v8::Object> object = engine->newQObject(o)->ToObject();
2257 // Non-existent methods
2259 QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
2260 QCOMPARE(o->error(), false);
2261 QCOMPARE(o->invoked(), -1);
2262 QCOMPARE(o->actuals().count(), 0);
2265 QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)"));
2266 QCOMPARE(o->error(), false);
2267 QCOMPARE(o->invoked(), -1);
2268 QCOMPARE(o->actuals().count(), 0);
2270 // Insufficient arguments
2272 QVERIFY(EVALUATE_ERROR("object.method_int()"));
2273 QCOMPARE(o->error(), false);
2274 QCOMPARE(o->invoked(), -1);
2275 QCOMPARE(o->actuals().count(), 0);
2278 QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
2279 QCOMPARE(o->error(), false);
2280 QCOMPARE(o->invoked(), -1);
2281 QCOMPARE(o->actuals().count(), 0);
2283 // Excessive arguments
2285 QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", v8::Undefined()));
2286 QCOMPARE(o->error(), false);
2287 QCOMPARE(o->invoked(), 8);
2288 QCOMPARE(o->actuals().count(), 1);
2289 QCOMPARE(o->actuals().at(0), QVariant(10));
2292 QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", v8::Undefined()));
2293 QCOMPARE(o->error(), false);
2294 QCOMPARE(o->invoked(), 9);
2295 QCOMPARE(o->actuals().count(), 2);
2296 QCOMPARE(o->actuals().at(0), QVariant(10));
2297 QCOMPARE(o->actuals().at(1), QVariant(11));
2299 // Test return types
2301 QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", v8::Undefined()));
2302 QCOMPARE(o->error(), false);
2303 QCOMPARE(o->invoked(), 0);
2304 QCOMPARE(o->actuals().count(), 0);
2307 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", v8::Integer::New(6)));
2308 QCOMPARE(o->error(), false);
2309 QCOMPARE(o->invoked(), 1);
2310 QCOMPARE(o->actuals().count(), 0);
2313 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", v8::Number::New(19.75)));
2314 QCOMPARE(o->error(), false);
2315 QCOMPARE(o->invoked(), 2);
2316 QCOMPARE(o->actuals().count(), 0);
2320 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QPointF()");
2321 QVERIFY(!ret.IsEmpty());
2322 QCOMPARE(engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5)));
2323 QCOMPARE(o->error(), false);
2324 QCOMPARE(o->invoked(), 3);
2325 QCOMPARE(o->actuals().count(), 0);
2330 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QObject()");
2331 QCOMPARE(engine->toQObject(ret), (QObject *)o);
2332 QCOMPARE(o->error(), false);
2333 QCOMPARE(o->invoked(), 4);
2334 QCOMPARE(o->actuals().count(), 0);
2338 QVERIFY(EVALUATE_ERROR("object.method_NoArgs_unknown()"));
2339 QCOMPARE(o->error(), false);
2340 QCOMPARE(o->invoked(), -1);
2341 QCOMPARE(o->actuals().count(), 0);
2345 v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QScriptValue()");
2346 QVERIFY(ret->IsString());
2347 QCOMPARE(engine->toString(ret), QString("Hello world"));
2348 QCOMPARE(o->error(), false);
2349 QCOMPARE(o->invoked(), 6);
2350 QCOMPARE(o->actuals().count(), 0);
2354 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks")));
2355 QCOMPARE(o->error(), false);
2356 QCOMPARE(o->invoked(), 7);
2357 QCOMPARE(o->actuals().count(), 0);
2361 QVERIFY(EVALUATE_VALUE("object.method_int(94)", v8::Undefined()));
2362 QCOMPARE(o->error(), false);
2363 QCOMPARE(o->invoked(), 8);
2364 QCOMPARE(o->actuals().count(), 1);
2365 QCOMPARE(o->actuals().at(0), QVariant(94));
2368 QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", v8::Undefined()));
2369 QCOMPARE(o->error(), false);
2370 QCOMPARE(o->invoked(), 8);
2371 QCOMPARE(o->actuals().count(), 1);
2372 QCOMPARE(o->actuals().at(0), QVariant(94));
2375 QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", v8::Undefined()));
2376 QCOMPARE(o->error(), false);
2377 QCOMPARE(o->invoked(), 8);
2378 QCOMPARE(o->actuals().count(), 1);
2379 QCOMPARE(o->actuals().at(0), QVariant(0));
2382 QVERIFY(EVALUATE_VALUE("object.method_int(null)", v8::Undefined()));
2383 QCOMPARE(o->error(), false);
2384 QCOMPARE(o->invoked(), 8);
2385 QCOMPARE(o->actuals().count(), 1);
2386 QCOMPARE(o->actuals().at(0), QVariant(0));
2389 QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", v8::Undefined()));
2390 QCOMPARE(o->error(), false);
2391 QCOMPARE(o->invoked(), 8);
2392 QCOMPARE(o->actuals().count(), 1);
2393 QCOMPARE(o->actuals().at(0), QVariant(0));
2396 QVERIFY(EVALUATE_VALUE("object.method_int(object)", v8::Undefined()));
2397 QCOMPARE(o->error(), false);
2398 QCOMPARE(o->invoked(), 8);
2399 QCOMPARE(o->actuals().count(), 1);
2400 QCOMPARE(o->actuals().at(0), QVariant(0));
2403 QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", v8::Undefined()));
2404 QCOMPARE(o->error(), false);
2405 QCOMPARE(o->invoked(), 9);
2406 QCOMPARE(o->actuals().count(), 2);
2407 QCOMPARE(o->actuals().at(0), QVariant(122));
2408 QCOMPARE(o->actuals().at(1), QVariant(9));
2411 QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", v8::Undefined()));
2412 QCOMPARE(o->error(), false);
2413 QCOMPARE(o->invoked(), 10);
2414 QCOMPARE(o->actuals().count(), 1);
2415 QCOMPARE(o->actuals().at(0), QVariant(94.3));
2418 QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", v8::Undefined()));
2419 QCOMPARE(o->error(), false);
2420 QCOMPARE(o->invoked(), 10);
2421 QCOMPARE(o->actuals().count(), 1);
2422 QCOMPARE(o->actuals().at(0), QVariant(94.3));
2425 QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", v8::Undefined()));
2426 QCOMPARE(o->error(), false);
2427 QCOMPARE(o->invoked(), 10);
2428 QCOMPARE(o->actuals().count(), 1);
2429 QVERIFY(qIsNaN(o->actuals().at(0).toDouble()));
2432 QVERIFY(EVALUATE_VALUE("object.method_real(null)", v8::Undefined()));
2433 QCOMPARE(o->error(), false);
2434 QCOMPARE(o->invoked(), 10);
2435 QCOMPARE(o->actuals().count(), 1);
2436 QCOMPARE(o->actuals().at(0), QVariant(0));
2439 QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", v8::Undefined()));
2440 QCOMPARE(o->error(), false);
2441 QCOMPARE(o->invoked(), 10);
2442 QCOMPARE(o->actuals().count(), 1);
2443 QVERIFY(qIsNaN(o->actuals().at(0).toDouble()));
2446 QVERIFY(EVALUATE_VALUE("object.method_real(object)", v8::Undefined()));
2447 QCOMPARE(o->error(), false);
2448 QCOMPARE(o->invoked(), 10);
2449 QCOMPARE(o->actuals().count(), 1);
2450 QVERIFY(qIsNaN(o->actuals().at(0).toDouble()));
2453 QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", v8::Undefined()));
2454 QCOMPARE(o->error(), false);
2455 QCOMPARE(o->invoked(), 11);
2456 QCOMPARE(o->actuals().count(), 1);
2457 QCOMPARE(o->actuals().at(0), QVariant("Hello world"));
2460 QVERIFY(EVALUATE_VALUE("object.method_QString(19)", v8::Undefined()));
2461 QCOMPARE(o->error(), false);
2462 QCOMPARE(o->invoked(), 11);
2463 QCOMPARE(o->actuals().count(), 1);
2464 QCOMPARE(o->actuals().at(0), QVariant("19"));
2468 QString expected = "MyInvokableObject(0x" + QString::number((quintptr)o, 16) + ")";
2469 QVERIFY(EVALUATE_VALUE("object.method_QString(object)", v8::Undefined()));
2470 QCOMPARE(o->error(), false);
2471 QCOMPARE(o->invoked(), 11);
2472 QCOMPARE(o->actuals().count(), 1);
2473 QCOMPARE(o->actuals().at(0), QVariant(expected));
2477 QVERIFY(EVALUATE_VALUE("object.method_QString(null)", v8::Undefined()));
2478 QCOMPARE(o->error(), false);
2479 QCOMPARE(o->invoked(), 11);
2480 QCOMPARE(o->actuals().count(), 1);
2481 QCOMPARE(o->actuals().at(0), QVariant(QString()));
2484 QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", v8::Undefined()));
2485 QCOMPARE(o->error(), false);
2486 QCOMPARE(o->invoked(), 11);
2487 QCOMPARE(o->actuals().count(), 1);
2488 QCOMPARE(o->actuals().at(0), QVariant(QString()));
2491 QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", v8::Undefined()));
2492 QCOMPARE(o->error(), false);
2493 QCOMPARE(o->invoked(), 12);
2494 QCOMPARE(o->actuals().count(), 1);
2495 QCOMPARE(o->actuals().at(0), QVariant(QPointF()));
2498 QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", v8::Undefined()));
2499 QCOMPARE(o->error(), false);
2500 QCOMPARE(o->invoked(), 12);
2501 QCOMPARE(o->actuals().count(), 1);
2502 QCOMPARE(o->actuals().at(0), QVariant(QPointF()));
2505 QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", v8::Undefined()));
2506 QCOMPARE(o->error(), false);
2507 QCOMPARE(o->invoked(), 12);
2508 QCOMPARE(o->actuals().count(), 1);
2509 QCOMPARE(o->actuals().at(0), QVariant(QPointF()));
2512 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", v8::Undefined()));
2513 QCOMPARE(o->error(), false);
2514 QCOMPARE(o->invoked(), 12);
2515 QCOMPARE(o->actuals().count(), 1);
2516 QCOMPARE(o->actuals().at(0), QVariant(QPointF()));
2519 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", v8::Undefined()));
2520 QCOMPARE(o->error(), false);
2521 QCOMPARE(o->invoked(), 12);
2522 QCOMPARE(o->actuals().count(), 1);
2523 QCOMPARE(o->actuals().at(0), QVariant(QPointF(99.3, -10.2)));
2526 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", v8::Undefined()));
2527 QCOMPARE(o->error(), false);
2528 QCOMPARE(o->invoked(), 12);
2529 QCOMPARE(o->actuals().count(), 1);
2530 QCOMPARE(o->actuals().at(0), QVariant(QPointF(9, 12)));
2533 QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", v8::Undefined()));
2534 QCOMPARE(o->error(), false);
2535 QCOMPARE(o->invoked(), 13);
2536 QCOMPARE(o->actuals().count(), 1);
2537 QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)0));
2540 QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", v8::Undefined()));
2541 QCOMPARE(o->error(), false);
2542 QCOMPARE(o->invoked(), 13);
2543 QCOMPARE(o->actuals().count(), 1);
2544 QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)0));
2547 QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", v8::Undefined()));
2548 QCOMPARE(o->error(), false);
2549 QCOMPARE(o->invoked(), 13);
2550 QCOMPARE(o->actuals().count(), 1);
2551 QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)0));
2554 QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", v8::Undefined()));
2555 QCOMPARE(o->error(), false);
2556 QCOMPARE(o->invoked(), 13);
2557 QCOMPARE(o->actuals().count(), 1);
2558 QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)0));
2561 QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", v8::Undefined()));
2562 QCOMPARE(o->error(), false);
2563 QCOMPARE(o->invoked(), 13);
2564 QCOMPARE(o->actuals().count(), 1);
2565 QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)o));
2568 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined()));
2569 QCOMPARE(o->error(), false);
2570 QCOMPARE(o->invoked(), 14);
2571 QCOMPARE(o->actuals().count(), 1);
2572 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(0)).isNull());
2575 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined()));
2576 QCOMPARE(o->error(), false);
2577 QCOMPARE(o->invoked(), 14);
2578 QCOMPARE(o->actuals().count(), 1);
2579 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(0)).isUndefined());
2582 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined()));
2583 QCOMPARE(o->error(), false);
2584 QCOMPARE(o->invoked(), 14);
2585 QCOMPARE(o->actuals().count(), 1);
2586 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(0)).strictlyEquals(QJSValue(19)));
2589 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined()));
2590 QCOMPARE(o->error(), false);
2591 QCOMPARE(o->invoked(), 14);
2592 QCOMPARE(o->actuals().count(), 1);
2593 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(0)).isArray());
2596 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined()));
2597 QCOMPARE(o->error(), false);
2598 QCOMPARE(o->invoked(), 15);
2599 QCOMPARE(o->actuals().count(), 2);
2600 QCOMPARE(o->actuals().at(0), QVariant(4));
2601 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(1)).isNull());
2604 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined()));
2605 QCOMPARE(o->error(), false);
2606 QCOMPARE(o->invoked(), 15);
2607 QCOMPARE(o->actuals().count(), 2);
2608 QCOMPARE(o->actuals().at(0), QVariant(8));
2609 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(1)).isUndefined());
2612 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined()));
2613 QCOMPARE(o->error(), false);
2614 QCOMPARE(o->invoked(), 15);
2615 QCOMPARE(o->actuals().count(), 2);
2616 QCOMPARE(o->actuals().at(0), QVariant(3));
2617 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(1)).strictlyEquals(QJSValue(19)));
2620 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined()));
2621 QCOMPARE(o->error(), false);
2622 QCOMPARE(o->invoked(), 15);
2623 QCOMPARE(o->actuals().count(), 2);
2624 QCOMPARE(o->actuals().at(0), QVariant(44));
2625 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(1)).isArray());
2628 QVERIFY(EVALUATE_ERROR("object.method_overload()"));
2629 QCOMPARE(o->error(), false);
2630 QCOMPARE(o->invoked(), -1);
2631 QCOMPARE(o->actuals().count(), 0);
2634 QVERIFY(EVALUATE_VALUE("object.method_overload(10)", v8::Undefined()));
2635 QCOMPARE(o->error(), false);
2636 QCOMPARE(o->invoked(), 16);
2637 QCOMPARE(o->actuals().count(), 1);
2638 QCOMPARE(o->actuals().at(0), QVariant(10));
2641 QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", v8::Undefined()));
2642 QCOMPARE(o->error(), false);
2643 QCOMPARE(o->invoked(), 17);
2644 QCOMPARE(o->actuals().count(), 2);
2645 QCOMPARE(o->actuals().at(0), QVariant(10));
2646 QCOMPARE(o->actuals().at(1), QVariant(11));
2649 QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", v8::Undefined()));
2650 QCOMPARE(o->error(), false);
2651 QCOMPARE(o->invoked(), 18);
2652 QCOMPARE(o->actuals().count(), 1);
2653 QCOMPARE(o->actuals().at(0), QVariant(QString("Hello")));
2656 QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", v8::Undefined()));
2657 QCOMPARE(o->error(), false);
2658 QCOMPARE(o->invoked(), 19);
2659 QCOMPARE(o->actuals().count(), 1);
2660 QCOMPARE(o->actuals().at(0), QVariant(9));
2663 QVERIFY(EVALUATE_VALUE("object.method_default(10)", v8::Integer::New(19)));
2664 QCOMPARE(o->error(), false);
2665 QCOMPARE(o->invoked(), 20);
2666 QCOMPARE(o->actuals().count(), 2);
2667 QCOMPARE(o->actuals().at(0), QVariant(10));
2668 QCOMPARE(o->actuals().at(1), QVariant(19));
2671 QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", v8::Integer::New(13)));
2672 QCOMPARE(o->error(), false);
2673 QCOMPARE(o->invoked(), 20);
2674 QCOMPARE(o->actuals().count(), 2);
2675 QCOMPARE(o->actuals().at(0), QVariant(10));
2676 QCOMPARE(o->actuals().at(1), QVariant(13));
2679 QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", v8::Undefined()));
2680 QCOMPARE(o->error(), false);
2681 QCOMPARE(o->invoked(), -3);
2682 QCOMPARE(o->actuals().count(), 1);
2683 QCOMPARE(o->actuals().at(0), QVariant(9));
2686 QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", v8::Undefined()));
2687 QCOMPARE(o->error(), false);
2688 QCOMPARE(o->invoked(), 21);
2689 QCOMPARE(o->actuals().count(), 2);
2690 QCOMPARE(o->actuals().at(0), QVariant(9));
2691 QCOMPARE(o->actuals().at(1), QVariant());
2694 QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", v8::Undefined()));
2695 QCOMPARE(o->error(), false);
2696 QCOMPARE(o->invoked(), 21);
2697 QCOMPARE(o->actuals().count(), 2);
2698 QCOMPARE(o->actuals().at(0), QVariant(QString("Hello")));
2699 QCOMPARE(o->actuals().at(1), QVariant(QString("World")));
2702 QVERIFY(EVALUATE_VALUE("object.method_QJsonObject({foo:123})", v8::Undefined()));
2703 QCOMPARE(o->error(), false);
2704 QCOMPARE(o->invoked(), 22);
2705 QCOMPARE(o->actuals().count(), 1);
2706 QCOMPARE(qvariant_cast<QJsonObject>(o->actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2709 QVERIFY(EVALUATE_VALUE("object.method_QJsonArray([123])", v8::Undefined()));
2710 QCOMPARE(o->error(), false);
2711 QCOMPARE(o->invoked(), 23);
2712 QCOMPARE(o->actuals().count(), 1);
2713 QCOMPARE(qvariant_cast<QJsonArray>(o->actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2716 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(123)", v8::Undefined()));
2717 QCOMPARE(o->error(), false);
2718 QCOMPARE(o->invoked(), 24);
2719 QCOMPARE(o->actuals().count(), 1);
2720 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(123));
2723 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(42.35)", v8::Undefined()));
2724 QCOMPARE(o->error(), false);
2725 QCOMPARE(o->invoked(), 24);
2726 QCOMPARE(o->actuals().count(), 1);
2727 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(42.35));
2730 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue('ciao')", v8::Undefined()));
2731 QCOMPARE(o->error(), false);
2732 QCOMPARE(o->invoked(), 24);
2733 QCOMPARE(o->actuals().count(), 1);
2734 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QStringLiteral("ciao")));
2737 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(true)", v8::Undefined()));
2738 QCOMPARE(o->error(), false);
2739 QCOMPARE(o->invoked(), 24);
2740 QCOMPARE(o->actuals().count(), 1);
2741 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(true));
2744 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(false)", v8::Undefined()));
2745 QCOMPARE(o->error(), false);
2746 QCOMPARE(o->invoked(), 24);
2747 QCOMPARE(o->actuals().count(), 1);
2748 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(false));
2751 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(null)", v8::Undefined()));
2752 QCOMPARE(o->error(), false);
2753 QCOMPARE(o->invoked(), 24);
2754 QCOMPARE(o->actuals().count(), 1);
2755 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QJsonValue::Null));
2758 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(undefined)", v8::Undefined()));
2759 QCOMPARE(o->error(), false);
2760 QCOMPARE(o->invoked(), 24);
2761 QCOMPARE(o->actuals().count(), 1);
2762 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2765 QVERIFY(EVALUATE_VALUE("object.method_overload({foo:123})", v8::Undefined()));
2766 QCOMPARE(o->error(), false);
2767 QCOMPARE(o->invoked(), 25);
2768 QCOMPARE(o->actuals().count(), 1);
2769 QCOMPARE(qvariant_cast<QJsonObject>(o->actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2772 QVERIFY(EVALUATE_VALUE("object.method_overload([123])", v8::Undefined()));
2773 QCOMPARE(o->error(), false);
2774 QCOMPARE(o->invoked(), 26);
2775 QCOMPARE(o->actuals().count(), 1);
2776 QCOMPARE(qvariant_cast<QJsonArray>(o->actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2779 QVERIFY(EVALUATE_VALUE("object.method_overload(null)", v8::Undefined()));
2780 QCOMPARE(o->error(), false);
2781 QCOMPARE(o->invoked(), 27);
2782 QCOMPARE(o->actuals().count(), 1);
2783 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QJsonValue::Null));
2786 QVERIFY(EVALUATE_VALUE("object.method_overload(undefined)", v8::Undefined()));
2787 QCOMPARE(o->error(), false);
2788 QCOMPARE(o->invoked(), 27);
2789 QCOMPARE(o->actuals().count(), 1);
2790 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2793 QVERIFY(EVALUATE_ERROR("object.method_unknown(null)"));
2794 QCOMPARE(o->error(), false);
2795 QCOMPARE(o->invoked(), -1);
2796 QCOMPARE(o->actuals().count(), 0);
2799 // QTBUG-13047 (check that you can pass registered object types as args)
2800 void tst_qqmlecmascript::invokableObjectArg()
2802 QQmlComponent component(&engine, testFileUrl("invokableObjectArg.qml"));
2804 QObject *o = component.create();
2806 MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o);
2808 QCOMPARE(qmlobject->myinvokableObject, qmlobject);
2813 // QTBUG-13047 (check that you can return registered object types from methods)
2814 void tst_qqmlecmascript::invokableObjectRet()
2816 QQmlComponent component(&engine, testFileUrl("invokableObjectRet.qml"));
2818 QObject *o = component.create();
2820 QCOMPARE(o->property("test").toBool(), true);
2824 void tst_qqmlecmascript::invokableEnumRet()
2826 QQmlComponent component(&engine, testFileUrl("invokableEnumRet.qml"));
2828 QObject *o = component.create();
2830 QCOMPARE(o->property("test").toBool(), true);
2835 void tst_qqmlecmascript::listToVariant()
2837 QQmlComponent component(&engine, testFileUrl("listToVariant.qml"));
2839 MyQmlContainer container;
2841 QQmlContext context(engine.rootContext());
2842 context.setContextObject(&container);
2844 QObject *object = component.create(&context);
2845 QVERIFY(object != 0);
2847 QVariant v = object->property("test");
2848 QCOMPARE(v.userType(), qMetaTypeId<QQmlListReference>());
2849 QVERIFY(qvariant_cast<QQmlListReference>(v).object() == &container);
2855 Q_DECLARE_METATYPE(QQmlListProperty<MyQmlObject>)
2856 void tst_qqmlecmascript::listAssignment()
2858 QQmlComponent component(&engine, testFileUrl("listAssignment.qml"));
2859 QObject *obj = component.create();
2860 QCOMPARE(obj->property("list1length").toInt(), 2);
2861 QQmlListProperty<MyQmlObject> list1 = obj->property("list1").value<QQmlListProperty<MyQmlObject> >();
2862 QQmlListProperty<MyQmlObject> list2 = obj->property("list2").value<QQmlListProperty<MyQmlObject> >();
2863 QCOMPARE(list1.count(&list1), list2.count(&list2));
2864 QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0));
2865 QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1));
2870 void tst_qqmlecmascript::multiEngineObject()
2873 obj.setStringProperty("Howdy planet");
2876 e1.rootContext()->setContextProperty("thing", &obj);
2877 QQmlComponent c1(&e1, testFileUrl("multiEngineObject.qml"));
2880 e2.rootContext()->setContextProperty("thing", &obj);
2881 QQmlComponent c2(&e2, testFileUrl("multiEngineObject.qml"));
2883 QObject *o1 = c1.create();
2884 QObject *o2 = c2.create();
2886 QCOMPARE(o1->property("test").toString(), QString("Howdy planet"));
2887 QCOMPARE(o2->property("test").toString(), QString("Howdy planet"));
2893 // Test that references to QObjects are cleanup when the object is destroyed
2894 void tst_qqmlecmascript::deletedObject()
2896 QQmlComponent component(&engine, testFileUrl("deletedObject.qml"));
2898 QObject *object = component.create();
2900 QCOMPARE(object->property("test1").toBool(), true);
2901 QCOMPARE(object->property("test2").toBool(), true);
2902 QCOMPARE(object->property("test3").toBool(), true);
2903 QCOMPARE(object->property("test4").toBool(), true);
2908 void tst_qqmlecmascript::attachedPropertyScope()
2910 QQmlComponent component(&engine, testFileUrl("attachedPropertyScope.qml"));
2912 QObject *object = component.create();
2913 QVERIFY(object != 0);
2915 MyQmlAttachedObject *attached =
2916 qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
2917 QVERIFY(attached != 0);
2919 QCOMPARE(object->property("value2").toInt(), 0);
2921 attached->emitMySignal();
2923 QCOMPARE(object->property("value2").toInt(), 9);
2928 void tst_qqmlecmascript::scriptConnect()
2931 QQmlComponent component(&engine, testFileUrl("scriptConnect.1.qml"));
2933 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2934 QVERIFY(object != 0);
2936 QCOMPARE(object->property("test").toBool(), false);
2937 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2938 QCOMPARE(object->property("test").toBool(), true);
2944 QQmlComponent component(&engine, testFileUrl("scriptConnect.2.qml"));
2946 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2947 QVERIFY(object != 0);
2949 QCOMPARE(object->property("test").toBool(), false);
2950 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2951 QCOMPARE(object->property("test").toBool(), true);
2957 QQmlComponent component(&engine, testFileUrl("scriptConnect.3.qml"));
2959 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2960 QVERIFY(object != 0);
2962 QCOMPARE(object->property("test").toBool(), false);
2963 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2964 QCOMPARE(object->property("test").toBool(), true);
2970 QQmlComponent component(&engine, testFileUrl("scriptConnect.4.qml"));
2972 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2973 QVERIFY(object != 0);
2975 QCOMPARE(object->methodCalled(), false);
2976 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2977 QCOMPARE(object->methodCalled(), true);
2983 QQmlComponent component(&engine, testFileUrl("scriptConnect.5.qml"));
2985 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2986 QVERIFY(object != 0);
2988 QCOMPARE(object->methodCalled(), false);
2989 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2990 QCOMPARE(object->methodCalled(), true);
2996 QQmlComponent component(&engine, testFileUrl("scriptConnect.6.qml"));
2998 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2999 QVERIFY(object != 0);
3001 QCOMPARE(object->property("test").toInt(), 0);
3002 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3003 QCOMPARE(object->property("test").toInt(), 2);
3009 void tst_qqmlecmascript::scriptDisconnect()
3012 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.1.qml"));
3014 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3015 QVERIFY(object != 0);
3017 QCOMPARE(object->property("test").toInt(), 0);
3018 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3019 QCOMPARE(object->property("test").toInt(), 1);
3020 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3021 QCOMPARE(object->property("test").toInt(), 2);
3022 emit object->basicSignal();
3023 QCOMPARE(object->property("test").toInt(), 2);
3024 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3025 QCOMPARE(object->property("test").toInt(), 2);
3031 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.2.qml"));
3033 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3034 QVERIFY(object != 0);
3036 QCOMPARE(object->property("test").toInt(), 0);
3037 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3038 QCOMPARE(object->property("test").toInt(), 1);
3039 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3040 QCOMPARE(object->property("test").toInt(), 2);
3041 emit object->basicSignal();
3042 QCOMPARE(object->property("test").toInt(), 2);
3043 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3044 QCOMPARE(object->property("test").toInt(), 2);
3050 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.3.qml"));
3052 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3053 QVERIFY(object != 0);
3055 QCOMPARE(object->property("test").toInt(), 0);
3056 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3057 QCOMPARE(object->property("test").toInt(), 1);
3058 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3059 QCOMPARE(object->property("test").toInt(), 2);
3060 emit object->basicSignal();
3061 QCOMPARE(object->property("test").toInt(), 2);
3062 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3063 QCOMPARE(object->property("test").toInt(), 3);
3068 QQmlComponent component(&engine, testFileUrl("scriptDisconnect.4.qml"));
3070 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3071 QVERIFY(object != 0);
3073 QCOMPARE(object->property("test").toInt(), 0);
3074 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3075 QCOMPARE(object->property("test").toInt(), 1);
3076 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3077 QCOMPARE(object->property("test").toInt(), 2);
3078 emit object->basicSignal();
3079 QCOMPARE(object->property("test").toInt(), 2);
3080 emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3081 QCOMPARE(object->property("test").toInt(), 3);
3087 class OwnershipObject : public QObject
3091 OwnershipObject() { object = new QObject; }
3093 QPointer<QObject> object;
3096 QObject *getObject() { return object; }
3099 void tst_qqmlecmascript::ownership()
3101 OwnershipObject own;
3102 QQmlContext *context = new QQmlContext(engine.rootContext());
3103 context->setContextObject(&own);
3106 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3108 QVERIFY(own.object != 0);
3110 QObject *object = component.create(context);
3112 engine.collectGarbage();
3114 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3115 QCoreApplication::processEvents();
3117 QVERIFY(own.object == 0);
3122 own.object = new QObject(&own);
3125 QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3127 QVERIFY(own.object != 0);
3129 QObject *object = component.create(context);
3131 engine.collectGarbage();
3133 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3134 QCoreApplication::processEvents();
3136 QVERIFY(own.object != 0);
3144 class CppOwnershipReturnValue : public QObject
3148 CppOwnershipReturnValue() : value(0) {}
3149 ~CppOwnershipReturnValue() { delete value; }
3151 Q_INVOKABLE QObject *create() {
3152 value = new QObject;
3153 QQmlEngine::setObjectOwnership(value, QQmlEngine::CppOwnership);
3157 Q_INVOKABLE MyQmlObject *createQmlObject() {
3158 MyQmlObject *rv = new MyQmlObject;
3163 QPointer<QObject> value;
3167 // Test setObjectOwnership(CppOwnership) works even when there is no QQmlData
3168 void tst_qqmlecmascript::cppOwnershipReturnValue()
3170 CppOwnershipReturnValue source;
3174 engine.rootContext()->setContextProperty("source", &source);
3176 QVERIFY(source.value == 0);
3178 QQmlComponent component(&engine);
3179 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl());
3181 QObject *object = component.create();
3183 QVERIFY(object != 0);
3184 QVERIFY(source.value != 0);
3189 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3190 QCoreApplication::processEvents();
3192 QVERIFY(source.value != 0);
3196 void tst_qqmlecmascript::ownershipCustomReturnValue()
3198 CppOwnershipReturnValue source;
3202 engine.rootContext()->setContextProperty("source", &source);
3204 QVERIFY(source.value == 0);
3206 QQmlComponent component(&engine);
3207 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl());
3209 QObject *object = component.create();
3211 QVERIFY(object != 0);
3212 QVERIFY(source.value != 0);
3217 engine.collectGarbage();
3218 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3219 QCoreApplication::processEvents();
3221 QVERIFY(source.value == 0);
3224 //the return value from getObject will be JS ownership,
3225 //unless strong Cpp ownership has been set
3226 class OwnershipChangingObject : public QObject
3230 OwnershipChangingObject(): object(0) { }
3232 QPointer<QObject> object;
3235 QObject *getObject() { return object; }
3236 void setObject(QObject *obj) { object = obj; }
3239 void tst_qqmlecmascript::ownershipRootObject()
3241 OwnershipChangingObject own;
3242 QQmlContext *context = new QQmlContext(engine.rootContext());
3243 context->setContextObject(&own);
3245 QQmlComponent component(&engine, testFileUrl("ownershipRootObject.qml"));
3246 QQmlGuard<QObject> object = component.create(context);
3249 engine.collectGarbage();
3251 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3252 QCoreApplication::processEvents();
3254 QVERIFY(own.object != 0);
3260 void tst_qqmlecmascript::ownershipConsistency()
3262 OwnershipChangingObject own;
3263 QQmlContext *context = new QQmlContext(engine.rootContext());
3264 context->setContextObject(&own);
3266 QString expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":19: Error: Invalid attempt to destroy() an indestructible object");
3267 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3268 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":15: Error: Invalid attempt to destroy() an indestructible object");
3269 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3270 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":6: Error: Invalid attempt to destroy() an indestructible object");
3271 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3272 expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":10: Error: Invalid attempt to destroy() an indestructible object");
3273 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3275 QQmlComponent component(&engine, testFileUrl("ownershipConsistency.qml"));
3276 QQmlGuard<QObject> object = component.create(context);
3279 engine.collectGarbage();
3281 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3282 QCoreApplication::processEvents();
3284 QVERIFY(own.object != 0);
3290 void tst_qqmlecmascript::ownershipQmlIncubated()
3292 QQmlComponent component(&engine, testFileUrl("ownershipQmlIncubated.qml"));
3293 QObject *object = component.create();
3296 QTRY_VERIFY(object->property("incubatedItem").value<QObject*>() != 0);
3298 QMetaObject::invokeMethod(object, "deleteIncubatedItem");
3300 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3301 QCoreApplication::processEvents();
3303 QVERIFY(object->property("incubatedItem").value<QObject*>() == 0);
3308 class QListQObjectMethodsObject : public QObject
3312 QListQObjectMethodsObject() {
3313 m_objects.append(new MyQmlObject());
3314 m_objects.append(new MyQmlObject());
3317 ~QListQObjectMethodsObject() {
3318 qDeleteAll(m_objects);
3322 QList<QObject *> getObjects() { return m_objects; }
3325 QList<QObject *> m_objects;
3328 // Tests that returning a QList<QObject*> from a method works
3329 void tst_qqmlecmascript::qlistqobjectMethods()
3331 QListQObjectMethodsObject obj;
3332 QQmlContext *context = new QQmlContext(engine.rootContext());
3333 context->setContextObject(&obj);
3335 QQmlComponent component(&engine, testFileUrl("qlistqobjectMethods.qml"));
3337 QObject *object = component.create(context);
3339 QCOMPARE(object->property("test").toInt(), 2);
3340 QCOMPARE(object->property("test2").toBool(), true);
3347 void tst_qqmlecmascript::strictlyEquals()
3349 QQmlComponent component(&engine, testFileUrl("strictlyEquals.qml"));
3351 QObject *object = component.create();
3352 QVERIFY(object != 0);
3354 QCOMPARE(object->property("test1").toBool(), true);
3355 QCOMPARE(object->property("test2").toBool(), true);
3356 QCOMPARE(object->property("test3").toBool(), true);
3357 QCOMPARE(object->property("test4").toBool(), true);
3358 QCOMPARE(object->property("test5").toBool(), true);
3359 QCOMPARE(object->property("test6").toBool(), true);
3360 QCOMPARE(object->property("test7").toBool(), true);
3361 QCOMPARE(object->property("test8").toBool(), true);
3366 void tst_qqmlecmascript::compiled()
3368 QQmlComponent component(&engine, testFileUrl("compiled.qml"));
3370 QObject *object = component.create();
3371 QVERIFY(object != 0);
3373 QCOMPARE(object->property("test1").toReal(), qreal(15.7));
3374 QCOMPARE(object->property("test2").toReal(), qreal(-6.7));
3375 QCOMPARE(object->property("test3").toBool(), true);
3376 QCOMPARE(object->property("test4").toBool(), false);
3377 QCOMPARE(object->property("test5").toBool(), false);
3378 QCOMPARE(object->property("test6").toBool(), true);
3380 QCOMPARE(object->property("test7").toInt(), 185);
3381 QCOMPARE(object->property("test8").toInt(), 167);
3382 QCOMPARE(object->property("test9").toBool(), true);
3383 QCOMPARE(object->property("test10").toBool(), false);
3384 QCOMPARE(object->property("test11").toBool(), false);
3385 QCOMPARE(object->property("test12").toBool(), true);
3387 QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld"));
3388 QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World"));
3389 QCOMPARE(object->property("test15").toBool(), false);
3390 QCOMPARE(object->property("test16").toBool(), true);
3392 QCOMPARE(object->property("test17").toInt(), 5);
3393 QCOMPARE(object->property("test18").toReal(), qreal(176));
3394 QCOMPARE(object->property("test19").toInt(), 7);
3395 QCOMPARE(object->property("test20").toReal(), qreal(6.7));
3396 QCOMPARE(object->property("test21").toString(), QLatin1String("6.7"));
3397 QCOMPARE(object->property("test22").toString(), QLatin1String("!"));
3398 QCOMPARE(object->property("test23").toBool(), true);
3399 QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33));
3400 QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA));
3405 // Test that numbers assigned in bindings as strings work consistently
3406 void tst_qqmlecmascript::numberAssignment()
3408 QQmlComponent component(&engine, testFileUrl("numberAssignment.qml"));
3410 QObject *object = component.create();
3411 QVERIFY(object != 0);
3413 QCOMPARE(object->property("test1"), QVariant((qreal)6.7));
3414 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3415 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3416 QCOMPARE(object->property("test3"), QVariant((qreal)6));
3417 QCOMPARE(object->property("test4"), QVariant((qreal)6));
3419 QCOMPARE(object->property("test5"), QVariant((int)7));
3420 QCOMPARE(object->property("test6"), QVariant((int)7));
3421 QCOMPARE(object->property("test7"), QVariant((int)6));
3422 QCOMPARE(object->property("test8"), QVariant((int)6));
3424 QCOMPARE(object->property("test9"), QVariant((unsigned int)7));
3425 QCOMPARE(object->property("test10"), QVariant((unsigned int)7));
3426 QCOMPARE(object->property("test11"), QVariant((unsigned int)6));
3427 QCOMPARE(object->property("test12"), QVariant((unsigned int)6));
3432 void tst_qqmlecmascript::propertySplicing()
3434 QQmlComponent component(&engine, testFileUrl("propertySplicing.qml"));
3436 QObject *object = component.create();
3437 QVERIFY(object != 0);
3439 QCOMPARE(object->property("test").toBool(), true);
3445 void tst_qqmlecmascript::signalWithUnknownTypes()
3447 QQmlComponent component(&engine, testFileUrl("signalWithUnknownTypes.qml"));
3449 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3450 QVERIFY(object != 0);
3452 MyQmlObject::MyType type;
3453 type.value = 0x8971123;
3454 emit object->signalWithUnknownType(type);
3456 MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant());
3458 QCOMPARE(result.value, type.value);
3460 MyQmlObject::MyOtherType othertype;
3461 othertype.value = 17;
3462 emit object->signalWithCompletelyUnknownType(othertype);
3464 QVERIFY(!object->variant().isValid());
3469 void tst_qqmlecmascript::signalWithJSValueInVariant_data()
3471 QTest::addColumn<QString>("expression");
3472 QTest::addColumn<QString>("compare");
3474 QString compareStrict("(function(a, b) { return a === b; })");
3475 QTest::newRow("true") << "true" << compareStrict;
3476 QTest::newRow("undefined") << "undefined" << compareStrict;
3477 QTest::newRow("null") << "null" << compareStrict;
3478 QTest::newRow("123") << "123" << compareStrict;
3479 QTest::newRow("'ciao'") << "'ciao'" << compareStrict;
3481 QString comparePropertiesStrict(
3483 " if (typeof b != 'object')"
3485 " var props = Object.getOwnPropertyNames(b);"
3486 " for (var i = 0; i < props.length; ++i) {"
3487 " var p = props[i];"
3488 " return arguments.callee(a[p], b[p]);"
3491 QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })" << comparePropertiesStrict;
3492 QTest::newRow("[10,20,30]") << "[10,20,30]" << comparePropertiesStrict;
3495 void tst_qqmlecmascript::signalWithJSValueInVariant()
3497 QFETCH(QString, expression);
3498 QFETCH(QString, compare);
3500 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3501 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3502 QVERIFY(object != 0);
3504 QJSValue value = engine.evaluate(expression);
3505 QVERIFY(!value.isError());
3506 object->setProperty("expression", expression);
3507 object->setProperty("compare", compare);
3508 object->setProperty("pass", false);
3510 emit object->signalWithVariant(QVariant::fromValue(value));
3511 QVERIFY(object->property("pass").toBool());
3514 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines_data()
3516 signalWithJSValueInVariant_data();
3519 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines()
3521 QFETCH(QString, expression);
3522 QFETCH(QString, compare);
3524 QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3525 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3526 QVERIFY(object != 0);
3529 QJSValue value = engine2.evaluate(expression);
3530 QVERIFY(!value.isError());
3531 object->setProperty("expression", expression);
3532 object->setProperty("compare", compare);
3533 object->setProperty("pass", false);
3535 QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine.");
3536 emit object->signalWithVariant(QVariant::fromValue(value));
3537 QVERIFY(!object->property("pass").toBool());
3540 void tst_qqmlecmascript::signalWithQJSValue_data()
3542 signalWithJSValueInVariant_data();
3545 void tst_qqmlecmascript::signalWithQJSValue()
3547 QFETCH(QString, expression);
3548 QFETCH(QString, compare);
3550 QQmlComponent component(&engine, testFileUrl("signalWithQJSValue.qml"));
3551 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3552 QVERIFY(object != 0);
3554 QJSValue value = engine.evaluate(expression);
3555 QVERIFY(!value.isError());
3556 object->setProperty("expression", expression);
3557 object->setProperty("compare", compare);
3558 object->setProperty("pass", false);
3560 emit object->signalWithQJSValue(value);
3562 QVERIFY(object->property("pass").toBool());
3563 QVERIFY(object->qjsvalue().strictlyEquals(value));
3566 void tst_qqmlecmascript::singletonType_data()
3568 QTest::addColumn<QUrl>("testfile");
3569 QTest::addColumn<QString>("errorMessage");
3570 QTest::addColumn<QStringList>("warningMessages");
3571 QTest::addColumn<QStringList>("readProperties");
3572 QTest::addColumn<QVariantList>("readExpectedValues");
3573 QTest::addColumn<QStringList>("writeProperties");
3574 QTest::addColumn<QVariantList>("writeValues");
3575 QTest::addColumn<QStringList>("readBackProperties");
3576 QTest::addColumn<QVariantList>("readBackExpectedValues");
3578 QTest::newRow("qobject, register + read + method [no qualifier]")
3579 << testFileUrl("singletontype/qobjectSingletonTypeNoQualifier.qml")
3582 << (QStringList() << "qobjectPropertyTest" << "qobjectMethodTest")
3583 << (QVariantList() << 20 << 1)
3589 QTest::newRow("script, register + read [no qualifier]")
3590 << testFileUrl("singletontype/scriptSingletonTypeNoQualifier.qml")
3593 << (QStringList() << "scriptTest")
3594 << (QVariantList() << 13)
3600 QTest::newRow("qobject, register + read + method")
3601 << testFileUrl("singletontype/qobjectSingletonType.qml")
3604 << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
3605 << "qobjectMinorVersionMethodTest" << "qobjectMinorVersionTest"
3606 << "qobjectMajorVersionTest" << "qobjectParentedTest")
3607 << (QVariantList() << 20 << 20 << 2 << 1 << 20 << 20 << 26)
3613 QTest::newRow("script, register + read")
3614 << testFileUrl("singletontype/scriptSingletonType.qml")
3617 << (QStringList() << "scriptTest")
3618 << (QVariantList() << 14) // will have incremented, since we create a new engine each row in this test.
3624 QTest::newRow("qobject, writing + readonly constraints")
3625 << testFileUrl("singletontype/qobjectSingletonTypeWriting.qml")
3627 << (QStringList() << QString(testFileUrl("singletontype/qobjectSingletonTypeWriting.qml").toString() + QLatin1String(":15: Error: Cannot assign to read-only property \"qobjectTestProperty\"")))
3628 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3629 << (QVariantList() << 20 << 50 << 10)
3630 << (QStringList() << "firstProperty" << "secondProperty")
3631 << (QVariantList() << 30 << 30)
3632 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3633 << (QVariantList() << 20 << 30 << 30);
3635 QTest::newRow("script, writing + readonly constraints")
3636 << testFileUrl("singletontype/scriptSingletonTypeWriting.qml")
3638 << (QStringList() << QString(testFileUrl("singletontype/scriptSingletonTypeWriting.qml").toString() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\"")))
3639 << (QStringList() << "readBack" << "unchanged")
3640 << (QVariantList() << 15 << 42)
3641 << (QStringList() << "firstProperty" << "secondProperty")
3642 << (QVariantList() << 30 << 30)
3643 << (QStringList() << "readBack" << "unchanged")
3644 << (QVariantList() << 30 << 42);
3646 QTest::newRow("qobject singleton Type enum values in JS")
3647 << testFileUrl("singletontype/qobjectSingletonTypeEnums.qml")
3650 << (QStringList() << "enumValue" << "enumMethod")
3651 << (QVariantList() << 42 << 30)
3657 QTest::newRow("qobject, invalid major version fail")
3658 << testFileUrl("singletontype/singletonTypeMajorVersionFail.qml")
3659 << QString("QQmlComponent: Component is not ready")
3668 QTest::newRow("qobject, invalid minor version fail")
3669 << testFileUrl("singletontype/singletonTypeMinorVersionFail.qml")
3670 << QString("QQmlComponent: Component is not ready")
3679 QTest::newRow("qobject, multiple in namespace")
3680 << testFileUrl("singletontype/singletonTypeMultiple.qml")
3683 << (QStringList() << "first" << "second")
3684 << (QVariantList() << 35 << 42)
3691 void tst_qqmlecmascript::singletonType()
3693 QFETCH(QUrl, testfile);
3694 QFETCH(QString, errorMessage);
3695 QFETCH(QStringList, warningMessages);
3696 QFETCH(QStringList, readProperties);
3697 QFETCH(QVariantList, readExpectedValues);
3698 QFETCH(QStringList, writeProperties);
3699 QFETCH(QVariantList, writeValues);
3700 QFETCH(QStringList, readBackProperties);
3701 QFETCH(QVariantList, readBackExpectedValues);
3703 QQmlEngine cleanEngine; // so tests don't interfere which each other, as singleton types are engine-singletons only.
3704 QQmlComponent component(&cleanEngine, testfile);
3706 if (!errorMessage.isEmpty())
3707 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3709 if (warningMessages.size())
3710 foreach (const QString &warning, warningMessages)
3711 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3713 QObject *object = component.create();
3714 if (!errorMessage.isEmpty()) {
3715 QVERIFY(object == 0);
3717 QVERIFY(object != 0);
3718 for (int i = 0; i < readProperties.size(); ++i)
3719 QCOMPARE(object->property(readProperties.at(i).toLatin1().constData()), readExpectedValues.at(i));
3720 for (int i = 0; i < writeProperties.size(); ++i)
3721 QVERIFY(object->setProperty(writeProperties.at(i).toLatin1().constData(), writeValues.at(i)));
3722 for (int i = 0; i < readBackProperties.size(); ++i)
3723 QCOMPARE(object->property(readBackProperties.at(i).toLatin1().constData()), readBackExpectedValues.at(i));
3728 void tst_qqmlecmascript::singletonTypeCaching_data()
3730 QTest::addColumn<QUrl>("testfile");
3731 QTest::addColumn<QStringList>("readProperties");
3733 QTest::newRow("qobject, caching + read")
3734 << testFileUrl("singletontype/qobjectSingletonTypeCaching.qml")
3735 << (QStringList() << "existingUriTest" << "qobjectParentedTest");
3737 QTest::newRow("script, caching + read")
3738 << testFileUrl("singletontype/scriptSingletonTypeCaching.qml")
3739 << (QStringList() << "scriptTest");
3742 void tst_qqmlecmascript::singletonTypeCaching()
3744 QFETCH(QUrl, testfile);
3745 QFETCH(QStringList, readProperties);
3747 // ensure that the singleton type instances are cached per-engine.
3749 QQmlEngine cleanEngine;
3750 QQmlComponent component(&cleanEngine, testfile);
3751 QObject *object = component.create();
3752 QVERIFY(object != 0);
3753 QList<QVariant> firstValues;
3754 QMetaObject::invokeMethod(object, "modifyValues");
3755 for (int i = 0; i < readProperties.size(); ++i)
3756 firstValues << object->property(readProperties.at(i).toLatin1().constData());
3759 QQmlComponent component2(&cleanEngine, testfile);
3760 QObject *object2 = component2.create();
3761 QVERIFY(object2 != 0);
3762 for (int i = 0; i < readProperties.size(); ++i)
3763 QCOMPARE(object2->property(readProperties.at(i).toLatin1().constData()), firstValues.at(i)); // cached, shouldn't have changed.
3767 void tst_qqmlecmascript::singletonTypeImportOrder()
3769 QQmlComponent component(&engine, testFileUrl("singletontype/singletonTypeImportOrder.qml"));
3770 QObject *object = component.create();
3772 QVERIFY(object->property("v") == 1);
3776 void tst_qqmlecmascript::singletonTypeResolution()
3778 QQmlComponent component(&engine, testFileUrl("singletontype/singletonTypeResolution.qml"));
3779 QObject *object = component.create();
3781 QVERIFY(object->property("success") == true);
3785 void tst_qqmlecmascript::importScripts_data()
3787 QTest::addColumn<QUrl>("testfile");
3788 QTest::addColumn<QString>("errorMessage");
3789 QTest::addColumn<QStringList>("warningMessages");
3790 QTest::addColumn<QStringList>("propertyNames");
3791 QTest::addColumn<QVariantList>("propertyValues");
3793 QTest::newRow("basic functionality")
3794 << testFileUrl("jsimport/testImport.qml")
3797 << (QStringList() << QLatin1String("importedScriptStringValue")
3798 << QLatin1String("importedScriptFunctionValue")
3799 << QLatin1String("importedModuleAttachedPropertyValue")
3800 << QLatin1String("importedModuleEnumValue"))
3801 << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3806 QTest::newRow("import scoping")
3807 << testFileUrl("jsimport/testImportScoping.qml")
3810 << (QStringList() << QLatin1String("componentError"))
3811 << (QVariantList() << QVariant(5));
3813 QTest::newRow("parent scope shouldn't be inherited by import with imports")
3814 << testFileUrl("jsimportfail/failOne.qml")
3816 << (QStringList() << QString(testFileUrl("jsimportfail/failOne.qml").toString() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
3817 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3818 << (QVariantList() << QVariant(QString()));
3820 QTest::newRow("javascript imports in an import should be private to the import scope")
3821 << testFileUrl("jsimportfail/failTwo.qml")
3823 << (QStringList() << QString(testFileUrl("jsimportfail/failTwo.qml").toString() + QLatin1String(":6: ReferenceError: ImportOneJs is not defined")))
3824 << (QStringList() << QLatin1String("importScriptFunctionValue"))
3825 << (QVariantList() << QVariant(QString()));
3827 QTest::newRow("module imports in an import should be private to the import scope")
3828 << testFileUrl("jsimportfail/failThree.qml")
3830 << (QStringList() << QString(testFileUrl("jsimportfail/failThree.qml").toString() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
3831 << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
3832 << (QVariantList() << QVariant(false));
3834 QTest::newRow("typenames in an import should be private to the import scope")
3835 << testFileUrl("jsimportfail/failFour.qml")
3837 << (QStringList() << QString(testFileUrl("jsimportfail/failFour.qml").toString() + QLatin1String(":6: ReferenceError: JsQtTest is not defined")))
3838 << (QStringList() << QLatin1String("importedModuleEnumValue"))
3839 << (QVariantList() << QVariant(0));
3841 QTest::newRow("import with imports has it's own activation scope")
3842 << testFileUrl("jsimportfail/failFive.qml")
3844 << (QStringList() << QString(testFileUrl("jsimportfail/importWithImports.js").toString() + QLatin1String(":8: ReferenceError: Component is not defined")))
3845 << (QStringList() << QLatin1String("componentError"))
3846 << (QVariantList() << QVariant(0));
3848 QTest::newRow("import pragma library script")
3849 << testFileUrl("jsimport/testImportPragmaLibrary.qml")
3852 << (QStringList() << QLatin1String("testValue"))
3853 << (QVariantList() << QVariant(31));
3855 QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3856 << testFileUrl("jsimportfail/testImportPragmaLibrary.qml")
3858 << (QStringList() << QString(testFileUrl("jsimportfail/importPragmaLibrary.js").toString() + QLatin1String(":6: ReferenceError: Component is not defined")))
3859 << (QStringList() << QLatin1String("testValue"))
3860 << (QVariantList() << QVariant(0));
3862 QTest::newRow("import pragma library script which has an import")
3863 << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
3866 << (QStringList() << QLatin1String("testValue"))
3867 << (QVariantList() << QVariant(55));
3869 QTest::newRow("import pragma library script which has a pragma library import")
3870 << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3873 << (QStringList() << QLatin1String("testValue"))
3874 << (QVariantList() << QVariant(18));
3876 QTest::newRow("import singleton type into js import")
3877 << testFileUrl("jsimport/testImportSingletonType.qml")
3880 << (QStringList() << QLatin1String("testValue"))
3881 << (QVariantList() << QVariant(20));
3883 QTest::newRow("import module which exports a script")
3884 << testFileUrl("jsimport/testJsImport.qml")
3887 << (QStringList() << QLatin1String("importedScriptStringValue")
3888 << QLatin1String("renamedScriptStringValue")
3889 << QLatin1String("reimportedScriptStringValue"))
3890 << (QVariantList() << QVariant(QString("Hello"))
3891 << QVariant(QString("Hello"))
3892 << QVariant(QString("Hello")));
3894 QTest::newRow("import module which exports a script which imports a remote module")
3895 << testFileUrl("jsimport/testJsRemoteImport.qml")
3898 << (QStringList() << QLatin1String("importedScriptStringValue")
3899 << QLatin1String("renamedScriptStringValue")
3900 << QLatin1String("reimportedScriptStringValue"))
3901 << (QVariantList() << QVariant(QString("Hello"))
3902 << QVariant(QString("Hello"))
3903 << QVariant(QString("Hello")));
3905 QTest::newRow("malformed import statement")
3906 << testFileUrl("jsimportfail/malformedImport.qml")
3908 << (QStringList() << testFileUrl("jsimportfail/malformedImport.js").toString() + QLatin1String(":1: SyntaxError: Unexpected token ."))
3912 QTest::newRow("malformed file name")
3913 << testFileUrl("jsimportfail/malformedFile.qml")
3915 << (QStringList() << testFileUrl("jsimportfail/malformedFile.js").toString() + QLatin1String(":1:9: Imported file must be a script"))
3919 QTest::newRow("missing file qualifier")
3920 << testFileUrl("jsimportfail/missingFileQualifier.qml")
3922 << (QStringList() << testFileUrl("jsimportfail/missingFileQualifier.js").toString() + QLatin1String(":1:1: File import requires a qualifier"))
3926 QTest::newRow("malformed file qualifier")
3927 << testFileUrl("jsimportfail/malformedFileQualifier.qml")
3929 << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.js").toString() + QLatin1String(":1:20: File import requires a qualifier"))
3933 QTest::newRow("malformed module qualifier 2")
3934 << testFileUrl("jsimportfail/malformedFileQualifier.2.qml")
3936 << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.2.js").toString() + QLatin1String(":1:1: Invalid import qualifier"))
3940 QTest::newRow("malformed module uri")
3941 << testFileUrl("jsimportfail/malformedModule.qml")
3943 << (QStringList() << testFileUrl("jsimportfail/malformedModule.js").toString() + QLatin1String(":1:17: Invalid module URI"))
3947 QTest::newRow("missing module version")
3948 << testFileUrl("jsimportfail/missingModuleVersion.qml")
3950 << (QStringList() << testFileUrl("jsimportfail/missingModuleVersion.js").toString() + QLatin1String(":1:17: Module import requires a version"))
3954 QTest::newRow("malformed module version")
3955 << testFileUrl("jsimportfail/malformedModuleVersion.qml")
3957 << (QStringList() << testFileUrl("jsimportfail/malformedModuleVersion.js").toString() + QLatin1String(":1:17: Module import requires a version"))
3961 QTest::newRow("missing module qualifier")
3962 << testFileUrl("jsimportfail/missingModuleQualifier.qml")
3964 << (QStringList() << testFileUrl("jsimportfail/missingModuleQualifier.js").toString() + QLatin1String(":1:1: Module import requires a qualifier"))
3968 QTest::newRow("malformed module qualifier")
3969 << testFileUrl("jsimportfail/malformedModuleQualifier.qml")
3971 << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.js").toString() + QLatin1String(":1:21: Module import requires a qualifier"))
3975 QTest::newRow("malformed module qualifier 2")
3976 << testFileUrl("jsimportfail/malformedModuleQualifier.2.qml")
3978 << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.2.js").toString() + QLatin1String(":1:1: Invalid import qualifier"))
3983 void tst_qqmlecmascript::importScripts()
3985 QFETCH(QUrl, testfile);
3986 QFETCH(QString, errorMessage);
3987 QFETCH(QStringList, warningMessages);
3988 QFETCH(QStringList, propertyNames);
3989 QFETCH(QVariantList, propertyValues);
3991 TestHTTPServer server(8111);
3992 QVERIFY(server.isValid());
3993 server.serveDirectory(dataDirectory() + "/remote");
3995 QStringList importPathList = engine.importPathList();
3997 QString remotePath(QLatin1String("http://127.0.0.1:8111/"));
3998 engine.addImportPath(remotePath);
4000 QQmlComponent component(&engine, testfile);
4002 if (!errorMessage.isEmpty())
4003 QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
4005 if (warningMessages.size())
4006 foreach (const QString &warning, warningMessages)
4007 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
4009 QTRY_VERIFY(component.isReady());
4011 QObject *object = component.create();
4012 if (!errorMessage.isEmpty()) {
4013 QVERIFY(object == 0);
4015 QVERIFY(object != 0);
4016 for (int i = 0; i < propertyNames.size(); ++i)
4017 QCOMPARE(object->property(propertyNames.at(i).toLatin1().constData()), propertyValues.at(i));
4021 engine.setImportPathList(importPathList);
4024 void tst_qqmlecmascript::scarceResources_other()
4026 /* These tests require knowledge of state, since we test values after
4027 performing signal or function invocation. */
4029 QPixmap origPixmap(100, 100);
4030 origPixmap.fill(Qt::blue);
4031 QString srp_name, expectedWarning;
4032 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4033 ScarceResourceObject *eo = 0;
4035 QObject *object = 0;
4037 /* property var semantics */
4039 // test that scarce resources are handled properly in signal invocation
4040 QQmlComponent varComponentTen(&engine, testFileUrl("scarceResourceSignal.var.qml"));
4041 object = varComponentTen.create();
4042 srsc = object->findChild<QObject*>("srsc");
4044 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
4045 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
4046 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4047 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4048 QMetaObject::invokeMethod(srsc, "testSignal");
4049 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
4050 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
4051 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4052 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
4053 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
4054 QVERIFY(srsc->property("scarceResourceCopy").isValid());
4055 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4056 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4057 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
4058 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4061 // test that scarce resources are handled properly from js functions in qml files
4062 QQmlComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml"));
4063 object = varComponentEleven.create();
4064 QVERIFY(object != 0);
4065 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4066 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4067 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4068 QMetaObject::invokeMethod(object, "retrieveScarceResource");
4069 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
4070 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4071 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4072 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
4073 QMetaObject::invokeMethod(object, "releaseScarceResource");
4074 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
4075 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4076 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4077 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4080 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
4081 QQmlComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
4082 object = varComponentTwelve.create();
4083 QVERIFY(object != 0);
4084 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4085 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4086 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4087 srp_name = object->property("srp_name").toString();
4088 expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
4089 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
4090 QMetaObject::invokeMethod(object, "retrieveScarceResource");
4091 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
4092 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4093 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4094 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4097 // test that if an Item which has JS ownership but has a scarce resource property is garbage collected,
4098 // that the scarce resource is removed from the engine's list of scarce resources to clean up.
4099 QQmlComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml"));
4100 object = varComponentThirteen.create();
4101 QVERIFY(object != 0);
4102 QVERIFY(!object->property("varProperty").isValid()); // not assigned yet
4103 QMetaObject::invokeMethod(object, "assignVarProperty");
4104 QVERIFY(ep->scarceResources.isEmpty()); // the scarce resource is a VME property.
4105 QMetaObject::invokeMethod(object, "deassignVarProperty");
4106 QVERIFY(ep->scarceResources.isEmpty()); // should still be empty; the resource should have been released on gc.
4109 /* property variant semantics */
4111 // test that scarce resources are handled properly in signal invocation
4112 QQmlComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml"));
4113 object = variantComponentTen.create();
4114 QVERIFY(object != 0);
4115 srsc = object->findChild<QObject*>("srsc");
4117 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
4118 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
4119 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4120 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4121 QMetaObject::invokeMethod(srsc, "testSignal");
4122 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
4123 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
4124 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4125 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
4126 QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
4127 QVERIFY(srsc->property("scarceResourceCopy").isValid());
4128 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4129 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4130 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
4131 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4134 // test that scarce resources are handled properly from js functions in qml files
4135 QQmlComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml"));
4136 object = variantComponentEleven.create();
4137 QVERIFY(object != 0);
4138 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4139 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4140 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4141 QMetaObject::invokeMethod(object, "retrieveScarceResource");
4142 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
4143 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4144 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4145 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
4146 QMetaObject::invokeMethod(object, "releaseScarceResource");
4147 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
4148 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4149 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4150 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4153 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
4154 QQmlComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml"));
4155 object = variantComponentTwelve.create();
4156 QVERIFY(object != 0);
4157 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4158 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4159 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4160 srp_name = object->property("srp_name").toString();
4161 expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
4162 QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
4163 QMetaObject::invokeMethod(object, "retrieveScarceResource");
4164 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
4165 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4166 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4167 QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4171 void tst_qqmlecmascript::scarceResources_data()
4173 QTest::addColumn<QUrl>("qmlFile");
4174 QTest::addColumn<bool>("readDetachStatus");
4175 QTest::addColumn<bool>("expectedDetachStatus");
4176 QTest::addColumn<QStringList>("propertyNames");
4177 QTest::addColumn<QVariantList>("expectedValidity");
4178 QTest::addColumn<QVariantList>("expectedValues");
4179 QTest::addColumn<QStringList>("expectedErrors");
4181 QPixmap origPixmap(100, 100);
4182 origPixmap.fill(Qt::blue);
4184 /* property var semantics */
4186 // in the following three cases, the instance created from the component
4187 // has a property which is a copy of the scarce resource; hence, the
4188 // resource should NOT be detached prior to deletion of the object instance,
4189 // unless the resource is destroyed explicitly.
4190 QTest::newRow("var: import scarce resource copy directly")
4191 << testFileUrl("scarceResourceCopy.var.qml")
4193 << false // won't be detached, because assigned to property and not explicitly released
4194 << (QStringList() << QLatin1String("scarceResourceCopy"))
4195 << (QList<QVariant>() << true)
4196 << (QList<QVariant>() << origPixmap)
4199 QTest::newRow("var: import scarce resource copy from JS")
4200 << testFileUrl("scarceResourceCopyFromJs.var.qml")
4202 << false // won't be detached, because assigned to property and not explicitly released
4203 << (QStringList() << QLatin1String("scarceResourceCopy"))
4204 << (QList<QVariant>() << true)
4205 << (QList<QVariant>() << origPixmap)
4208 QTest::newRow("var: import released scarce resource copy from JS")
4209 << testFileUrl("scarceResourceDestroyedCopy.var.qml")
4211 << true // explicitly released, so it will be detached
4212 << (QStringList() << QLatin1String("scarceResourceCopy"))
4213 << (QList<QVariant>() << false)
4214 << (QList<QVariant>() << QVariant())
4217 // in the following three cases, no other copy should exist in memory,
4218 // and so it should be detached (unless explicitly preserved).
4219 QTest::newRow("var: import auto-release SR from JS in binding side-effect")
4220 << testFileUrl("scarceResourceTest.var.qml")
4222 << true // auto released, so it will be detached
4223 << (QStringList() << QLatin1String("scarceResourceTest"))
4224 << (QList<QVariant>() << true)
4225 << (QList<QVariant>() << QVariant(100))
4227 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4228 << testFileUrl("scarceResourceTestPreserve.var.qml")
4230 << false // won't be detached because we explicitly preserve it
4231 << (QStringList() << QLatin1String("scarceResourceTest"))
4232 << (QList<QVariant>() << true)
4233 << (QList<QVariant>() << QVariant(100))
4235 QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4236 << testFileUrl("scarceResourceTestMultiple.var.qml")
4238 << true // will be detached because all resources were released manually or automatically.
4239 << (QStringList() << QLatin1String("scarceResourceTest"))
4240 << (QList<QVariant>() << true)
4241 << (QList<QVariant>() << QVariant(100))
4244 // In the following three cases, test that scarce resources are handled
4245 // correctly for imports.
4246 QTest::newRow("var: import with no binding")
4247 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4248 << false // cannot check detach status.
4251 << QList<QVariant>()
4252 << QList<QVariant>()
4254 QTest::newRow("var: import with binding without explicit preserve")
4255 << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4258 << (QStringList() << QLatin1String("scarceResourceCopy"))
4259 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4260 << (QList<QVariant>() << QVariant())
4262 QTest::newRow("var: import with explicit release after binding evaluation")
4263 << testFileUrl("scarceResourceCopyImport.var.qml")
4266 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4267 << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated.
4268 << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true))
4270 QTest::newRow("var: import with different js objects")
4271 << testFileUrl("scarceResourceCopyImportDifferent.var.qml")
4274 << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4275 << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4276 << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false))
4278 QTest::newRow("var: import with different js objects and explicit release")
4279 << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml")
4282 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4283 << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4284 << (QList<QVariant>() << QVariant(origPixmap) << QVariant())
4286 QTest::newRow("var: import with same js objects and explicit release")
4287 << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml")
4290 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4291 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4292 << (QList<QVariant>() << QVariant() << QVariant())
4294 QTest::newRow("var: binding with same js objects and explicit release")
4295 << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml")
4298 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4299 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4300 << (QList<QVariant>() << QVariant() << QVariant())
4304 /* property variant semantics */
4306 // in the following three cases, the instance created from the component
4307 // has a property which is a copy of the scarce resource; hence, the
4308 // resource should NOT be detached prior to deletion of the object instance,
4309 // unless the resource is destroyed explicitly.
4310 QTest::newRow("variant: import scarce resource copy directly")
4311 << testFileUrl("scarceResourceCopy.variant.qml")
4313 << false // won't be detached, because assigned to property and not explicitly released
4314 << (QStringList() << QLatin1String("scarceResourceCopy"))
4315 << (QList<QVariant>() << true)
4316 << (QList<QVariant>() << origPixmap)
4319 QTest::newRow("variant: import scarce resource copy from JS")
4320 << testFileUrl("scarceResourceCopyFromJs.variant.qml")
4322 << false // won't be detached, because assigned to property and not explicitly released
4323 << (QStringList() << QLatin1String("scarceResourceCopy"))
4324 << (QList<QVariant>() << true)
4325 << (QList<QVariant>() << origPixmap)
4328 QTest::newRow("variant: import released scarce resource copy from JS")
4329 << testFileUrl("scarceResourceDestroyedCopy.variant.qml")
4331 << true // explicitly released, so it will be detached
4332 << (QStringList() << QLatin1String("scarceResourceCopy"))
4333 << (QList<QVariant>() << false)
4334 << (QList<QVariant>() << QVariant())
4337 // in the following three cases, no other copy should exist in memory,
4338 // and so it should be detached (unless explicitly preserved).
4339 QTest::newRow("variant: import auto-release SR from JS in binding side-effect")
4340 << testFileUrl("scarceResourceTest.variant.qml")
4342 << true // auto released, so it will be detached
4343 << (QStringList() << QLatin1String("scarceResourceTest"))
4344 << (QList<QVariant>() << true)
4345 << (QList<QVariant>() << QVariant(100))
4347 QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect")
4348 << testFileUrl("scarceResourceTestPreserve.variant.qml")
4350 << false // won't be detached because we explicitly preserve it
4351 << (QStringList() << QLatin1String("scarceResourceTest"))
4352 << (QList<QVariant>() << true)
4353 << (QList<QVariant>() << QVariant(100))
4355 QTest::newRow("variant: import multiple scarce resources")
4356 << testFileUrl("scarceResourceTestMultiple.variant.qml")
4358 << true // will be detached because all resources were released manually or automatically.
4359 << (QStringList() << QLatin1String("scarceResourceTest"))
4360 << (QList<QVariant>() << true)
4361 << (QList<QVariant>() << QVariant(100))
4364 // In the following three cases, test that scarce resources are handled
4365 // correctly for imports.
4366 QTest::newRow("variant: import with no binding")
4367 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4368 << false // cannot check detach status.
4371 << QList<QVariant>()
4372 << QList<QVariant>()
4374 QTest::newRow("variant: import with binding without explicit preserve")
4375 << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4378 << (QStringList() << QLatin1String("scarceResourceCopy"))
4379 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4380 << (QList<QVariant>() << QVariant())
4382 QTest::newRow("variant: import with explicit release after binding evaluation")
4383 << testFileUrl("scarceResourceCopyImport.variant.qml")
4386 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo"))
4387 << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies.
4388 << (QList<QVariant>() << origPixmap << origPixmap << QVariant())
4392 void tst_qqmlecmascript::scarceResources()
4394 QFETCH(QUrl, qmlFile);
4395 QFETCH(bool, readDetachStatus);
4396 QFETCH(bool, expectedDetachStatus);
4397 QFETCH(QStringList, propertyNames);
4398 QFETCH(QVariantList, expectedValidity);
4399 QFETCH(QVariantList, expectedValues);
4400 QFETCH(QStringList, expectedErrors);
4402 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4403 ScarceResourceObject *eo = 0;
4404 QObject *object = 0;
4406 QQmlComponent c(&engine, qmlFile);
4407 object = c.create();
4408 QVERIFY(object != 0);
4409 for (int i = 0; i < propertyNames.size(); ++i) {
4410 QString prop = propertyNames.at(i);
4411 bool validity = expectedValidity.at(i).toBool();
4412 QVariant value = expectedValues.at(i);
4414 QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity);
4415 if (value.type() == QVariant::Int) {
4416 QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt());
4417 } else if (value.type() == QVariant::Pixmap) {
4418 QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>());
4422 if (readDetachStatus) {
4423 eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4424 QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
4427 QVERIFY(ep->scarceResources.isEmpty());
4431 void tst_qqmlecmascript::propertyChangeSlots()
4433 // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
4434 QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml"));
4435 QObject *object = component.create();
4436 QVERIFY(object != 0);
4439 // ensure that invalid property names fail properly.
4440 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4441 QQmlComponent e1(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.1.qml"));
4442 QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
4443 QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
4444 object = e1.create();
4445 QVERIFY(object == 0);
4448 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4449 QQmlComponent e2(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.2.qml"));
4450 expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
4451 QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
4452 object = e2.create();
4453 QVERIFY(object == 0);
4456 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4457 QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml"));
4458 expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
4459 QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
4460 object = e3.create();
4461 QVERIFY(object == 0);
4464 QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4465 QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml"));
4466 expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
4467 QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
4468 object = e4.create();
4469 QVERIFY(object == 0);
4473 void tst_qqmlecmascript::propertyVar_data()
4475 QTest::addColumn<QUrl>("qmlFile");
4478 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyVar.1.qml");
4479 QTest::newRow("non-bindable object changed") << testFileUrl("propertyVar.2.qml");
4480 QTest::newRow("primitive changed") << testFileUrl("propertyVar.3.qml");
4481 QTest::newRow("javascript array modification") << testFileUrl("propertyVar.4.qml");
4482 QTest::newRow("javascript map modification") << testFileUrl("propertyVar.5.qml");
4483 QTest::newRow("javascript array assignment") << testFileUrl("propertyVar.6.qml");
4484 QTest::newRow("javascript map assignment") << testFileUrl("propertyVar.7.qml");
4485 QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml");
4486 QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml");
4487 QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml");
4488 QTest::newRow("javascript function assignment") << testFileUrl("propertyVar.11.qml");
4489 QTest::newRow("javascript special assignment") << testFileUrl("propertyVar.12.qml");
4490 QTest::newRow("declarative binding assignment") << testFileUrl("propertyVar.13.qml");
4491 QTest::newRow("imperative binding assignment") << testFileUrl("propertyVar.14.qml");
4492 QTest::newRow("stored binding assignment") << testFileUrl("propertyVar.15.qml");
4493 QTest::newRow("function expression binding assignment") << testFileUrl("propertyVar.16.qml");
4496 void tst_qqmlecmascript::propertyVar()
4498 QFETCH(QUrl, qmlFile);
4500 QQmlComponent component(&engine, qmlFile);
4501 QObject *object = component.create();
4502 QVERIFY(object != 0);
4504 QCOMPARE(object->property("test").toBool(), true);
4509 void tst_qqmlecmascript::propertyQJSValue_data()
4511 QTest::addColumn<QUrl>("qmlFile");
4514 QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyQJSValue.1.qml");
4515 QTest::newRow("non-bindable object changed") << testFileUrl("propertyQJSValue.2.qml");
4516 QTest::newRow("primitive changed") << testFileUrl("propertyQJSValue.3.qml");
4517 QTest::newRow("javascript array modification") << testFileUrl("propertyQJSValue.4.qml");
4518 QTest::newRow("javascript map modification") << testFileUrl("propertyQJSValue.5.qml");
4519 QTest::newRow("javascript array assignment") << testFileUrl("propertyQJSValue.6.qml");
4520 QTest::newRow("javascript map assignment") << testFileUrl("propertyQJSValue.7.qml");
4521 QTest::newRow("literal property assignment") << testFileUrl("propertyQJSValue.8.qml");
4522 QTest::newRow("qobject property assignment") << testFileUrl("propertyQJSValue.9.qml");
4523 QTest::newRow("base class var property assignment") << testFileUrl("propertyQJSValue.10.qml");
4524 QTest::newRow("javascript function assignment") << testFileUrl("propertyQJSValue.11.qml");
4525 QTest::newRow("javascript special assignment") << testFileUrl("propertyQJSValue.12.qml");
4526 QTest::newRow("declarative binding assignment") << testFileUrl("propertyQJSValue.13.qml");
4527 QTest::newRow("imperative binding assignment") << testFileUrl("propertyQJSValue.14.qml");
4528 QTest::newRow("stored binding assignment") << testFileUrl("propertyQJSValue.15.qml");
4529 QTest::newRow("javascript function binding") << testFileUrl("propertyQJSValue.16.qml");
4531 QTest::newRow("reset property") << testFileUrl("propertyQJSValue.reset.qml");
4532 QTest::newRow("reset property in binding") << testFileUrl("propertyQJSValue.bindingreset.qml");
4535 void tst_qqmlecmascript::propertyQJSValue()
4537 QFETCH(QUrl, qmlFile);
4539 QQmlComponent component(&engine, qmlFile);
4540 QObject *object = component.create();
4541 QVERIFY(object != 0);
4543 QCOMPARE(object->property("test").toBool(), true);
4548 // Tests that we can write QVariant values to var properties from C++
4549 void tst_qqmlecmascript::propertyVarCpp()
4551 QObject *object = 0;
4553 // ensure that writing to and reading from a var property from cpp works as required.
4554 // Literal values stored in var properties can be read and written as QVariants
4555 // of a specific type, whereas object values are read as QVariantMaps.
4556 QQmlComponent component(&engine, testFileUrl("propertyVarCpp.qml"));
4557 object = component.create();
4558 QVERIFY(object != 0);
4559 // assign int to property var that currently has int assigned
4560 QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
4561 QCOMPARE(object->property("varBound"), QVariant(15));
4562 QCOMPARE(object->property("intBound"), QVariant(15));
4563 QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
4564 QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
4565 // assign string to property var that current has bool assigned
4566 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
4567 QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
4568 QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
4569 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
4570 // now enforce behaviour when accessing JavaScript objects from cpp.
4571 QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
4575 static void gc(QQmlEngine &engine)
4577 engine.collectGarbage();
4578 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4579 QCoreApplication::processEvents();
4582 void tst_qqmlecmascript::propertyVarOwnership()
4584 // Referenced JS objects are not collected
4586 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.qml"));
4587 QObject *object = component.create();
4588 QVERIFY(object != 0);
4589 QCOMPARE(object->property("test").toBool(), false);
4590 QMetaObject::invokeMethod(object, "runTest");
4591 QCOMPARE(object->property("test").toBool(), true);
4594 // Referenced JS objects are not collected
4596 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml"));
4597 QObject *object = component.create();
4598 QVERIFY(object != 0);
4599 QCOMPARE(object->property("test").toBool(), false);
4600 QMetaObject::invokeMethod(object, "runTest");
4601 QCOMPARE(object->property("test").toBool(), true);
4604 // Qt objects are not collected until they've been dereferenced
4606 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml"));
4607 QObject *object = component.create();
4608 QVERIFY(object != 0);
4610 QCOMPARE(object->property("test2").toBool(), false);
4611 QCOMPARE(object->property("test2").toBool(), false);
4613 QMetaObject::invokeMethod(object, "runTest");
4614 QCOMPARE(object->property("test1").toBool(), true);
4616 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4617 QVERIFY(!referencedObject.isNull());
4619 QVERIFY(!referencedObject.isNull());
4621 QMetaObject::invokeMethod(object, "runTest2");
4622 QCOMPARE(object->property("test2").toBool(), true);
4624 QVERIFY(referencedObject.isNull());
4628 // Self reference does not prevent Qt object collection
4630 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml"));
4631 QObject *object = component.create();
4632 QVERIFY(object != 0);
4634 QCOMPARE(object->property("test").toBool(), true);
4636 QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4637 QVERIFY(!referencedObject.isNull());
4639 QVERIFY(!referencedObject.isNull());
4641 QMetaObject::invokeMethod(object, "runTest");
4643 QVERIFY(referencedObject.isNull());
4647 // Garbage collection cannot result in attempted dereference of empty handle
4649 QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.5.qml"));
4650 QObject *object = component.create();
4651 QVERIFY(object != 0);
4652 QMetaObject::invokeMethod(object, "runTest");
4653 QCOMPARE(object->property("test").toBool(), true);
4658 void tst_qqmlecmascript::propertyVarImplicitOwnership()
4660 // The childObject has a reference to a different QObject. We want to ensure
4661 // that the different item will not be cleaned up until required. IE, the childObject
4662 // has implicit ownership of the constructed QObject.
4663 QQmlComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml"));
4664 QObject *object = component.create();
4665 QVERIFY(object != 0);
4666 QMetaObject::invokeMethod(object, "assignCircular");
4667 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4668 QCoreApplication::processEvents();
4669 QObject *rootObject = object->property("vp").value<QObject*>();
4670 QVERIFY(rootObject != 0);
4671 QObject *childObject = rootObject->findChild<QObject*>("text");
4672 QVERIFY(childObject != 0);
4673 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4674 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4675 QMetaObject::invokeMethod(childObject, "constructQObject"); // creates a reference to a constructed QObject.
4676 QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
4677 QVERIFY(!qobjectGuard.isNull());
4678 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4679 QCoreApplication::processEvents();
4680 QVERIFY(!qobjectGuard.isNull());
4681 QMetaObject::invokeMethod(object, "deassignCircular");
4682 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4683 QCoreApplication::processEvents();
4684 QVERIFY(qobjectGuard.isNull()); // should have been collected now.
4688 void tst_qqmlecmascript::propertyVarReparent()
4690 // ensure that nothing breaks if we re-parent objects
4691 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4692 QObject *object = component.create();
4693 QVERIFY(object != 0);
4694 QMetaObject::invokeMethod(object, "assignVarProp");
4695 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4696 QCoreApplication::processEvents();
4697 QObject *rect = object->property("vp").value<QObject*>();
4698 QObject *text = rect->findChild<QObject*>("textOne");
4699 QObject *text2 = rect->findChild<QObject*>("textTwo");
4700 QWeakPointer<QObject> rectGuard(rect);
4701 QWeakPointer<QObject> textGuard(text);
4702 QWeakPointer<QObject> text2Guard(text2);
4703 QVERIFY(!rectGuard.isNull());
4704 QVERIFY(!textGuard.isNull());
4705 QVERIFY(!text2Guard.isNull());
4706 QCOMPARE(text->property("textCanary").toInt(), 11);
4707 QCOMPARE(text2->property("textCanary").toInt(), 12);
4708 // now construct an image which we will reparent.
4709 QMetaObject::invokeMethod(text2, "constructQObject");
4710 QObject *image = text2->property("vp").value<QObject*>();
4711 QWeakPointer<QObject> imageGuard(image);
4712 QVERIFY(!imageGuard.isNull());
4713 QCOMPARE(image->property("imageCanary").toInt(), 13);
4714 // now reparent the "Image" object (currently, it has JS ownership)
4715 image->setParent(text); // shouldn't be collected after deassignVp now, since has a parent.
4716 QMetaObject::invokeMethod(text2, "deassignVp");
4717 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4718 QCoreApplication::processEvents();
4719 QCOMPARE(text->property("textCanary").toInt(), 11);
4720 QCOMPARE(text2->property("textCanary").toInt(), 22);
4721 QVERIFY(!imageGuard.isNull()); // should still be alive.
4722 QCOMPARE(image->property("imageCanary").toInt(), 13); // still able to access var properties
4723 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4724 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4725 QCoreApplication::processEvents();
4726 QVERIFY(imageGuard.isNull()); // should now have been deleted, due to parent being deleted.
4730 void tst_qqmlecmascript::propertyVarReparentNullContext()
4732 // sometimes reparenting can cause problems
4733 // (eg, if the ctxt is collected, varproperties are no longer available)
4734 // this test ensures that no crash occurs in that situation.
4735 QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4736 QObject *object = component.create();
4737 QVERIFY(object != 0);
4738 QMetaObject::invokeMethod(object, "assignVarProp");
4739 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4740 QCoreApplication::processEvents();
4741 QObject *rect = object->property("vp").value<QObject*>();
4742 QObject *text = rect->findChild<QObject*>("textOne");
4743 QObject *text2 = rect->findChild<QObject*>("textTwo");
4744 QWeakPointer<QObject> rectGuard(rect);
4745 QWeakPointer<QObject> textGuard(text);
4746 QWeakPointer<QObject> text2Guard(text2);
4747 QVERIFY(!rectGuard.isNull());
4748 QVERIFY(!textGuard.isNull());
4749 QVERIFY(!text2Guard.isNull());
4750 QCOMPARE(text->property("textCanary").toInt(), 11);
4751 QCOMPARE(text2->property("textCanary").toInt(), 12);
4752 // now construct an image which we will reparent.
4753 QMetaObject::invokeMethod(text2, "constructQObject");
4754 QObject *image = text2->property("vp").value<QObject*>();
4755 QWeakPointer<QObject> imageGuard(image);
4756 QVERIFY(!imageGuard.isNull());
4757 QCOMPARE(image->property("imageCanary").toInt(), 13);
4758 // now reparent the "Image" object (currently, it has JS ownership)
4759 image->setParent(object); // reparented to base object. after deassignVarProp, the ctxt will be invalid.
4760 QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
4761 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4762 QCoreApplication::processEvents();
4763 QVERIFY(!imageGuard.isNull()); // should still be alive.
4764 QVERIFY(!image->property("imageCanary").isValid()); // but varProperties won't be available (null context).
4766 QVERIFY(imageGuard.isNull()); // should now be dead.
4769 void tst_qqmlecmascript::propertyVarCircular()
4771 // enforce behaviour regarding circular references - ensure qdvmemo deletion.
4772 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.qml"));
4773 QObject *object = component.create();
4774 QVERIFY(object != 0);
4775 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4776 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4777 QCoreApplication::processEvents();
4778 QCOMPARE(object->property("canaryInt"), QVariant(5));
4779 QVariant canaryResourceVariant = object->property("canaryResource");
4780 QVERIFY(canaryResourceVariant.isValid());
4781 QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
4782 canaryResourceVariant = QVariant(); // invalidate it to remove one copy of the pixmap from memory.
4783 QMetaObject::invokeMethod(object, "deassignCanaryResource"); // remove one copy of the pixmap from memory
4784 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4785 QCoreApplication::processEvents();
4786 QVERIFY(!canaryResourcePixmap.isDetached()); // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
4787 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4788 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4789 QCoreApplication::processEvents();
4790 QCOMPARE(object->property("canaryInt"), QVariant(2));
4791 QCOMPARE(object->property("canaryResource"), QVariant(1));
4792 QVERIFY(canaryResourcePixmap.isDetached()); // now detached, since orig copy was member of qdvmemo which was deleted.
4796 void tst_qqmlecmascript::propertyVarCircular2()
4798 // track deletion of JS-owned parent item with Cpp-owned child
4799 // where the child has a var property referencing its parent.
4800 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4801 QObject *object = component.create();
4802 QVERIFY(object != 0);
4803 QMetaObject::invokeMethod(object, "assignCircular");
4804 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4805 QCoreApplication::processEvents();
4806 QObject *rootObject = object->property("vp").value<QObject*>();
4807 QVERIFY(rootObject != 0);
4808 QObject *childObject = rootObject->findChild<QObject*>("text");
4809 QVERIFY(childObject != 0);
4810 QWeakPointer<QObject> rootObjectTracker(rootObject);
4811 QVERIFY(!rootObjectTracker.isNull());
4812 QWeakPointer<QObject> childObjectTracker(childObject);
4813 QVERIFY(!childObjectTracker.isNull());
4815 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4816 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4817 QMetaObject::invokeMethod(object, "deassignCircular");
4818 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4819 QCoreApplication::processEvents();
4820 QVERIFY(rootObjectTracker.isNull()); // should have been collected
4821 QVERIFY(childObjectTracker.isNull()); // should have been collected
4825 void tst_qqmlecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
4827 *(int*)(parameter) += 1;
4828 qPersistentDispose(object);
4831 void tst_qqmlecmascript::propertyVarInheritance()
4833 int propertyVarWeakRefCallbackCount = 0;
4835 // enforce behaviour regarding element inheritance - ensure handle disposal.
4836 // The particular component under test here has a chain of references.
4837 QQmlComponent component(&engine, testFileUrl("propertyVar.inherit.qml"));
4838 QObject *object = component.create();
4839 QVERIFY(object != 0);
4840 QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc
4841 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4842 QCoreApplication::processEvents();
4843 // we want to be able to track when the varProperties array of the last metaobject is disposed
4844 QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
4845 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*>();
4846 QQmlVMEMetaObject *icovmemo = QQmlVMEMetaObject::get(ico5);
4847 QQmlVMEMetaObject *ccovmemo = QQmlVMEMetaObject::get(cco5);
4848 v8::Persistent<v8::Value> icoCanaryHandle;
4849 v8::Persistent<v8::Value> ccoCanaryHandle;
4852 // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only
4853 // public function which can return us a handle to something in the varProperties array.
4854 icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ")));
4855 ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")));
4856 // we make them weak and invoke the gc, but we should not hit the weak-callback yet
4857 // as the varproperties array of each vmemo still references the resource.
4858 icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4859 ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4861 QVERIFY(propertyVarWeakRefCallbackCount == 0);
4863 // now we deassign the var prop, which should trigger collection of item subtrees.
4864 QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc
4865 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4866 QCoreApplication::processEvents();
4867 // ensure that there are only weak handles to the underlying varProperties array remaining.
4869 QCOMPARE(propertyVarWeakRefCallbackCount, 2); // should have been called for both, since all refs should be weak.
4871 // since there are no parent vmemo's to keep implicit references alive, and the only handles
4872 // to what remains are weak, all varProperties arrays must have been collected.
4875 void tst_qqmlecmascript::propertyVarInheritance2()
4877 int propertyVarWeakRefCallbackCount = 0;
4879 // The particular component under test here does NOT have a chain of references; the
4880 // only link between rootObject and childObject is that rootObject is the parent of childObject.
4881 QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4882 QObject *object = component.create();
4883 QVERIFY(object != 0);
4884 QMetaObject::invokeMethod(object, "assignCircular");
4885 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4886 QCoreApplication::processEvents();
4887 QObject *rootObject = object->property("vp").value<QObject*>();
4888 QVERIFY(rootObject != 0);
4889 QObject *childObject = rootObject->findChild<QObject*>("text");
4890 QVERIFY(childObject != 0);
4891 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4892 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4893 v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
4896 propertyVarWeakRefCallbackCount = 0; // reset callback count.
4897 childObjectVarArrayValueHandle = qPersistentNew(QQmlVMEMetaObject::get(childObject)->vmeProperty(childObject->metaObject()->indexOfProperty("vp")));
4898 childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4900 QVERIFY(propertyVarWeakRefCallbackCount == 0); // should not have been collected yet.
4901 QCOMPARE(childObject->property("vp").value<QObject*>(), rootObject);
4902 QCOMPARE(childObject->property("textCanary").toInt(), 10);
4904 QMetaObject::invokeMethod(object, "deassignCircular");
4905 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4906 QCoreApplication::processEvents();
4907 QVERIFY(propertyVarWeakRefCallbackCount == 1); // should have been collected now.
4911 // Ensure that QObject type conversion works on binding assignment
4912 void tst_qqmlecmascript::elementAssign()
4914 QQmlComponent component(&engine, testFileUrl("elementAssign.qml"));
4916 QObject *object = component.create();
4917 QVERIFY(object != 0);
4919 QCOMPARE(object->property("test").toBool(), true);
4925 void tst_qqmlecmascript::objectPassThroughSignals()
4927 QQmlComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml"));
4929 QObject *object = component.create();
4930 QVERIFY(object != 0);
4932 QCOMPARE(object->property("test").toBool(), true);
4938 void tst_qqmlecmascript::objectConversion()
4940 QQmlComponent component(&engine, testFileUrl("objectConversion.qml"));
4942 QObject *object = component.create();
4943 QVERIFY(object != 0);
4945 QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
4946 QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
4953 void tst_qqmlecmascript::booleanConversion()
4955 QQmlComponent component(&engine, testFileUrl("booleanConversion.qml"));
4957 QObject *object = component.create();
4958 QVERIFY(object != 0);
4960 QCOMPARE(object->property("test_true1").toBool(), true);
4961 QCOMPARE(object->property("test_true2").toBool(), true);
4962 QCOMPARE(object->property("test_true3").toBool(), true);
4963 QCOMPARE(object->property("test_true4").toBool(), true);
4964 QCOMPARE(object->property("test_true5").toBool(), true);
4966 QCOMPARE(object->property("test_false1").toBool(), false);
4967 QCOMPARE(object->property("test_false2").toBool(), false);
4968 QCOMPARE(object->property("test_false3").toBool(), false);
4973 void tst_qqmlecmascript::handleReferenceManagement()
4978 // Linear QObject reference
4979 QQmlEngine hrmEngine;
4980 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml"));
4981 QObject *object = component.create();
4982 QVERIFY(object != 0);
4983 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4984 cro->setEngine(&hrmEngine);
4985 cro->setDtorCount(&dtorCount);
4986 QMetaObject::invokeMethod(object, "createReference");
4988 QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
4990 hrmEngine.collectGarbage();
4991 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4992 QCoreApplication::processEvents();
4993 QCOMPARE(dtorCount, 3);
4998 // Circular QObject reference
4999 QQmlEngine hrmEngine;
5000 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml"));
5001 QObject *object = component.create();
5002 QVERIFY(object != 0);
5003 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
5004 cro->setEngine(&hrmEngine);
5005 cro->setDtorCount(&dtorCount);
5006 QMetaObject::invokeMethod(object, "circularReference");
5008 QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
5010 hrmEngine.collectGarbage();
5011 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5012 QCoreApplication::processEvents();
5013 QCOMPARE(dtorCount, 3);
5018 // Linear handle reference
5019 QQmlEngine hrmEngine;
5020 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.1.qml"));
5021 QObject *object = component.create();
5022 QVERIFY(object != 0);
5023 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
5025 crh->setEngine(&hrmEngine);
5026 crh->setDtorCount(&dtorCount);
5027 QMetaObject::invokeMethod(object, "createReference");
5028 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
5029 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
5030 QVERIFY(first != 0);
5031 QVERIFY(second != 0);
5032 first->addReference(QQmlData::get(second)->v8object); // create reference
5033 // now we have to reparent second and make second owned by JS.
5034 second->setParent(0);
5035 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
5037 QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
5039 hrmEngine.collectGarbage();
5040 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5041 QCoreApplication::processEvents();
5042 QCOMPARE(dtorCount, 3);
5047 // Circular handle reference
5048 QQmlEngine hrmEngine;
5049 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.2.qml"));
5050 QObject *object = component.create();
5051 QVERIFY(object != 0);
5052 CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
5054 crh->setEngine(&hrmEngine);
5055 crh->setDtorCount(&dtorCount);
5056 QMetaObject::invokeMethod(object, "circularReference");
5057 CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
5058 CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
5059 QVERIFY(first != 0);
5060 QVERIFY(second != 0);
5061 first->addReference(QQmlData::get(second)->v8object); // create circular reference
5062 second->addReference(QQmlData::get(first)->v8object); // note: must be weak.
5063 // now we have to reparent and change ownership, and unset the property references.
5064 first->setParent(0);
5065 second->setParent(0);
5066 QQmlEngine::setObjectOwnership(first, QQmlEngine::JavaScriptOwnership);
5067 QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
5068 object->setProperty("first", QVariant::fromValue<QObject*>(0));
5069 object->setProperty("second", QVariant::fromValue<QObject*>(0));
5071 QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
5073 hrmEngine.collectGarbage();
5074 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5075 QCoreApplication::processEvents();
5076 QCOMPARE(dtorCount, 3);
5081 // multiple engine interaction - linear reference
5082 QQmlEngine hrmEngine1;
5083 QQmlEngine hrmEngine2;
5084 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
5085 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
5086 QObject *object1 = component1.create();
5087 QObject *object2 = component2.create();
5088 QVERIFY(object1 != 0);
5089 QVERIFY(object2 != 0);
5090 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
5091 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
5094 crh1->setEngine(&hrmEngine1);
5095 crh2->setEngine(&hrmEngine2);
5096 crh1->setDtorCount(&dtorCount);
5097 crh2->setDtorCount(&dtorCount);
5098 QMetaObject::invokeMethod(object1, "createReference");
5099 QMetaObject::invokeMethod(object2, "createReference");
5100 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
5101 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
5102 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
5103 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
5104 QVERIFY(first1 != 0);
5105 QVERIFY(second1 != 0);
5106 QVERIFY(first2 != 0);
5107 QVERIFY(second2 != 0);
5108 first1->addReference(QQmlData::get(second2)->v8object); // create reference across engines
5109 // now we have to reparent second2 and make second2 owned by JS.
5110 second2->setParent(0);
5111 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5113 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5114 QCoreApplication::processEvents();
5115 QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
5118 hrmEngine1.collectGarbage();
5119 hrmEngine2.collectGarbage();
5120 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5121 QCoreApplication::processEvents();
5122 QCOMPARE(dtorCount, 6);
5127 // multiple engine interaction - circular reference
5128 QQmlEngine hrmEngine1;
5129 QQmlEngine hrmEngine2;
5130 QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
5131 QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
5132 QObject *object1 = component1.create();
5133 QObject *object2 = component2.create();
5134 QVERIFY(object1 != 0);
5135 QVERIFY(object2 != 0);
5136 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
5137 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
5140 crh1->setEngine(&hrmEngine1);
5141 crh2->setEngine(&hrmEngine2);
5142 crh1->setDtorCount(&dtorCount);
5143 crh2->setDtorCount(&dtorCount);
5144 QMetaObject::invokeMethod(object1, "createReference");
5145 QMetaObject::invokeMethod(object2, "createReference");
5146 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
5147 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
5148 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
5149 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
5150 QVERIFY(first1 != 0);
5151 QVERIFY(second1 != 0);
5152 QVERIFY(first2 != 0);
5153 QVERIFY(second2 != 0);
5154 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
5155 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
5156 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
5157 first2->addReference(QQmlData::get(first1)->v8object); // close the loop - circular ref across engines
5158 // now we have to reparent and change ownership to JS, and remove property references.
5159 first1->setParent(0);
5160 second1->setParent(0);
5161 first2->setParent(0);
5162 second2->setParent(0);
5163 QQmlEngine::setObjectOwnership(first1, QQmlEngine::JavaScriptOwnership);
5164 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
5165 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
5166 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5167 object1->setProperty("first", QVariant::fromValue<QObject*>(0));
5168 object1->setProperty("second", QVariant::fromValue<QObject*>(0));
5169 object2->setProperty("first", QVariant::fromValue<QObject*>(0));
5170 object2->setProperty("second", QVariant::fromValue<QObject*>(0));
5172 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5173 QCoreApplication::processEvents();
5174 QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
5177 hrmEngine1.collectGarbage();
5178 hrmEngine2.collectGarbage();
5179 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5180 QCoreApplication::processEvents();
5181 QCOMPARE(dtorCount, 6);
5186 // multiple engine interaction - linear reference with engine deletion
5187 QQmlEngine *hrmEngine1 = new QQmlEngine;
5188 QQmlEngine *hrmEngine2 = new QQmlEngine;
5189 QQmlComponent component1(hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
5190 QQmlComponent component2(hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
5191 QObject *object1 = component1.create();
5192 QObject *object2 = component2.create();
5193 QVERIFY(object1 != 0);
5194 QVERIFY(object2 != 0);
5195 CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
5196 CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
5199 crh1->setEngine(hrmEngine1);
5200 crh2->setEngine(hrmEngine2);
5201 crh1->setDtorCount(&dtorCount);
5202 crh2->setDtorCount(&dtorCount);
5203 QMetaObject::invokeMethod(object1, "createReference");
5204 QMetaObject::invokeMethod(object2, "createReference");
5205 CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
5206 CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
5207 CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
5208 CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
5209 QVERIFY(first1 != 0);
5210 QVERIFY(second1 != 0);
5211 QVERIFY(first2 != 0);
5212 QVERIFY(second2 != 0);
5213 first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1
5214 second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
5215 second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2
5216 // now we have to reparent and change ownership to JS.
5217 first1->setParent(crh1);
5218 second1->setParent(0);
5219 first2->setParent(0);
5220 second2->setParent(0);
5221 QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
5222 QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
5223 QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5226 QCOMPARE(dtorCount, 0);
5227 delete hrmEngine2; // should trigger deletion of objects with JS ownership tracked by this engine
5229 QCOMPARE(dtorCount, 2); // first2 and second2 should have been deleted.
5233 QCOMPARE(dtorCount, 6); // deleting object1 and object2 should trigger deletion of first1 and first2.
5235 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5236 QCoreApplication::processEvents();
5237 QCOMPARE(dtorCount, 6); // all objects should have been cleaned up prior to deleting hrmEngine1.
5241 // Dynamic variant property reference keeps target alive
5242 QQmlEngine hrmEngine;
5243 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.qml"));
5244 QObject *object = component.create();
5245 QVERIFY(object != 0);
5246 QMetaObject::invokeMethod(object, "createReference");
5248 QMetaObject::invokeMethod(object, "ensureReference");
5250 QMetaObject::invokeMethod(object, "removeReference");
5252 QMetaObject::invokeMethod(object, "ensureDeletion");
5253 QCOMPARE(object->property("success").toBool(), true);
5258 // Dynamic Item property reference keeps target alive
5259 QQmlEngine hrmEngine;
5260 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.2.qml"));
5261 QObject *object = component.create();
5262 QVERIFY(object != 0);
5263 QMetaObject::invokeMethod(object, "createReference");
5265 QMetaObject::invokeMethod(object, "ensureReference");
5267 QMetaObject::invokeMethod(object, "removeReference");
5269 QMetaObject::invokeMethod(object, "ensureDeletion");
5270 QCOMPARE(object->property("success").toBool(), true);
5275 // Item property reference to deleted item doesn't crash
5276 QQmlEngine hrmEngine;
5277 QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.3.qml"));
5278 QObject *object = component.create();
5279 QVERIFY(object != 0);
5280 QMetaObject::invokeMethod(object, "createReference");
5282 QMetaObject::invokeMethod(object, "ensureReference");
5284 QMetaObject::invokeMethod(object, "manuallyDelete");
5286 QMetaObject::invokeMethod(object, "ensureDeleted");
5287 QCOMPARE(object->property("success").toBool(), true);
5292 void tst_qqmlecmascript::stringArg()
5294 QQmlComponent component(&engine, testFileUrl("stringArg.qml"));
5295 QObject *object = component.create();
5296 QVERIFY(object != 0);
5297 QMetaObject::invokeMethod(object, "success");
5298 QVERIFY(object->property("returnValue").toBool());
5300 QString w1 = testFileUrl("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
5301 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5302 QMetaObject::invokeMethod(object, "failure");
5303 QVERIFY(object->property("returnValue").toBool());
5308 void tst_qqmlecmascript::readonlyDeclaration()
5310 QQmlComponent component(&engine, testFileUrl("readonlyDeclaration.qml"));
5312 QObject *object = component.create();
5313 QVERIFY(object != 0);
5315 QCOMPARE(object->property("test").toBool(), true);
5320 Q_DECLARE_METATYPE(QList<int>)
5321 Q_DECLARE_METATYPE(QList<qreal>)
5322 Q_DECLARE_METATYPE(QList<bool>)
5323 Q_DECLARE_METATYPE(QList<QString>)
5324 Q_DECLARE_METATYPE(QList<QUrl>)
5325 void tst_qqmlecmascript::sequenceConversionRead()
5328 QUrl qmlFile = testFileUrl("sequenceConversion.read.qml");
5329 QQmlComponent component(&engine, qmlFile);
5330 QObject *object = component.create();
5331 QVERIFY(object != 0);
5332 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5335 QMetaObject::invokeMethod(object, "readSequences");
5336 QList<int> intList; intList << 1 << 2 << 3 << 4;
5337 QCOMPARE(object->property("intListLength").toInt(), intList.length());
5338 QCOMPARE(object->property("intList").value<QList<int> >(), intList);
5339 QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
5340 QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
5341 QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
5342 QList<bool> boolList; boolList << true << false << true << false;
5343 QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
5344 QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
5345 QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5346 QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
5347 QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
5348 QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
5349 QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
5350 QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
5351 QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5352 QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
5353 QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
5355 QMetaObject::invokeMethod(object, "readSequenceElements");
5356 QCOMPARE(object->property("intVal").toInt(), 2);
5357 QCOMPARE(object->property("qrealVal").toReal(), 2.2);
5358 QCOMPARE(object->property("boolVal").toBool(), false);
5359 QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
5360 QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
5361 QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
5363 QMetaObject::invokeMethod(object, "enumerateSequenceElements");
5364 QCOMPARE(object->property("enumerationMatches").toBool(), true);
5366 intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
5367 QQmlProperty seqProp(seq, "intListProperty");
5368 QCOMPARE(seqProp.read().value<QList<int> >(), intList);
5369 QQmlProperty seqProp2(seq, "intListProperty", &engine);
5370 QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
5372 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5373 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5379 QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml");
5380 QQmlComponent component(&engine, qmlFile);
5381 QObject *object = component.create();
5382 QVERIFY(object != 0);
5383 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5386 // we haven't registered QList<NonRegisteredType> as a sequence type.
5387 QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<NonRegisteredType>' for property 'MySequenceConversionObject::typeListProperty'");
5388 QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
5389 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5390 QTest::ignoreMessage(QtWarningMsg, warningTwo.toLatin1().constData());
5392 QMetaObject::invokeMethod(object, "performTest");
5394 // QList<NonRegisteredType> has not been registered as a sequence type.
5395 QCOMPARE(object->property("pointListLength").toInt(), 0);
5396 QVERIFY(!object->property("pointList").isValid());
5397 QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<NonRegisteredType>' for property 'MySequenceConversionObject::typeListProperty'");
5398 QQmlProperty seqProp(seq, "typeListProperty", &engine);
5399 QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
5405 void tst_qqmlecmascript::sequenceConversionWrite()
5408 QUrl qmlFile = testFileUrl("sequenceConversion.write.qml");
5409 QQmlComponent component(&engine, qmlFile);
5410 QObject *object = component.create();
5411 QVERIFY(object != 0);
5412 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5415 QMetaObject::invokeMethod(object, "writeSequences");
5416 QCOMPARE(object->property("success").toBool(), true);
5418 QMetaObject::invokeMethod(object, "writeSequenceElements");
5419 QCOMPARE(object->property("success").toBool(), true);
5421 QMetaObject::invokeMethod(object, "writeOtherElements");
5422 QCOMPARE(object->property("success").toBool(), true);
5424 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5425 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5431 QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml");
5432 QQmlComponent component(&engine, qmlFile);
5433 QObject *object = component.create();
5434 QVERIFY(object != 0);
5435 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5438 // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
5439 QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to an unregistered type");
5440 QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5442 QMetaObject::invokeMethod(object, "performTest");
5444 QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
5445 QCOMPARE(seq->pointListProperty(), pointList);
5451 void tst_qqmlecmascript::sequenceConversionArray()
5453 // ensure that in JS the returned sequences act just like normal JS Arrays.
5454 QUrl qmlFile = testFileUrl("sequenceConversion.array.qml");
5455 QQmlComponent component(&engine, qmlFile);
5456 QObject *object = component.create();
5457 QVERIFY(object != 0);
5458 QMetaObject::invokeMethod(object, "indexedAccess");
5459 QVERIFY(object->property("success").toBool());
5460 QMetaObject::invokeMethod(object, "arrayOperations");
5461 QVERIFY(object->property("success").toBool());
5462 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5463 QVERIFY(object->property("success").toBool());
5464 QMetaObject::invokeMethod(object, "testReferenceDeletion");
5465 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5470 void tst_qqmlecmascript::sequenceConversionIndexes()
5472 // ensure that we gracefully fail if unsupported index values are specified.
5473 // Qt container classes only support non-negative, signed integer index values.
5474 QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
5475 QQmlComponent component(&engine, qmlFile);
5476 QObject *object = component.create();
5477 QVERIFY(object != 0);
5478 QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
5479 QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
5480 QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
5481 QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
5482 QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
5483 QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
5484 QMetaObject::invokeMethod(object, "indexedAccess");
5485 QVERIFY(object->property("success").toBool());
5489 void tst_qqmlecmascript::sequenceConversionThreads()
5491 // ensure that sequence conversion operations work correctly in a worker thread
5492 // and that serialisation between the main and worker thread succeeds.
5493 QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml");
5494 QQmlComponent component(&engine, qmlFile);
5495 QObject *object = component.create();
5496 QVERIFY(object != 0);
5498 QMetaObject::invokeMethod(object, "testIntSequence");
5499 QTRY_VERIFY(object->property("finished").toBool());
5500 QVERIFY(object->property("success").toBool());
5502 QMetaObject::invokeMethod(object, "testQrealSequence");
5503 QTRY_VERIFY(object->property("finished").toBool());
5504 QVERIFY(object->property("success").toBool());
5506 QMetaObject::invokeMethod(object, "testBoolSequence");
5507 QTRY_VERIFY(object->property("finished").toBool());
5508 QVERIFY(object->property("success").toBool());
5510 QMetaObject::invokeMethod(object, "testStringSequence");
5511 QTRY_VERIFY(object->property("finished").toBool());
5512 QVERIFY(object->property("success").toBool());
5514 QMetaObject::invokeMethod(object, "testQStringSequence");
5515 QTRY_VERIFY(object->property("finished").toBool());
5516 QVERIFY(object->property("success").toBool());
5518 QMetaObject::invokeMethod(object, "testUrlSequence");
5519 QTRY_VERIFY(object->property("finished").toBool());
5520 QVERIFY(object->property("success").toBool());
5522 QMetaObject::invokeMethod(object, "testVariantSequence");
5523 QTRY_VERIFY(object->property("finished").toBool());
5524 QVERIFY(object->property("success").toBool());
5529 void tst_qqmlecmascript::sequenceConversionBindings()
5532 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml");
5533 QQmlComponent component(&engine, qmlFile);
5534 QObject *object = component.create();
5535 QVERIFY(object != 0);
5536 QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
5537 QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
5538 QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
5539 QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
5540 QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
5545 QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml");
5546 QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
5547 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5548 QQmlComponent component(&engine, qmlFile);
5549 QObject *object = component.create();
5550 QVERIFY(object != 0);
5555 void tst_qqmlecmascript::sequenceConversionCopy()
5557 QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml");
5558 QQmlComponent component(&engine, qmlFile);
5559 QObject *object = component.create();
5560 QVERIFY(object != 0);
5561 QMetaObject::invokeMethod(object, "testCopySequences");
5562 QCOMPARE(object->property("success").toBool(), true);
5563 QMetaObject::invokeMethod(object, "readSequenceCopyElements");
5564 QCOMPARE(object->property("success").toBool(), true);
5565 QMetaObject::invokeMethod(object, "testEqualitySemantics");
5566 QCOMPARE(object->property("success").toBool(), true);
5567 QMetaObject::invokeMethod(object, "testCopyParameters");
5568 QCOMPARE(object->property("success").toBool(), true);
5569 QMetaObject::invokeMethod(object, "testReferenceParameters");
5570 QCOMPARE(object->property("success").toBool(), true);
5574 void tst_qqmlecmascript::assignSequenceTypes()
5576 // test binding array to sequence type property
5578 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml"));
5579 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5580 QVERIFY(object != 0);
5581 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5582 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5583 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5584 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5585 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5586 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5590 // test binding literal to sequence type property
5592 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml"));
5593 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5594 QVERIFY(object != 0);
5595 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5596 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5597 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5598 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5599 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5600 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5604 // test binding single value to sequence type property
5606 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml"));
5607 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5608 QVERIFY(object != 0);
5609 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5610 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5611 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5612 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5616 // test assigning array to sequence type property in js function
5618 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml"));
5619 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5620 QVERIFY(object != 0);
5621 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5622 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5623 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5624 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5625 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5626 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5630 // test assigning literal to sequence type property in js function
5632 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml"));
5633 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5634 QVERIFY(object != 0);
5635 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5636 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5637 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5638 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5639 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5640 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5644 // test assigning single value to sequence type property in js function
5646 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml"));
5647 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5648 QVERIFY(object != 0);
5649 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5650 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5651 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5652 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5656 // test QList<QUrl> literal assignment and binding assignment causes url resolution when required
5658 QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml"));
5659 QObject *object = component.create();
5660 QVERIFY(object != 0);
5661 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
5662 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
5663 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
5664 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
5665 MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco5"));
5666 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0);
5667 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5668 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5669 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5670 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5671 QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5676 // Test that assigning a null object works
5677 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
5678 void tst_qqmlecmascript::nullObjectBinding()
5680 QQmlComponent component(&engine, testFileUrl("nullObjectBinding.qml"));
5682 QObject *object = component.create();
5683 QVERIFY(object != 0);
5685 QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
5690 // Test that bindings don't evaluate once the engine has been destroyed
5691 void tst_qqmlecmascript::deletedEngine()
5693 QQmlEngine *engine = new QQmlEngine;
5694 QQmlComponent component(engine, testFileUrl("deletedEngine.qml"));
5696 QObject *object = component.create();
5697 QVERIFY(object != 0);
5699 QCOMPARE(object->property("a").toInt(), 39);
5700 object->setProperty("b", QVariant(9));
5701 QCOMPARE(object->property("a").toInt(), 117);
5705 QCOMPARE(object->property("a").toInt(), 117);
5706 object->setProperty("b", QVariant(10));
5707 QCOMPARE(object->property("a").toInt(), 117);
5712 // Test the crashing part of QTBUG-9705
5713 void tst_qqmlecmascript::libraryScriptAssert()
5715 QQmlComponent component(&engine, testFileUrl("libraryScriptAssert.qml"));
5717 QObject *object = component.create();
5718 QVERIFY(object != 0);
5723 void tst_qqmlecmascript::variantsAssignedUndefined()
5725 QQmlComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml"));
5727 QObject *object = component.create();
5728 QVERIFY(object != 0);
5730 QCOMPARE(object->property("test1").toInt(), 10);
5731 QCOMPARE(object->property("test2").toInt(), 11);
5733 object->setProperty("runTest", true);
5735 QCOMPARE(object->property("test1"), QVariant());
5736 QCOMPARE(object->property("test2"), QVariant());
5742 void tst_qqmlecmascript::qtbug_9792()
5744 QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml"));
5746 QQmlContext *context = new QQmlContext(engine.rootContext());
5748 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
5749 QVERIFY(object != 0);
5751 QTest::ignoreMessage(QtDebugMsg, "Hello world!");
5752 object->basicSignal();
5756 transientErrorsMsgCount = 0;
5757 QtMessageHandler old = qInstallMessageHandler(transientErrorsMsgHandler);
5759 object->basicSignal();
5761 qInstallMessageHandler(old);
5763 QCOMPARE(transientErrorsMsgCount, 0);
5768 // Verifies that QQmlGuard<>s used in the vmemetaobject are cleaned correctly
5769 void tst_qqmlecmascript::qtcreatorbug_1289()
5771 QQmlComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml"));
5773 QObject *o = component.create();
5776 QObject *nested = qvariant_cast<QObject *>(o->property("object"));
5777 QVERIFY(nested != 0);
5779 QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
5782 nested = qvariant_cast<QObject *>(o->property("object"));
5783 QVERIFY(nested == 0);
5785 // If the bug is present, the next line will crash
5789 // Test that we shut down without stupid warnings
5790 void tst_qqmlecmascript::noSpuriousWarningsAtShutdown()
5793 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml"));
5795 QObject *o = component.create();
5797 transientErrorsMsgCount = 0;
5798 QtMessageHandler old = qInstallMessageHandler(transientErrorsMsgHandler);
5802 qInstallMessageHandler(old);
5804 QCOMPARE(transientErrorsMsgCount, 0);
5809 QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml"));
5811 QObject *o = component.create();
5813 transientErrorsMsgCount = 0;
5814 QtMessageHandler old = qInstallMessageHandler(transientErrorsMsgHandler);
5818 qInstallMessageHandler(old);
5820 QCOMPARE(transientErrorsMsgCount, 0);
5824 void tst_qqmlecmascript::canAssignNullToQObject()
5827 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml"));
5829 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5832 QVERIFY(o->objectProperty() != 0);
5834 o->setProperty("runTest", true);
5836 QVERIFY(o->objectProperty() == 0);
5842 QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml"));
5844 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5847 QVERIFY(o->objectProperty() == 0);
5853 void tst_qqmlecmascript::functionAssignment_fromBinding()
5855 QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml"));
5857 QString url = component.url().toString();
5858 QString w1 = url + ":4:25: Unable to assign a function to a property of any type other than var.";
5859 QString w2 = url + ":5:25: Invalid use of Qt.binding() in a binding declaration.";
5860 QString w3 = url + ":6:21: Invalid use of Qt.binding() in a binding declaration.";
5861 QString w4 = url + ":7:15: Invalid use of Qt.binding() in a binding declaration.";
5862 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5863 QTest::ignoreMessage(QtWarningMsg, w2.toLatin1().constData());
5864 QTest::ignoreMessage(QtWarningMsg, w3.toLatin1().constData());
5865 QTest::ignoreMessage(QtWarningMsg, w4.toLatin1().constData());
5867 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5870 QVERIFY(!o->property("a").isValid());
5875 void tst_qqmlecmascript::functionAssignment_fromJS()
5877 QFETCH(QString, triggerProperty);
5879 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5880 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5882 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5884 QVERIFY(!o->property("a").isValid());
5886 o->setProperty("aNumber", QVariant(5));
5887 o->setProperty(triggerProperty.toUtf8().constData(), true);
5888 QCOMPARE(o->property("a"), QVariant(50));
5890 o->setProperty("aNumber", QVariant(10));
5891 QCOMPARE(o->property("a"), QVariant(100));
5896 void tst_qqmlecmascript::functionAssignment_fromJS_data()
5898 QTest::addColumn<QString>("triggerProperty");
5900 QTest::newRow("assign to property") << "assignToProperty";
5901 QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
5903 QTest::newRow("assign to value type") << "assignToValueType";
5905 QTest::newRow("use 'this'") << "assignWithThis";
5906 QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
5909 void tst_qqmlecmascript::functionAssignmentfromJS_invalid()
5911 QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5912 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5914 MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5916 QVERIFY(!o->property("a").isValid());
5918 o->setProperty("assignFuncWithoutReturn", true);
5919 QVERIFY(!o->property("a").isValid());
5921 QString url = component.url().toString();
5922 QString warning = url + ":67:17: Unable to assign QString to int";
5923 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5924 o->setProperty("assignWrongType", true);
5926 warning = url + ":71:29: Unable to assign QString to int";
5927 QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5928 o->setProperty("assignWrongTypeToValueType", true);
5933 void tst_qqmlecmascript::functionAssignment_afterBinding()
5935 QQmlComponent component(&engine, testFileUrl("functionAssignment.3.qml"));
5937 QString url = component.url().toString();
5938 QString w1 = url + ":16: Error: Cannot assign JavaScript function to int";
5939 QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5941 QObject *o = component.create();
5943 QCOMPARE(o->property("t1"), QVariant::fromValue<int>(4)); // should have bound
5944 QCOMPARE(o->property("t2"), QVariant::fromValue<int>(2)); // should not have changed
5949 void tst_qqmlecmascript::eval()
5951 QQmlComponent component(&engine, testFileUrl("eval.qml"));
5953 QObject *o = component.create();
5956 QCOMPARE(o->property("test1").toBool(), true);
5957 QCOMPARE(o->property("test2").toBool(), true);
5958 QCOMPARE(o->property("test3").toBool(), true);
5959 QCOMPARE(o->property("test4").toBool(), true);
5960 QCOMPARE(o->property("test5").toBool(), true);
5965 void tst_qqmlecmascript::function()
5967 QQmlComponent component(&engine, testFileUrl("function.qml"));
5969 QObject *o = component.create();
5972 QCOMPARE(o->property("test1").toBool(), true);
5973 QCOMPARE(o->property("test2").toBool(), true);
5974 QCOMPARE(o->property("test3").toBool(), true);
5979 void tst_qqmlecmascript::functionException()
5981 // QTBUG-24037 - shouldn't crash.
5982 QString errstr = testFileUrl("v8functionException.qml").toString() + QLatin1String(":13: SyntaxError: Unexpected token ILLEGAL");
5983 QTest::ignoreMessage(QtWarningMsg, qPrintable(errstr));
5984 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Exception occurred during compilation of function: dynamicSlot()");
5985 QQmlComponent component(&engine, testFileUrl("v8functionException.qml"));
5986 QObject *o = component.create();
5988 QMetaObject::invokeMethod(o, "dynamicSlot");
5992 // Test the "Qt.include" method
5993 void tst_qqmlecmascript::include()
5995 // Non-library relative include
5997 QQmlComponent component(&engine, testFileUrl("include.qml"));
5998 QObject *o = component.create();
6001 QCOMPARE(o->property("test0").toInt(), 99);
6002 QCOMPARE(o->property("test1").toBool(), true);
6003 QCOMPARE(o->property("test2").toBool(), true);
6004 QCOMPARE(o->property("test2_1").toBool(), true);
6005 QCOMPARE(o->property("test3").toBool(), true);
6006 QCOMPARE(o->property("test3_1").toBool(), true);
6011 // Library relative include
6013 QQmlComponent component(&engine, testFileUrl("include_shared.qml"));
6014 QObject *o = component.create();
6017 QCOMPARE(o->property("test0").toInt(), 99);
6018 QCOMPARE(o->property("test1").toBool(), true);
6019 QCOMPARE(o->property("test2").toBool(), true);
6020 QCOMPARE(o->property("test2_1").toBool(), true);
6021 QCOMPARE(o->property("test3").toBool(), true);
6022 QCOMPARE(o->property("test3_1").toBool(), true);
6029 QQmlComponent component(&engine, testFileUrl("include_callback.qml"));
6030 QObject *o = component.create();
6033 QCOMPARE(o->property("test1").toBool(), true);
6034 QCOMPARE(o->property("test2").toBool(), true);
6035 QCOMPARE(o->property("test3").toBool(), true);
6036 QCOMPARE(o->property("test4").toBool(), true);
6037 QCOMPARE(o->property("test5").toBool(), true);
6038 QCOMPARE(o->property("test6").toBool(), true);
6043 // Including file with ".pragma library"
6045 QQmlComponent component(&engine, testFileUrl("include_pragma.qml"));
6046 QObject *o = component.create();
6048 QCOMPARE(o->property("test1").toInt(), 100);
6055 TestHTTPServer server(8111);
6056 QVERIFY(server.isValid());
6057 server.serveDirectory(dataDirectory());
6059 QQmlComponent component(&engine, testFileUrl("include_remote.qml"));
6060 QObject *o = component.create();
6063 QTRY_VERIFY(o->property("done").toBool() == true);
6064 QTRY_VERIFY(o->property("done2").toBool() == true);
6066 QCOMPARE(o->property("test1").toBool(), true);
6067 QCOMPARE(o->property("test2").toBool(), true);
6068 QCOMPARE(o->property("test3").toBool(), true);
6069 QCOMPARE(o->property("test4").toBool(), true);
6070 QCOMPARE(o->property("test5").toBool(), true);
6072 QCOMPARE(o->property("test6").toBool(), true);
6073 QCOMPARE(o->property("test7").toBool(), true);
6074 QCOMPARE(o->property("test8").toBool(), true);
6075 QCOMPARE(o->property("test9").toBool(), true);
6076 QCOMPARE(o->property("test10").toBool(), true);
6083 TestHTTPServer server(8111);
6084 QVERIFY(server.isValid());
6085 server.serveDirectory(dataDirectory());
6087 QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml"));
6088 QObject *o = component.create();
6091 QTRY_VERIFY(o->property("done").toBool() == true);
6093 QCOMPARE(o->property("test1").toBool(), true);
6094 QCOMPARE(o->property("test2").toBool(), true);
6095 QCOMPARE(o->property("test3").toBool(), true);
6101 void tst_qqmlecmascript::signalHandlers()
6103 QQmlComponent component(&engine, testFileUrl("signalHandlers.qml"));
6104 QObject *o = component.create();
6107 QVERIFY(o->property("count").toInt() == 0);
6108 QMetaObject::invokeMethod(o, "testSignalCall");
6109 QCOMPARE(o->property("count").toInt(), 1);
6111 QMetaObject::invokeMethod(o, "testSignalHandlerCall");
6112 QCOMPARE(o->property("count").toInt(), 1);
6113 QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
6115 QVERIFY(o->property("funcCount").toInt() == 0);
6116 QMetaObject::invokeMethod(o, "testSignalConnection");
6117 QCOMPARE(o->property("funcCount").toInt(), 1);
6119 QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
6120 QCOMPARE(o->property("funcCount").toInt(), 2);
6122 QMetaObject::invokeMethod(o, "testSignalDefined");
6123 QCOMPARE(o->property("definedResult").toBool(), true);
6125 QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
6126 QCOMPARE(o->property("definedHandlerResult").toBool(), true);
6131 void tst_qqmlecmascript::qtbug_10696()
6133 QQmlComponent component(&engine, testFileUrl("qtbug_10696.qml"));
6134 QObject *o = component.create();
6139 void tst_qqmlecmascript::qtbug_11606()
6141 QQmlComponent component(&engine, testFileUrl("qtbug_11606.qml"));
6142 QObject *o = component.create();
6144 QCOMPARE(o->property("test").toBool(), true);
6148 void tst_qqmlecmascript::qtbug_11600()
6150 QQmlComponent component(&engine, testFileUrl("qtbug_11600.qml"));
6151 QObject *o = component.create();
6153 QCOMPARE(o->property("test").toBool(), true);
6157 void tst_qqmlecmascript::qtbug_21864()
6159 QQmlComponent component(&engine, testFileUrl("qtbug_21864.qml"));
6160 QObject *o = component.create();
6162 QCOMPARE(o->property("test").toBool(), true);
6166 void tst_qqmlecmascript::rewriteMultiLineStrings()
6170 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml"));
6171 QObject *o = component.create();
6173 QTRY_COMPARE(o->property("test").toBool(), true);
6178 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings_crlf.1.qml"));
6179 QObject *o = component.create();
6185 void tst_qqmlecmascript::qobjectConnectionListExceptionHandling()
6188 QQmlComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml"));
6189 QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined");
6190 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6191 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6192 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6193 QObject *o = component.create();
6195 QCOMPARE(o->property("test").toBool(), true);
6199 // Reading and writing non-scriptable properties should fail
6200 void tst_qqmlecmascript::nonscriptable()
6202 QQmlComponent component(&engine, testFileUrl("nonscriptable.qml"));
6203 QObject *o = component.create();
6205 QCOMPARE(o->property("readOk").toBool(), true);
6206 QCOMPARE(o->property("writeOk").toBool(), true);
6210 // deleteLater() should not be callable from QML
6211 void tst_qqmlecmascript::deleteLater()
6213 QQmlComponent component(&engine, testFileUrl("deleteLater.qml"));
6214 QObject *o = component.create();
6216 QCOMPARE(o->property("test").toBool(), true);
6220 // objectNameChanged() should be usable from QML
6221 void tst_qqmlecmascript::objectNameChangedSignal()
6223 QQmlComponent component(&engine, testFileUrl("objectNameChangedSignal.qml"));
6224 QObject *o = component.create();
6226 QCOMPARE(o->property("test").toBool(), false);
6227 o->setObjectName("obj");
6228 QCOMPARE(o->property("test").toBool(), true);
6232 // destroyed() should not be usable from QML
6233 void tst_qqmlecmascript::destroyedSignal()
6235 QQmlComponent component(&engine, testFileUrl("destroyedSignal.qml"));
6236 QVERIFY(component.isError());
6238 QString expectedErrorString = component.url().toString() + QLatin1String(":5:5: Cannot assign to non-existent property \"onDestroyed\"");
6239 QCOMPARE(component.errors().at(0).toString(), expectedErrorString);
6242 void tst_qqmlecmascript::in()
6244 QQmlComponent component(&engine, testFileUrl("in.qml"));
6245 QObject *o = component.create();
6247 QCOMPARE(o->property("test1").toBool(), true);
6248 QCOMPARE(o->property("test2").toBool(), true);
6252 void tst_qqmlecmascript::typeOf()
6254 QQmlComponent component(&engine, testFileUrl("typeOf.qml"));
6256 QObject *o = component.create();
6259 QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
6260 QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
6261 QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
6262 QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
6263 QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
6264 QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
6265 QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
6266 QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
6267 QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
6272 void tst_qqmlecmascript::qtbug_24448()
6274 QQmlComponent component(&engine, testFileUrl("qtbug_24448.qml"));
6275 QScopedPointer<QObject> o(component.create());
6277 QVERIFY(o->property("test").toBool());
6280 void tst_qqmlecmascript::sharedAttachedObject()
6282 QQmlComponent component(&engine, testFileUrl("sharedAttachedObject.qml"));
6283 QObject *o = component.create();
6285 QCOMPARE(o->property("test1").toBool(), true);
6286 QCOMPARE(o->property("test2").toBool(), true);
6291 void tst_qqmlecmascript::objectName()
6293 QQmlComponent component(&engine, testFileUrl("objectName.qml"));
6294 QObject *o = component.create();
6297 QCOMPARE(o->property("test1").toString(), QString("hello"));
6298 QCOMPARE(o->property("test2").toString(), QString("ell"));
6300 o->setObjectName("world");
6302 QCOMPARE(o->property("test1").toString(), QString("world"));
6303 QCOMPARE(o->property("test2").toString(), QString("orl"));
6308 void tst_qqmlecmascript::writeRemovesBinding()
6310 QQmlComponent component(&engine, testFileUrl("writeRemovesBinding.qml"));
6311 QObject *o = component.create();
6314 QCOMPARE(o->property("test").toBool(), true);
6319 // Test bindings assigned to alias properties actually assign to the alias' target
6320 void tst_qqmlecmascript::aliasBindingsAssignCorrectly()
6322 QQmlComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml"));
6323 QObject *o = component.create();
6326 QCOMPARE(o->property("test").toBool(), true);
6331 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
6332 void tst_qqmlecmascript::aliasBindingsOverrideTarget()
6335 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml"));
6336 QObject *o = component.create();
6339 QCOMPARE(o->property("test").toBool(), true);
6345 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml"));
6346 QObject *o = component.create();
6349 QCOMPARE(o->property("test").toBool(), true);
6355 QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml"));
6356 QObject *o = component.create();
6359 QCOMPARE(o->property("test").toBool(), true);
6365 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
6366 void tst_qqmlecmascript::aliasWritesOverrideBindings()
6369 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml"));
6370 QObject *o = component.create();
6373 QCOMPARE(o->property("test").toBool(), true);
6379 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml"));
6380 QObject *o = component.create();
6383 QCOMPARE(o->property("test").toBool(), true);
6389 QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml"));
6390 QObject *o = component.create();
6393 QCOMPARE(o->property("test").toBool(), true);
6399 // Allow an alais to a composite element
6401 void tst_qqmlecmascript::aliasToCompositeElement()
6403 QQmlComponent component(&engine, testFileUrl("aliasToCompositeElement.qml"));
6405 QObject *object = component.create();
6406 QVERIFY(object != 0);
6411 void tst_qqmlecmascript::qtbug_20344()
6413 QQmlComponent component(&engine, testFileUrl("qtbug_20344.qml"));
6415 QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
6416 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6418 QObject *object = component.create();
6419 QVERIFY(object != 0);
6424 void tst_qqmlecmascript::revisionErrors()
6427 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml"));
6428 QString url = component.url().toString();
6430 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6431 QString warning2 = url + ":11: ReferenceError: prop2 is not defined";
6432 QString warning3 = url + ":13: ReferenceError: method2 is not defined";
6434 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6435 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6436 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6437 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6438 QVERIFY(object != 0);
6442 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml"));
6443 QString url = component.url().toString();
6445 // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
6446 // method2, prop2 from MyRevisionedClass not available
6447 // method4, prop4 from MyRevisionedSubclass not available
6448 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6449 QString warning2 = url + ":14: ReferenceError: prop2 is not defined";
6450 QString warning3 = url + ":10: ReferenceError: prop4 is not defined";
6451 QString warning4 = url + ":16: ReferenceError: prop4 is not defined";
6452 QString warning5 = url + ":20: ReferenceError: method2 is not defined";
6454 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6455 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6456 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6457 QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
6458 QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
6459 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6460 QVERIFY(object != 0);
6464 QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml"));
6465 QString url = component.url().toString();
6467 // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
6468 // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
6469 QString warning1 = url + ":30: ReferenceError: methodD is not defined";
6470 QString warning2 = url + ":10: ReferenceError: propD is not defined";
6471 QString warning3 = url + ":20: ReferenceError: propD is not defined";
6472 QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6473 QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6474 QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6475 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6476 QVERIFY(object != 0);
6481 void tst_qqmlecmascript::revision()
6484 QQmlComponent component(&engine, testFileUrl("metaobjectRevision.qml"));
6485 QString url = component.url().toString();
6487 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6488 QVERIFY(object != 0);
6492 QQmlComponent component(&engine, testFileUrl("metaobjectRevision2.qml"));
6493 QString url = component.url().toString();
6495 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6496 QVERIFY(object != 0);
6500 QQmlComponent component(&engine, testFileUrl("metaobjectRevision3.qml"));
6501 QString url = component.url().toString();
6503 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6504 QVERIFY(object != 0);
6507 // Test that non-root classes can resolve revisioned methods
6509 QQmlComponent component(&engine, testFileUrl("metaobjectRevision4.qml"));
6511 QObject *object = component.create();
6512 QVERIFY(object != 0);
6513 QCOMPARE(object->property("test").toReal(), 11.);
6518 void tst_qqmlecmascript::realToInt()
6520 QQmlComponent component(&engine, testFileUrl("realToInt.qml"));
6521 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6522 QVERIFY(object != 0);
6524 QMetaObject::invokeMethod(object, "test1");
6525 QCOMPARE(object->value(), int(4));
6526 QMetaObject::invokeMethod(object, "test2");
6527 QCOMPARE(object->value(), int(8));
6530 void tst_qqmlecmascript::urlProperty()
6533 QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml"));
6534 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6535 QVERIFY(object != 0);
6536 object->setStringProperty("http://qt-project.org");
6537 QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
6538 QCOMPARE(object->intProperty(), 123);
6539 QCOMPARE(object->value(), 1);
6540 QCOMPARE(object->property("result").toBool(), true);
6544 void tst_qqmlecmascript::urlPropertyWithEncoding()
6547 QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml"));
6548 MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6549 QVERIFY(object != 0);
6550 object->setStringProperty("http://qt-project.org");
6552 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6553 QCOMPARE(object->urlProperty(), encoded);
6554 QCOMPARE(object->value(), 0); // Interpreting URL as string yields canonicalised version
6555 QCOMPARE(object->property("result").toBool(), true);
6559 void tst_qqmlecmascript::urlListPropertyWithEncoding()
6562 QQmlComponent component(&engine, testFileUrl("urlListProperty.qml"));
6563 QObject *object = component.create();
6564 QVERIFY(object != 0);
6565 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
6566 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
6567 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
6568 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
6569 QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0);
6571 encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6572 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded));
6573 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded));
6574 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6575 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6580 void tst_qqmlecmascript::dynamicString()
6582 QQmlComponent component(&engine, testFileUrl("dynamicString.qml"));
6583 QObject *object = component.create();
6584 QVERIFY(object != 0);
6585 QCOMPARE(object->property("stringProperty").toString(),
6586 QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
6589 void tst_qqmlecmascript::deleteLaterObjectMethodCall()
6591 QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml"));
6592 QObject *object = component.create();
6593 QVERIFY(object != 0);
6596 void tst_qqmlecmascript::automaticSemicolon()
6598 QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml"));
6599 QObject *object = component.create();
6600 QVERIFY(object != 0);
6603 void tst_qqmlecmascript::unaryExpression()
6605 QQmlComponent component(&engine, testFileUrl("unaryExpression.qml"));
6606 QObject *object = component.create();
6607 QVERIFY(object != 0);
6610 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
6611 void tst_qqmlecmascript::doubleEvaluate()
6613 QQmlComponent component(&engine, testFileUrl("doubleEvaluate.qml"));
6614 QObject *object = component.create();
6615 QVERIFY(object != 0);
6616 WriteCounter *wc = qobject_cast<WriteCounter *>(object);
6618 QCOMPARE(wc->count(), 1);
6620 wc->setProperty("x", 9);
6622 QCOMPARE(wc->count(), 2);
6627 static QStringList messages;
6628 static void captureMsgHandler(QtMsgType, const QMessageLogContext &, const QString &msg)
6630 messages.append(msg);
6633 void tst_qqmlecmascript::nonNotifyable()
6635 QV4Compiler::enableV4(false);
6636 QQmlComponent component(&engine, testFileUrl("nonNotifyable.qml"));
6637 QV4Compiler::enableV4(true);
6639 QtMessageHandler old = qInstallMessageHandler(captureMsgHandler);
6641 QObject *object = component.create();
6642 qInstallMessageHandler(old);
6644 QVERIFY(object != 0);
6646 QString expected1 = QLatin1String("QQmlExpression: Expression ") +
6647 component.url().toString() +
6648 QLatin1String(":5 depends on non-NOTIFYable properties:");
6649 QString expected2 = QLatin1String(" ") +
6650 QLatin1String(object->metaObject()->className()) +
6651 QLatin1String("::value");
6653 QCOMPARE(messages.length(), 2);
6654 QCOMPARE(messages.at(0), expected1);
6655 QCOMPARE(messages.at(1), expected2);
6660 void tst_qqmlecmascript::forInLoop()
6662 QQmlComponent component(&engine, testFileUrl("forInLoop.qml"));
6663 QObject *object = component.create();
6664 QVERIFY(object != 0);
6666 QMetaObject::invokeMethod(object, "listProperty");
6668 QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
6669 QCOMPARE(r.size(), 3);
6670 QCOMPARE(r[0],QLatin1String("0=obj1"));
6671 QCOMPARE(r[1],QLatin1String("1=obj2"));
6672 QCOMPARE(r[2],QLatin1String("2=obj3"));
6674 //TODO: should test for in loop for other objects (such as QObjects) as well.
6679 // An object the binding depends on is deleted while the binding is still running
6680 void tst_qqmlecmascript::deleteWhileBindingRunning()
6682 QQmlComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml"));
6683 QObject *object = component.create();
6684 QVERIFY(object != 0);
6688 void tst_qqmlecmascript::qtbug_22679()
6691 object.setStringProperty(QLatin1String("Please work correctly"));
6692 engine.rootContext()->setContextProperty("contextProp", &object);
6694 QQmlComponent component(&engine, testFileUrl("qtbug_22679.qml"));
6695 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6696 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6698 QObject *o = component.create();
6700 QCOMPARE(warningsSpy.count(), 0);
6704 void tst_qqmlecmascript::qtbug_22843_data()
6706 QTest::addColumn<bool>("library");
6708 QTest::newRow("without .pragma library") << false;
6709 QTest::newRow("with .pragma library") << true;
6712 void tst_qqmlecmascript::qtbug_22843()
6714 QFETCH(bool, library);
6716 QString fileName("qtbug_22843");
6718 fileName += QLatin1String(".library");
6719 fileName += QLatin1String(".qml");
6721 QQmlComponent component(&engine, testFileUrl(fileName));
6722 QString url = component.url().toString();
6723 QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )");
6724 QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'");
6726 qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6727 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6728 for (int x = 0; x < 3; ++x) {
6729 warningsSpy.clear();
6730 // For libraries, only the first import attempt should produce a
6731 // SyntaxError warning; subsequent component creation should not
6732 // attempt to reload the script.
6733 bool expectSyntaxError = !library || (x == 0);
6734 if (expectSyntaxError)
6735 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
6736 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
6737 QObject *object = component.create();
6738 QVERIFY(object != 0);
6739 QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
6745 void tst_qqmlecmascript::switchStatement()
6748 QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml"));
6749 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6750 QVERIFY(object != 0);
6752 // `object->value()' is the number of executed statements
6754 object->setStringProperty("A");
6755 QCOMPARE(object->value(), 5);
6757 object->setStringProperty("S");
6758 QCOMPARE(object->value(), 3);
6760 object->setStringProperty("D");
6761 QCOMPARE(object->value(), 3);
6763 object->setStringProperty("F");
6764 QCOMPARE(object->value(), 4);
6766 object->setStringProperty("something else");
6767 QCOMPARE(object->value(), 1);
6771 QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml"));
6772 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6773 QVERIFY(object != 0);
6775 // `object->value()' is the number of executed statements
6777 object->setStringProperty("A");
6778 QCOMPARE(object->value(), 5);
6780 object->setStringProperty("S");
6781 QCOMPARE(object->value(), 3);
6783 object->setStringProperty("D");
6784 QCOMPARE(object->value(), 3);
6786 object->setStringProperty("F");
6787 QCOMPARE(object->value(), 3);
6789 object->setStringProperty("something else");
6790 QCOMPARE(object->value(), 4);
6794 QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml"));
6795 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6796 QVERIFY(object != 0);
6798 // `object->value()' is the number of executed statements
6800 object->setStringProperty("A");
6801 QCOMPARE(object->value(), 5);
6803 object->setStringProperty("S");
6804 QCOMPARE(object->value(), 3);
6806 object->setStringProperty("D");
6807 QCOMPARE(object->value(), 3);
6809 object->setStringProperty("F");
6810 QCOMPARE(object->value(), 3);
6812 object->setStringProperty("something else");
6813 QCOMPARE(object->value(), 6);
6817 QQmlComponent component(&engine, testFileUrl("switchStatement.4.qml"));
6819 QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
6820 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6822 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6823 QVERIFY(object != 0);
6825 // `object->value()' is the number of executed statements
6827 object->setStringProperty("A");
6828 QCOMPARE(object->value(), 5);
6830 object->setStringProperty("S");
6831 QCOMPARE(object->value(), 3);
6833 object->setStringProperty("D");
6834 QCOMPARE(object->value(), 3);
6836 object->setStringProperty("F");
6837 QCOMPARE(object->value(), 3);
6839 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6841 object->setStringProperty("something else");
6845 QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml"));
6846 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6847 QVERIFY(object != 0);
6849 // `object->value()' is the number of executed statements
6851 object->setStringProperty("A");
6852 QCOMPARE(object->value(), 1);
6854 object->setStringProperty("S");
6855 QCOMPARE(object->value(), 1);
6857 object->setStringProperty("D");
6858 QCOMPARE(object->value(), 1);
6860 object->setStringProperty("F");
6861 QCOMPARE(object->value(), 1);
6863 object->setStringProperty("something else");
6864 QCOMPARE(object->value(), 1);
6868 QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml"));
6869 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6870 QVERIFY(object != 0);
6872 // `object->value()' is the number of executed statements
6874 object->setStringProperty("A");
6875 QCOMPARE(object->value(), 123);
6877 object->setStringProperty("S");
6878 QCOMPARE(object->value(), 123);
6880 object->setStringProperty("D");
6881 QCOMPARE(object->value(), 321);
6883 object->setStringProperty("F");
6884 QCOMPARE(object->value(), 321);
6886 object->setStringProperty("something else");
6887 QCOMPARE(object->value(), 0);
6891 void tst_qqmlecmascript::withStatement()
6894 QQmlComponent component(&engine, testFileUrl("withStatement.1.qml"));
6895 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6896 QVERIFY(object != 0);
6898 QCOMPARE(object->value(), 123);
6902 void tst_qqmlecmascript::tryStatement()
6905 QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml"));
6906 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6907 QVERIFY(object != 0);
6909 QCOMPARE(object->value(), 123);
6913 QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml"));
6914 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6915 QVERIFY(object != 0);
6917 QCOMPARE(object->value(), 321);
6921 QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml"));
6922 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6923 QVERIFY(object != 0);
6925 QCOMPARE(object->value(), 1);
6929 QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml"));
6930 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6931 QVERIFY(object != 0);
6933 QCOMPARE(object->value(), 1);
6937 class CppInvokableWithQObjectDerived : public QObject
6941 CppInvokableWithQObjectDerived() {}
6942 ~CppInvokableWithQObjectDerived() {}
6944 Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data)
6946 MyQmlObject *obj = new MyQmlObject();
6947 obj->setStringProperty(data);
6951 Q_INVOKABLE QString getStringProperty(MyQmlObject *obj)
6953 return obj->stringProperty();
6957 void tst_qqmlecmascript::invokableWithQObjectDerived()
6959 CppInvokableWithQObjectDerived invokable;
6963 engine.rootContext()->setContextProperty("invokable", &invokable);
6965 QQmlComponent component(&engine, testFileUrl("qobjectDerivedArgument.qml"));
6967 QObject *object = component.create();
6969 QVERIFY(object != 0);
6970 QVERIFY(object->property("result").value<bool>() == true);
6976 void tst_qqmlecmascript::realTypePrecision()
6978 // Properties and signal parameters of type real should have double precision.
6979 QQmlComponent component(&engine, testFileUrl("realTypePrecision.qml"));
6980 QScopedPointer<QObject> object(component.create());
6981 QVERIFY(object != 0);
6982 QCOMPARE(object->property("test").toDouble(), 1234567890.);
6983 QCOMPARE(object->property("test2").toDouble(), 1234567890.);
6984 QCOMPARE(object->property("test3").toDouble(), 1234567890.);
6985 QCOMPARE(object->property("test4").toDouble(), 1234567890.);
6986 QCOMPARE(object->property("test5").toDouble(), 1234567890.);
6987 QCOMPARE(object->property("test6").toDouble(), 1234567890.*2);
6990 void tst_qqmlecmascript::registeredFlagMethod()
6993 QQmlComponent component(&engine, testFileUrl("registeredFlagMethod.qml"));
6994 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6995 QVERIFY(object != 0);
6997 QCOMPARE(object->buttons(), 0);
6998 emit object->basicSignal();
6999 QCOMPARE(object->buttons(), Qt::RightButton);
7005 void tst_qqmlecmascript::replaceBinding()
7008 QQmlComponent c(&engine, testFileUrl("replaceBinding.qml"));
7009 QObject *obj = c.create();
7012 QVERIFY(obj->property("success").toBool());
7016 void tst_qqmlecmascript::deleteRootObjectInCreation()
7020 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.qml"));
7021 QObject *obj = c.create();
7023 QVERIFY(obj->property("rootIndestructible").toBool());
7024 QVERIFY(!obj->property("childDestructible").toBool());
7026 QVERIFY(obj->property("childDestructible").toBool());
7031 QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.2.qml"));
7032 QObject *object = c.create();
7033 QVERIFY(object != 0);
7034 QVERIFY(object->property("testConditionsMet").toBool());
7039 void tst_qqmlecmascript::onDestruction()
7042 // Delete object manually to invoke the associated handlers,
7043 // prior to engine destruction.
7045 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
7046 QObject *obj = c.create();
7052 // In this case, the teardown of the engine causes deletion
7053 // of contexts and child items. This triggers the
7054 // onDestruction handler of a (previously .destroy()ed)
7055 // component instance. This shouldn't crash.
7057 QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
7058 QObject *obj = c.create();
7063 struct EventProcessor : public QObject
7067 Q_INVOKABLE void process()
7069 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
7070 QCoreApplication::processEvents();
7074 void tst_qqmlecmascript::bindingSuppression()
7077 EventProcessor processor;
7078 engine.rootContext()->setContextProperty("pendingEvents", &processor);
7080 transientErrorsMsgCount = 0;
7081 QtMessageHandler old = qInstallMessageHandler(transientErrorsMsgHandler);
7083 QQmlComponent c(&engine, testFileUrl("bindingSuppression.qml"));
7084 QObject *obj = c.create();
7088 qInstallMessageHandler(old);
7089 QCOMPARE(transientErrorsMsgCount, 0);
7092 void tst_qqmlecmascript::signalEmitted()
7095 // calling destroy on the sibling.
7097 QQmlComponent c(&engine, testFileUrl("signalEmitted.2.qml"));
7098 QObject *obj = c.create();
7100 QTRY_VERIFY(obj->property("success").toBool());
7105 // allowing gc to clean up the sibling.
7107 QQmlComponent c(&engine, testFileUrl("signalEmitted.3.qml"));
7108 QObject *obj = c.create();
7110 gc(engine); // should collect c1.
7111 QTRY_VERIFY(obj->property("success").toBool());
7116 // allowing gc to clean up the sibling after manually destroying target.
7118 QQmlComponent c(&engine, testFileUrl("signalEmitted.4.qml"));
7119 QObject *obj = c.create();
7121 gc(engine); // should collect c1.
7122 QMetaObject::invokeMethod(obj, "destroyC2");
7123 QTRY_VERIFY(obj->property("success").toBool()); // handles events (incl. delete later).
7129 void tst_qqmlecmascript::threadSignal()
7132 QQmlComponent c(&engine, testFileUrl("threadSignal.qml"));
7133 QObject *object = c.create();
7134 QVERIFY(object != 0);
7135 QTRY_VERIFY(object->property("passed").toBool());
7139 QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml"));
7140 QObject *object = c.create();
7141 QVERIFY(object != 0);
7142 QSignalSpy doneSpy(object, SIGNAL(done(const QString &)));
7143 QMetaObject::invokeMethod(object, "doIt");
7144 QTRY_VERIFY(object->property("passed").toBool());
7145 QCOMPARE(doneSpy.count(), 1);
7150 // ensure that the qqmldata::destroyed() handler doesn't cause problems
7151 void tst_qqmlecmascript::qqmldataDestroyed()
7153 // gc cleans up a qobject, later the qqmldata destroyed handler will run.
7155 QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.qml"));
7156 QObject *object = c.create();
7157 QVERIFY(object != 0);
7158 // now gc causing the collection of the dynamically constructed object.
7159 engine.collectGarbage();
7160 engine.collectGarbage();
7161 // now process events to allow deletion (calling qqmldata::destroyed())
7162 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
7163 QCoreApplication::processEvents();
7168 // in this case, the object has CPP ownership, and the gc will
7169 // be triggered during its beginCreate stage.
7171 QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.2.qml"));
7172 QObject *object = c.create();
7173 QVERIFY(object != 0);
7174 QVERIFY(object->property("testConditionsMet").toBool());
7175 // the gc() within the handler will have triggered the weak
7176 // qobject reference callback. If that incorrectly disposes
7177 // the handle, when the qqmldata::destroyed() handler is
7178 // called due to object deletion we will see a crash.
7180 // shouldn't have crashed.
7184 void tst_qqmlecmascript::secondAlias()
7186 QQmlComponent c(&engine, testFileUrl("secondAlias.qml"));
7187 QObject *object = c.create();
7188 QVERIFY(object != 0);
7189 QCOMPARE(object->property("test").toInt(), 200);
7193 // An alias to a var property works
7194 void tst_qqmlecmascript::varAlias()
7196 QQmlComponent c(&engine, testFileUrl("varAlias.qml"));
7197 QObject *object = c.create();
7198 QVERIFY(object != 0);
7199 QCOMPARE(object->property("test").toInt(), 192);
7203 // Used to trigger an assert in the lazy meta object creation stage
7204 void tst_qqmlecmascript::overrideDataAssert()
7206 QQmlComponent c(&engine, testFileUrl("overrideDataAssert.qml"));
7207 QObject *object = c.create();
7208 QVERIFY(object != 0);
7209 object->metaObject();
7213 void tst_qqmlecmascript::fallbackBindings_data()
7215 QTest::addColumn<QString>("source");
7217 QTest::newRow("Property without fallback") << "fallbackBindings.1.qml";
7218 QTest::newRow("Property fallback") << "fallbackBindings.2.qml";
7219 QTest::newRow("SingletonType without fallback") << "fallbackBindings.3.qml";
7220 QTest::newRow("SingletonType fallback") << "fallbackBindings.4.qml";
7221 QTest::newRow("Attached without fallback") << "fallbackBindings.5.qml";
7222 QTest::newRow("Attached fallback") << "fallbackBindings.6.qml";
7223 QTest::newRow("Subproperty without fallback") << "fallbackBindings.7.qml";
7224 QTest::newRow("Subproperty fallback") << "fallbackBindings.8.qml";
7227 void tst_qqmlecmascript::fallbackBindings()
7229 QFETCH(QString, source);
7231 QQmlComponent component(&engine, testFileUrl(source));
7232 QScopedPointer<QObject> object(component.create());
7233 QVERIFY(object != 0);
7235 QCOMPARE(object->property("success").toBool(), true);
7238 void tst_qqmlecmascript::propertyOverride()
7240 QQmlComponent component(&engine, testFileUrl("propertyOverride.qml"));
7241 QScopedPointer<QObject> object(component.create());
7242 QVERIFY(object != 0);
7244 QCOMPARE(object->property("success").toBool(), true);
7247 void tst_qqmlecmascript::sequenceSort_data()
7249 QTest::addColumn<QString>("function");
7250 QTest::addColumn<bool>("useComparer");
7252 QTest::newRow("qtbug_25269") << "test_qtbug_25269" << false;
7254 const char *types[] = { "alphabet", "numbers", "reals" };
7255 const char *sort[] = { "insertionSort", "quickSort" };
7257 for (size_t t=0 ; t < sizeof(types)/sizeof(types[0]) ; ++t) {
7258 for (size_t s=0 ; s < sizeof(sort)/sizeof(sort[0]) ; ++s) {
7259 for (int c=0 ; c < 2 ; ++c) {
7260 QString testName = QLatin1String(types[t]) + QLatin1String("_") + QLatin1String(sort[s]);
7261 QString fnName = QLatin1String("test_") + testName;
7262 bool useComparer = c != 0;
7263 testName += useComparer ? QLatin1String("[custom]") : QLatin1String("[default]");
7264 QTest::newRow(testName.toAscii().constData()) << fnName << useComparer;
7270 void tst_qqmlecmascript::sequenceSort()
7272 QFETCH(QString, function);
7273 QFETCH(bool, useComparer);
7275 QQmlComponent component(&engine, testFileUrl("sequenceSort.qml"));
7277 QObject *object = component.create();
7279 qDebug() << component.errorString();
7280 QVERIFY(object != 0);
7283 QMetaObject::invokeMethod(object, function.toAscii().constData(), Q_RETURN_ARG(QVariant, q), Q_ARG(QVariant, useComparer));
7284 QVERIFY(q.toBool() == true);
7289 void tst_qqmlecmascript::concatenatedStringPropertyAccess()
7291 QQmlComponent component(&engine, testFileUrl("concatenatedStringPropertyAccess.qml"));
7292 QObject *object = component.create();
7294 QVERIFY(object->property("success").toBool());
7298 void tst_qqmlecmascript::jsOwnedObjectsDeletedOnEngineDestroy()
7300 QQmlEngine *myEngine = new QQmlEngine;
7302 MyDeleteObject deleteObject;
7303 deleteObject.setObjectName("deleteObject");
7304 QObject * const object1 = new QObject;
7305 QObject * const object2 = new QObject;
7306 object1->setObjectName("object1");
7307 object2->setObjectName("object2");
7308 deleteObject.setObject1(object1);
7309 deleteObject.setObject2(object2);
7311 // Objects returned by function calls get marked as destructible, but objects returned by
7312 // property getters do not - therefore we explicitly set the object as destructible.
7313 QQmlEngine::setObjectOwnership(object2, QQmlEngine::JavaScriptOwnership);
7315 myEngine->rootContext()->setContextProperty("deleteObject", &deleteObject);
7316 QQmlComponent component(myEngine, testFileUrl("jsOwnedObjectsDeletedOnEngineDestroy.qml"));
7317 QObject *object = component.create();
7320 // Destroying the engine should delete all JS owned QObjects
7321 QSignalSpy spy1(object1, SIGNAL(destroyed()));
7322 QSignalSpy spy2(object2, SIGNAL(destroyed()));
7324 QCOMPARE(spy1.count(), 1);
7325 QCOMPARE(spy2.count(), 1);
7330 QTEST_MAIN(tst_qqmlecmascript)
7332 #include "tst_qqmlecmascript.moc"