Autotests: Use qInstallMessageHandler
[profile/ivi/qtdeclarative.git] / tests / auto / qml / qqmlecmascript / tst_qqmlecmascript.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
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"
57
58 /*
59 This test covers evaluation of ECMAScript expressions and bindings from within
60 QML.  This does not include static QML language issues.
61
62 Static QML language issues are covered in qmllanguage
63 */
64
65 class tst_qqmlecmascript : public QQmlDataTest
66 {
67     Q_OBJECT
68 public:
69     tst_qqmlecmascript() {}
70
71 private slots:
72     void initTestCase();
73     void assignBasicTypes();
74     void assignDate_data();
75     void assignDate();
76     void exportDate_data();
77     void exportDate();
78     void idShortcutInvalidates();
79     void boolPropertiesEvaluateAsBool();
80     void methods();
81     void signalAssignment();
82     void signalArguments();
83     void bindingLoop();
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();
94     void enums();
95     void valueTypeFunctions();
96     void constantsOverrideBindings();
97     void outerBindingOverridesInnerBinding();
98     void aliasPropertyAndBinding();
99     void aliasPropertyReset();
100     void nonExistentAttachedObject();
101     void scope();
102     void importScope();
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();
115     void scriptErrors();
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();
127     void jsObject();
128     void undefinedResetsProperty();
129     void listToVariant();
130     void listAssignment();
131     void multiEngineObject();
132     void deletedObject();
133     void attachedPropertyScope();
134     void scriptConnect();
135     void scriptDisconnect();
136     void ownership();
137     void cppOwnershipReturnValue();
138     void ownershipCustomReturnValue();
139     void ownershipRootObject();
140     void ownershipConsistency();
141     void ownershipQmlIncubated();
142     void qlistqobjectMethods();
143     void strictlyEquals();
144     void compiled();
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();
167     void propertyVar();
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();
184     void stringArg();
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();
195     void sequenceSort();
196     void qtbug_22464();
197     void qtbug_21580();
198     void singleV8BindingDestroyedDuringEvaluation();
199     void bug1();
200 #ifndef QT_NO_WIDGETS
201     void bug2();
202 #endif
203     void dynamicCreationCrash();
204     void dynamicCreationOwnership();
205     void regExpBug();
206     void nullObjectBinding();
207     void deletedEngine();
208     void libraryScriptAssert();
209     void variantsAssignedUndefined();
210     void qtbug_9792();
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();
219     void eval();
220     void function();
221     void functionException();
222     void qtbug_10696();
223     void qtbug_11606();
224     void qtbug_11600();
225     void qtbug_21864();
226     void qobjectConnectionListExceptionHandling();
227     void nonscriptable();
228     void deleteLater();
229     void objectNameChangedSignal();
230     void destroyedSignal();
231     void in();
232     void typeOf();
233     void qtbug_24448();
234     void sharedAttachedObject();
235     void objectName();
236     void writeRemovesBinding();
237     void aliasBindingsAssignCorrectly();
238     void aliasBindingsOverrideTarget();
239     void aliasWritesOverrideBindings();
240     void aliasToCompositeElement();
241     void realToInt();
242     void urlProperty();
243     void urlPropertyWithEncoding();
244     void urlListPropertyWithEncoding();
245     void dynamicString();
246     void include();
247     void signalHandlers();
248     void doubleEvaluate();
249     void forInLoop();
250     void nonNotifyable();
251     void deleteWhileBindingRunning();
252     void callQtInvokables();
253     void invokableObjectArg();
254     void invokableObjectRet();
255     void invokableEnumRet();
256     void qtbug_20344();
257     void qtbug_22679();
258     void qtbug_22843_data();
259     void qtbug_22843();
260     void rewriteMultiLineStrings();
261     void revisionErrors();
262     void revision();
263     void invokableWithQObjectDerived();
264     void realTypePrecision();
265     void registeredFlagMethod();
266     void deleteLaterObjectMethodCall();
267     void automaticSemicolon();
268     void unaryExpression();
269     void switchStatement();
270     void withStatement();
271     void tryStatement();
272     void replaceBinding();
273     void deleteRootObjectInCreation();
274     void onDestruction();
275     void bindingSuppression();
276     void signalEmitted();
277     void threadSignal();
278     void qqmldataDestroyed();
279     void secondAlias();
280     void varAlias();
281     void overrideDataAssert();
282     void fallbackBindings_data();
283     void fallbackBindings();
284     void propertyOverride();
285     void concatenatedStringPropertyAccess();
286     void jsOwnedObjectsDeletedOnEngineDestroy();
287
288 private:
289     static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
290     QQmlEngine engine;
291 };
292
293 void tst_qqmlecmascript::initTestCase()
294 {
295     QQmlDataTest::initTestCase();
296     registerTypes();
297
298     QString dataDir(dataDirectory() + QLatin1Char('/') + QLatin1String("lib"));
299     engine.addImportPath(dataDir);
300 }
301
302 void tst_qqmlecmascript::assignBasicTypes()
303 {
304     {
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")));
331     delete object;
332     }
333     {
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")));
360     delete object;
361     }
362 }
363
364 void tst_qqmlecmascript::assignDate_data()
365 {
366     QTest::addColumn<QUrl>("source");
367
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");
375 }
376
377 void tst_qqmlecmascript::assignDate()
378 {
379     QFETCH(QUrl, source);
380
381     QQmlComponent component(&engine, source);
382     QScopedPointer<QObject> obj(component.create());
383     MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
384     QVERIFY(object != 0);
385
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());
390
391     QCOMPARE(object->dateProperty(), expectedDate);
392     QCOMPARE(object->dateTimeProperty(), expectedDateTime);
393     QCOMPARE(object->dateTimeProperty2(), expectedDateTime2);
394     QCOMPARE(object->boolProperty(), true);
395 }
396
397 void tst_qqmlecmascript::exportDate_data()
398 {
399     QTest::addColumn<QUrl>("source");
400     QTest::addColumn<QDateTime>("datetime");
401
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
404
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);
409
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);
414     {
415         QDateTime dt(date, early, Qt::OffsetFromUTC);
416         dt.setUtcOffset(offset);
417         QTest::newRow("+11:30 early") << testFileUrl("exportDate.5.qml") << dt;
418     }
419     {
420         QDateTime dt(date, late, Qt::OffsetFromUTC);
421         dt.setUtcOffset(offset);
422         QTest::newRow("+11:30 late") << testFileUrl("exportDate.6.qml") << dt;
423     }
424     {
425         QDateTime dt(date, early, Qt::OffsetFromUTC);
426         dt.setUtcOffset(-offset);
427         QTest::newRow("-11:30 early") << testFileUrl("exportDate.7.qml") << dt;
428     }
429     {
430         QDateTime dt(date, late, Qt::OffsetFromUTC);
431         dt.setUtcOffset(-offset);
432         QTest::newRow("-11:30 late") << testFileUrl("exportDate.8.qml") << dt;
433     }
434 }
435
436 void tst_qqmlecmascript::exportDate()
437 {
438     QFETCH(QUrl, source);
439     QFETCH(QDateTime, datetime);
440
441     DateTimeExporter exporter(datetime);
442
443     QQmlEngine e;
444     e.rootContext()->setContextProperty("datetimeExporter", &exporter);
445
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);
451 }
452
453 void tst_qqmlecmascript::idShortcutInvalidates()
454 {
455     {
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);
462         delete object;
463     }
464
465     {
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);
472         delete object;
473     }
474 }
475
476 void tst_qqmlecmascript::boolPropertiesEvaluateAsBool()
477 {
478     {
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"));
483         delete object;
484     }
485     {
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"));
490         delete object;
491     }
492 }
493
494 void tst_qqmlecmascript::signalAssignment()
495 {
496     {
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"));
503         delete object;
504     }
505
506     {
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"));
513         delete object;
514     }
515
516     {
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);
521     }
522
523     {
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);
528     }
529 }
530
531 void tst_qqmlecmascript::signalArguments()
532 {
533     {
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);
542         delete object;
543     }
544
545     {
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);
554         delete object;
555     }
556 }
557
558 void tst_qqmlecmascript::methods()
559 {
560     {
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);
569         delete object;
570     }
571
572     {
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);
581         delete object;
582     }
583
584     {
585         QQmlComponent component(&engine, testFileUrl("methods.3.qml"));
586         QObject *object = component.create();
587         QVERIFY(object != 0);
588         QCOMPARE(object->property("test").toInt(), 19);
589         delete object;
590     }
591
592     {
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);
599         delete object;
600     }
601
602     {
603         QQmlComponent component(&engine, testFileUrl("methods.5.qml"));
604         QObject *object = component.create();
605         QVERIFY(object != 0);
606         QCOMPARE(object->property("test").toInt(), 9);
607         delete object;
608     }
609 }
610
611 void tst_qqmlecmascript::bindingLoop()
612 {
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);
618     delete object;
619 }
620
621 void tst_qqmlecmascript::basicExpressions_data()
622 {
623     QTest::addColumn<QString>("expression");
624     QTest::addColumn<QVariant>("result");
625     QTest::addColumn<bool>("nest");
626
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;
646 }
647
648 void tst_qqmlecmascript::basicExpressions()
649 {
650     QFETCH(QString, expression);
651     QFETCH(QVariant, result);
652     QFETCH(bool, nest);
653
654     MyQmlObject object1;
655     MyQmlObject object2;
656     MyQmlObject object3;
657     MyDefaultObject1 default1;
658     MyDefaultObject3 default3;
659     object1.setStringProperty("Object1");
660     object2.setStringProperty("Object2");
661     object3.setStringProperty("Object3");
662
663     QQmlContext context(engine.rootContext());
664     QQmlContext nestedContext(&context);
665
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));
675
676     MyExpression expr(nest?&nestedContext:&context, expression);
677     QCOMPARE(expr.evaluate(), result);
678 }
679
680 void tst_qqmlecmascript::arrayExpressions()
681 {
682     QObject obj1;
683     QObject obj2;
684     QObject obj3;
685
686     QQmlContext context(engine.rootContext());
687     context.setContextProperty("a", &obj1);
688     context.setContextProperty("b", &obj2);
689     context.setContextProperty("c", &obj3);
690
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);
700 }
701
702 // Tests that modifying a context property will reevaluate expressions
703 void tst_qqmlecmascript::contextPropertiesTriggerReeval()
704 {
705     QQmlContext context(engine.rootContext());
706     MyQmlObject object1;
707     MyQmlObject object2;
708     MyQmlObject *object3 = new MyQmlObject;
709
710     object1.setStringProperty("Hello");
711     object2.setStringProperty("World");
712
713     context.setContextProperty("testProp", QVariant(1));
714     context.setContextProperty("testObj", &object1);
715     context.setContextProperty("testObj2", object3);
716
717     { 
718         MyExpression expr(&context, "testProp + 1");
719         QCOMPARE(expr.changed, false);
720         QCOMPARE(expr.evaluate(), QVariant(2));
721
722         context.setContextProperty("testProp", QVariant(2));
723         QCOMPARE(expr.changed, true);
724         QCOMPARE(expr.evaluate(), QVariant(3));
725     }
726
727     { 
728         MyExpression expr(&context, "testProp + testProp + testProp");
729         QCOMPARE(expr.changed, false);
730         QCOMPARE(expr.evaluate(), QVariant(6));
731
732         context.setContextProperty("testProp", QVariant(4));
733         QCOMPARE(expr.changed, true);
734         QCOMPARE(expr.evaluate(), QVariant(12));
735     }
736
737     { 
738         MyExpression expr(&context, "testObj.stringProperty");
739         QCOMPARE(expr.changed, false);
740         QCOMPARE(expr.evaluate(), QVariant("Hello"));
741
742         context.setContextProperty("testObj", &object2);
743         QCOMPARE(expr.changed, true);
744         QCOMPARE(expr.evaluate(), QVariant("World"));
745     }
746
747     { 
748         MyExpression expr(&context, "testObj.stringProperty /**/");
749         QCOMPARE(expr.changed, false);
750         QCOMPARE(expr.evaluate(), QVariant("World"));
751
752         context.setContextProperty("testObj", &object1);
753         QCOMPARE(expr.changed, true);
754         QCOMPARE(expr.evaluate(), QVariant("Hello"));
755     }
756
757     { 
758         MyExpression expr(&context, "testObj2");
759         QCOMPARE(expr.changed, false);
760         QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3));
761     }
762
763     delete object3;
764 }
765
766 void tst_qqmlecmascript::objectPropertiesTriggerReeval()
767 {
768     QQmlContext context(engine.rootContext());
769     MyQmlObject object1;
770     MyQmlObject object2;
771     MyQmlObject object3;
772     context.setContextProperty("testObj", &object1);
773
774     object1.setStringProperty(QLatin1String("Hello"));
775     object2.setStringProperty(QLatin1String("Dog"));
776     object3.setStringProperty(QLatin1String("Cat"));
777
778     { 
779         MyExpression expr(&context, "testObj.stringProperty");
780         QCOMPARE(expr.changed, false);
781         QCOMPARE(expr.evaluate(), QVariant("Hello"));
782
783         object1.setStringProperty(QLatin1String("World"));
784         QCOMPARE(expr.changed, true);
785         QCOMPARE(expr.evaluate(), QVariant("World"));
786     }
787
788     { 
789         MyExpression expr(&context, "testObj.objectProperty.stringProperty");
790         QCOMPARE(expr.changed, false);
791         QCOMPARE(expr.evaluate(), QVariant());
792
793         object1.setObjectProperty(&object2);
794         QCOMPARE(expr.changed, true);
795         expr.changed = false;
796         QCOMPARE(expr.evaluate(), QVariant("Dog"));
797
798         object1.setObjectProperty(&object3);
799         QCOMPARE(expr.changed, true);
800         expr.changed = false;
801         QCOMPARE(expr.evaluate(), QVariant("Cat"));
802
803         object1.setObjectProperty(0);
804         QCOMPARE(expr.changed, true);
805         expr.changed = false;
806         QCOMPARE(expr.evaluate(), QVariant());
807
808         object1.setObjectProperty(&object3);
809         QCOMPARE(expr.changed, true);
810         expr.changed = false;
811         QCOMPARE(expr.evaluate(), QVariant("Cat"));
812
813         object3.setStringProperty("Donkey");
814         QCOMPARE(expr.changed, true);
815         expr.changed = false;
816         QCOMPARE(expr.evaluate(), QVariant("Donkey"));
817     }
818 }
819
820 void tst_qqmlecmascript::deferredProperties()
821 {
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);
838
839     delete object;
840 }
841
842 // Check errors on deferred properties are correctly emitted
843 void tst_qqmlecmascript::deferredPropertiesErrors()
844 {
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);
852
853     QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject*";
854     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
855
856     qmlExecuteDeferred(object);
857
858     delete object;
859 }
860
861 void tst_qqmlecmascript::extensionObjects()
862 {
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);
873
874     MyExtendedObject *nested = qobject_cast<MyExtendedObject*>(qvariant_cast<QObject *>(object->property("nested")));
875     QVERIFY(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);
882
883     delete object;
884 }
885
886 void tst_qqmlecmascript::overrideExtensionProperties()
887 {
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);
894
895     delete object;
896 }
897
898 void tst_qqmlecmascript::attachedProperties()
899 {
900     {
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);
908         delete object;
909     }
910
911     {
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);
919
920         delete object;
921     }
922
923     {
924         QQmlComponent component(&engine, testFileUrl("writeAttachedProperty.qml"));
925         QObject *object = component.create();
926         QVERIFY(object != 0);
927
928         QMetaObject::invokeMethod(object, "writeValue2");
929
930         MyQmlAttachedObject *attached =
931             qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
932         QVERIFY(attached != 0);
933
934         QCOMPARE(attached->value2(), 9);
935         delete object;
936     }
937 }
938
939 void tst_qqmlecmascript::enums()
940 {
941     // Existent enums
942     {
943     QQmlComponent component(&engine, testFileUrl("enums.1.qml"));
944     QObject *object = component.create();
945     QVERIFY(object != 0);
946
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);
963
964     delete object;
965     }
966     // Non-existent enums
967     {
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));
985
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);
992
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");
996
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");
1000
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");
1004
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");
1008
1009     delete object;
1010     }
1011     // Enums as literals
1012     {
1013     QQmlComponent component(&engine, testFileUrl("enums.3.qml"));
1014     QObject *object = component.create();
1015     QVERIFY(object != 0);
1016
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);
1028
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);
1040
1041     delete object;
1042     }
1043 }
1044
1045 void tst_qqmlecmascript::valueTypeFunctions()
1046 {
1047     QQmlComponent component(&engine, testFileUrl("valueTypeFunctions.qml"));
1048     MyTypeObject *obj = qobject_cast<MyTypeObject*>(component.create());
1049     QVERIFY(obj != 0);
1050     QCOMPARE(obj->rectProperty(), QRect(0,0,100,100));
1051     QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5));
1052
1053     delete obj;
1054 }
1055
1056 /* 
1057 Tests that writing a constant to a property with a binding on it disables the
1058 binding.
1059 */
1060 void tst_qqmlecmascript::constantsOverrideBindings()
1061 {
1062     // From ECMAScript
1063     {
1064         QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.1.qml"));
1065         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1066         QVERIFY(object != 0);
1067
1068         QCOMPARE(object->property("c2").toInt(), 0);
1069         object->setProperty("c1", QVariant(9));
1070         QCOMPARE(object->property("c2").toInt(), 9);
1071
1072         emit object->basicSignal();
1073
1074         QCOMPARE(object->property("c2").toInt(), 13);
1075         object->setProperty("c1", QVariant(8));
1076         QCOMPARE(object->property("c2").toInt(), 13);
1077
1078         delete object;
1079     }
1080
1081     // During construction
1082     {
1083         QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.2.qml"));
1084         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1085         QVERIFY(object != 0);
1086
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);
1092
1093         delete object;
1094     }
1095
1096 #if 0
1097     // From C++
1098     {
1099         QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.3.qml"));
1100         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1101         QVERIFY(object != 0);
1102
1103         QCOMPARE(object->property("c2").toInt(), 0);
1104         object->setProperty("c1", QVariant(9));
1105         QCOMPARE(object->property("c2").toInt(), 9);
1106
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);
1112
1113         delete object;
1114     }
1115 #endif
1116
1117     // Using an alias
1118     {
1119         QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.4.qml"));
1120         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1121         QVERIFY(object != 0);
1122
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);
1128
1129         delete object;
1130     }
1131 }
1132
1133 /*
1134 Tests that assigning a binding to a property that already has a binding causes
1135 the original binding to be disabled.
1136 */
1137 void tst_qqmlecmascript::outerBindingOverridesInnerBinding()
1138 {
1139     QQmlComponent component(&engine, 
1140                            testFileUrl("outerBindingOverridesInnerBinding.qml"));
1141     MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1142     QVERIFY(object != 0);
1143
1144     QCOMPARE(object->property("c1").toInt(), 0);
1145     QCOMPARE(object->property("c2").toInt(), 0);
1146     QCOMPARE(object->property("c3").toInt(), 0);
1147
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);
1152
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);
1157
1158     delete object;
1159 }
1160
1161 /*
1162 Access a non-existent attached object.  
1163
1164 Tests for a regression where this used to crash.
1165 */
1166 void tst_qqmlecmascript::nonExistentAttachedObject()
1167 {
1168     QQmlComponent component(&engine, testFileUrl("nonExistentAttachedObject.qml"));
1169
1170     QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString";
1171     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
1172
1173     QObject *object = component.create();
1174     QVERIFY(object != 0);
1175
1176     delete object;
1177 }
1178
1179 void tst_qqmlecmascript::scope()
1180 {
1181     {
1182         QQmlComponent component(&engine, testFileUrl("scope.qml"));
1183         QObject *object = component.create();
1184         QVERIFY(object != 0);
1185
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);
1196
1197         delete object;
1198     }
1199
1200     {
1201         QQmlComponent component(&engine, testFileUrl("scope.2.qml"));
1202         QObject *object = component.create();
1203         QVERIFY(object != 0);
1204
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);
1211
1212         delete object;
1213     }
1214
1215     {
1216         QQmlComponent component(&engine, testFileUrl("scope.3.qml"));
1217         QObject *object = component.create();
1218         QVERIFY(object != 0);
1219
1220         QCOMPARE(object->property("test1").toBool(), true);
1221         QCOMPARE(object->property("test2").toBool(), true);
1222         QCOMPARE(object->property("test3").toBool(), true);
1223
1224         delete object;
1225     }
1226
1227     // Signal argument scope
1228     {
1229         QQmlComponent component(&engine, testFileUrl("scope.4.qml"));
1230         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1231         QVERIFY(object != 0);
1232
1233         QCOMPARE(object->property("test").toInt(), 0);
1234         QCOMPARE(object->property("test2").toString(), QString());
1235
1236         emit object->argumentSignal(13, "Argument Scope", 9, MyQmlObject::EnumValue4, Qt::RightButton);
1237
1238         QCOMPARE(object->property("test").toInt(), 13);
1239         QCOMPARE(object->property("test2").toString(), QString("Argument Scope"));
1240
1241         delete object;
1242     }
1243
1244     {
1245         QQmlComponent component(&engine, testFileUrl("scope.5.qml"));
1246         QObject *object = component.create();
1247         QVERIFY(object != 0);
1248
1249         QCOMPARE(object->property("test1").toBool(), true);
1250         QCOMPARE(object->property("test2").toBool(), true);
1251
1252         delete object;
1253     }
1254
1255     {
1256         QQmlComponent component(&engine, testFileUrl("scope.6.qml"));
1257         QObject *object = component.create();
1258         QVERIFY(object != 0);
1259
1260         QCOMPARE(object->property("test").toBool(), true);
1261
1262         delete object;
1263     }
1264 }
1265
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()
1269 {
1270     QQmlComponent component(&engine, testFileUrl("importScope.qml"));
1271     QObject *o = component.create();
1272     QVERIFY(o != 0);
1273
1274     QCOMPARE(o->property("test").toInt(), 240);
1275
1276     delete o;
1277 }
1278
1279 /*
1280 Tests that "any" type passes through a synthesized signal parameter.  This
1281 is essentially a test of QQmlMetaType::copy()
1282 */
1283 void tst_qqmlecmascript::signalParameterTypes()
1284 {
1285     QQmlComponent component(&engine, testFileUrl("signalParameterTypes.qml"));
1286     MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1287     QVERIFY(object != 0);
1288
1289     emit object->basicSignal();
1290
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);
1297
1298     delete object;
1299 }
1300
1301 /*
1302 Test that two JS objects for the same QObject compare as equal.
1303 */
1304 void tst_qqmlecmascript::objectsCompareAsEqual()
1305 {
1306     QQmlComponent component(&engine, testFileUrl("objectsCompareAsEqual.qml"));
1307     QObject *object = component.create();
1308     QVERIFY(object != 0);
1309
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);
1315
1316     delete object;
1317 }
1318
1319 /*
1320 Confirm bindings and alias properties can coexist.
1321
1322 Tests for a regression where the binding would not reevaluate.
1323 */
1324 void tst_qqmlecmascript::aliasPropertyAndBinding()
1325 {
1326     QQmlComponent component(&engine, testFileUrl("aliasPropertyAndBinding.qml"));
1327     QObject *object = component.create();
1328     QVERIFY(object != 0);
1329
1330     QCOMPARE(object->property("c2").toInt(), 3);
1331     QCOMPARE(object->property("c3").toInt(), 3);
1332
1333     object->setProperty("c2", QVariant(19));
1334
1335     QCOMPARE(object->property("c2").toInt(), 19);
1336     QCOMPARE(object->property("c3").toInt(), 19);
1337
1338     delete object;
1339 }
1340
1341 /*
1342 Ensure that we can write undefined value to an alias property,
1343 and that the aliased property is reset correctly if possible.
1344 */
1345 void tst_qqmlecmascript::aliasPropertyReset()
1346 {
1347     QObject *object = 0;
1348
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));
1358     delete object;
1359
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));
1369     delete object;
1370
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));
1382     delete object;
1383
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);
1392     delete loader;
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);
1398     delete object;
1399
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.
1405     delete object;
1406
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));
1419     delete object;
1420 }
1421
1422 void tst_qqmlecmascript::componentCreation_data()
1423 {
1424     QTest::addColumn<QString>("method");
1425     QTest::addColumn<QString>("creationError");
1426     QTest::addColumn<QString>("createdParent");
1427
1428     QTest::newRow("url")
1429         << "url"
1430         << ""
1431         << "";
1432     QTest::newRow("urlMode")
1433         << "urlMode"
1434         << ""
1435         << "";
1436     QTest::newRow("urlParent")
1437         << "urlParent"
1438         << ""
1439         << "obj";
1440     QTest::newRow("urlNullParent")
1441         << "urlNullParent"
1442         << ""
1443         << "null";
1444     QTest::newRow("urlModeParent")
1445         << "urlModeParent"
1446         << ""
1447         << "obj";
1448     QTest::newRow("urlModeNullParent")
1449         << "urlModeNullParent"
1450         << ""
1451         << "null";
1452     QTest::newRow("invalidSecondArg")
1453         << "invalidSecondArg"
1454         << ":40: Error: Qt.createComponent(): Invalid arguments"
1455         << "";
1456     QTest::newRow("invalidThirdArg")
1457         << "invalidThirdArg"
1458         << ":45: Error: Qt.createComponent(): Invalid parent object"
1459         << "";
1460     QTest::newRow("invalidMode")
1461         << "invalidMode"
1462         << ":50: Error: Qt.createComponent(): Invalid arguments"
1463         << "";
1464 }
1465
1466 /*
1467 Test using createComponent to dynamically generate a component.
1468 */
1469 void tst_qqmlecmascript::componentCreation()
1470 {
1471     QFETCH(QString, method);
1472     QFETCH(QString, creationError);
1473     QFETCH(QString, createdParent);
1474
1475     QUrl testUrl(testFileUrl("componentCreation.qml"));
1476
1477     if (!creationError.isEmpty()) {
1478         QString warning = testUrl.toString() + creationError;
1479         QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1480     }
1481
1482     QQmlComponent component(&engine, testUrl);
1483     MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create());
1484     QVERIFY(object != 0);
1485
1486     QMetaObject::invokeMethod(object, method.toUtf8());
1487     QQmlComponent *created = object->componentProperty();
1488
1489     if (creationError.isEmpty()) {
1490         QVERIFY(created);
1491
1492         QObject *expectedParent;
1493         if (createdParent == QLatin1String("obj")) {
1494             expectedParent = object;
1495         } else if ((createdParent == QLatin1String("null")) || createdParent.isEmpty()) {
1496             expectedParent = 0;
1497         }
1498         QCOMPARE(created->parent(), expectedParent);
1499     }
1500 }
1501
1502 void tst_qqmlecmascript::dynamicCreation_data()
1503 {
1504     QTest::addColumn<QString>("method");
1505     QTest::addColumn<QString>("createdName");
1506
1507     QTest::newRow("One") << "createOne" << "objectOne";
1508     QTest::newRow("Two") << "createTwo" << "objectTwo";
1509     QTest::newRow("Three") << "createThree" << "objectThree";
1510 }
1511
1512 /*
1513 Test using createQmlObject to dynamically generate an item
1514 Also using createComponent is tested.
1515 */
1516 void tst_qqmlecmascript::dynamicCreation()
1517 {
1518     QFETCH(QString, method);
1519     QFETCH(QString, createdName);
1520
1521     QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
1522     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1523     QVERIFY(object != 0);
1524
1525     QMetaObject::invokeMethod(object, method.toUtf8());
1526     QObject *created = object->objectProperty();
1527     QVERIFY(created);
1528     QCOMPARE(created->objectName(), createdName);
1529
1530     delete object;
1531 }
1532
1533 /*
1534    Tests the destroy function
1535 */
1536 void tst_qqmlecmascript::dynamicDestruction()
1537 {
1538     {
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;
1543
1544     QMetaObject::invokeMethod(object, "create");
1545     createdQmlObject = object->objectProperty();
1546     QVERIFY(createdQmlObject);
1547     QCOMPARE(createdQmlObject->objectName(), QString("emptyObject"));
1548
1549     QMetaObject::invokeMethod(object, "killOther");
1550     QVERIFY(createdQmlObject);
1551
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) {
1557             QTest::qWait(100);
1558             QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1559             QCoreApplication::processEvents();
1560         }
1561     }
1562     QVERIFY(!createdQmlObject);
1563
1564     QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
1565     QMetaObject::invokeMethod(object, "killMe");
1566     QVERIFY(object);
1567     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1568     QCoreApplication::processEvents();
1569     QVERIFY(!object);
1570     }
1571
1572     {
1573     QQmlComponent component(&engine, testFileUrl("dynamicDeletion.2.qml"));
1574     QObject *o = component.create();
1575     QVERIFY(o != 0);
1576
1577     QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1578
1579     QMetaObject::invokeMethod(o, "create");
1580
1581     QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) != 0);
1582
1583     QMetaObject::invokeMethod(o, "destroy");
1584
1585     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1586     QCoreApplication::processEvents();
1587
1588     QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1589
1590     delete o;
1591     }
1592
1593     {
1594     // QTBUG-23451
1595     QQmlGuard<QObject> createdQmlObject = 0;
1596     QQmlComponent component(&engine, testFileUrl("dynamicDeletion.3.qml"));
1597     QObject *o = component.create();
1598     QVERIFY(o != 0);
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
1606         QTest::qWait(100);
1607         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1608         QCoreApplication::processEvents();
1609     }
1610     QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1611     QVERIFY(qvariant_cast<bool>(o->property("test")) == true);
1612     delete o;
1613     }
1614 }
1615
1616 /*
1617    tests that id.toString() works
1618 */
1619 void tst_qqmlecmascript::objectToString()
1620 {
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\")"));
1627
1628     delete object;
1629 }
1630
1631 /*
1632   tests that id.hasOwnProperty() works
1633 */
1634 void tst_qqmlecmascript::objectHasOwnProperty()
1635 {
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";
1640
1641     QQmlComponent component(&engine, url);
1642     QObject *object = component.create();
1643     QVERIFY(object != 0);
1644
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);
1650
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);
1666
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);
1676
1677     delete object;
1678 }
1679
1680 /*
1681 Tests bindings that indirectly cause their own deletion work.
1682
1683 This test is best run under valgrind to ensure no invalid memory access occur.
1684 */
1685 void tst_qqmlecmascript::selfDeletingBinding()
1686 {
1687     {
1688         QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.qml"));
1689         QObject *object = component.create();
1690         QVERIFY(object != 0);
1691         object->setProperty("triggerDelete", true);
1692         delete object;
1693     }
1694
1695     {
1696         QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.2.qml"));
1697         QObject *object = component.create();
1698         QVERIFY(object != 0);
1699         object->setProperty("triggerDelete", true);
1700         delete object;
1701     }
1702 }
1703
1704 /*
1705 Test that extended object properties can be accessed.
1706
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).
1710 */
1711 void tst_qqmlecmascript::extendedObjectPropertyLookup()
1712 {
1713     QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup.qml"));
1714     QObject *object = component.create();
1715     QVERIFY(object != 0);
1716     delete object;
1717 }
1718
1719 /*
1720 Test that extended object properties can be accessed correctly.
1721 */
1722 void tst_qqmlecmascript::extendedObjectPropertyLookup2()
1723 {
1724     QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup2.qml"));
1725     QObject *object = component.create();
1726     QVERIFY(object != 0);
1727
1728     QVariant returnValue;
1729     QVERIFY(QMetaObject::invokeMethod(object, "getValue", Q_RETURN_ARG(QVariant, returnValue)));
1730     QCOMPARE(returnValue.toInt(), 42);
1731
1732     delete object;
1733 }
1734 /*
1735 Test file/lineNumbers for binding/Script errors.
1736 */
1737 void tst_qqmlecmascript::scriptErrors()
1738 {
1739     QQmlComponent component(&engine, testFileUrl("scriptErrors.qml"));
1740     QString url = component.url().toString();
1741
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\"";
1750
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);
1758
1759     QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
1760     emit object->basicSignal();
1761
1762     QTest::ignoreMessage(QtWarningMsg, warning7.toLatin1().constData());
1763     emit object->anotherBasicSignal();
1764
1765     QTest::ignoreMessage(QtWarningMsg, warning8.toLatin1().constData());
1766     emit object->thirdBasicSignal();
1767
1768     delete object;
1769 }
1770
1771 /*
1772 Test file/lineNumbers for inline functions.
1773 */
1774 void tst_qqmlecmascript::functionErrors()
1775 {
1776     QQmlComponent component(&engine, testFileUrl("functionErrors.qml"));
1777     QString url = component.url().toString();
1778
1779     QString warning = url + ":5: Error: Invalid write to global property \"a\"";
1780
1781     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1782
1783     QObject *object = component.create();
1784     QVERIFY(object != 0);
1785     delete object;
1786
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);
1792
1793     QString srpname = object->property("srp_name").toString();
1794     
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");
1799     delete object;
1800 }
1801
1802 /*
1803 Test various errors that can occur when assigning a property from script
1804 */
1805 void tst_qqmlecmascript::propertyAssignmentErrors()
1806 {
1807     QQmlComponent component(&engine, testFileUrl("propertyAssignmentErrors.qml"));
1808
1809     QString url = component.url().toString();
1810
1811     QObject *object = component.create();
1812     QVERIFY(object != 0);
1813
1814     QCOMPARE(object->property("test1").toBool(), true);
1815     QCOMPARE(object->property("test2").toBool(), true);
1816
1817     delete object;
1818 }
1819     
1820 /*
1821 Test bindings still work when the reeval is triggered from within
1822 a signal script.
1823 */
1824 void tst_qqmlecmascript::signalTriggeredBindings()
1825 {
1826     QQmlComponent component(&engine, testFileUrl("signalTriggeredBindings.qml"));
1827     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1828     QVERIFY(object != 0);
1829
1830     QCOMPARE(object->property("base").toReal(), 50.);
1831     QCOMPARE(object->property("test1").toReal(), 50.);
1832     QCOMPARE(object->property("test2").toReal(), 50.);
1833
1834     object->basicSignal();
1835
1836     QCOMPARE(object->property("base").toReal(), 200.);
1837     QCOMPARE(object->property("test1").toReal(), 200.);
1838     QCOMPARE(object->property("test2").toReal(), 200.);
1839
1840     object->argumentSignal(10, QString(), 10, MyQmlObject::EnumValue4, Qt::RightButton);
1841
1842     QCOMPARE(object->property("base").toReal(), 400.);
1843     QCOMPARE(object->property("test1").toReal(), 400.);
1844     QCOMPARE(object->property("test2").toReal(), 400.);
1845
1846     delete object;
1847 }
1848
1849 /*
1850 Test that list properties can be iterated from ECMAScript
1851 */
1852 void tst_qqmlecmascript::listProperties()
1853 {
1854     QQmlComponent component(&engine, testFileUrl("listProperties.qml"));
1855     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1856     QVERIFY(object != 0);
1857
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);
1862
1863     delete object;
1864 }
1865
1866 void tst_qqmlecmascript::exceptionClearsOnReeval()
1867 {
1868     QQmlComponent component(&engine, testFileUrl("exceptionClearsOnReeval.qml"));
1869     QString url = component.url().toString();
1870
1871     QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null";
1872
1873     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1874     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1875     QVERIFY(object != 0);
1876
1877     QCOMPARE(object->property("test").toBool(), false);
1878
1879     MyQmlObject object2;
1880     MyQmlObject object3;
1881     object2.setObjectProperty(&object3);
1882     object->setObjectProperty(&object2);
1883
1884     QCOMPARE(object->property("test").toBool(), true);
1885
1886     delete object;
1887 }
1888
1889 void tst_qqmlecmascript::exceptionSlotProducesWarning()
1890 {
1891     QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning.qml"));
1892     QString url = component.url().toString();
1893
1894     QString warning = component.url().toString() + ":6: Error: JS exception";
1895
1896     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1897     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1898     QVERIFY(object != 0);
1899     delete object;
1900 }
1901
1902 void tst_qqmlecmascript::exceptionBindingProducesWarning()
1903 {
1904     QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning2.qml"));
1905     QString url = component.url().toString();
1906
1907     QString warning = component.url().toString() + ":5: Error: JS exception";
1908
1909     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1910     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1911     QVERIFY(object != 0);
1912     delete object;
1913 }
1914
1915 void tst_qqmlecmascript::compileInvalidBinding()
1916 {
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);
1921     delete object;
1922 }
1923
1924 static int transientErrorsMsgCount = 0;
1925 static void transientErrorsMsgHandler(QtMsgType, const QMessageLogContext &, const QString &)
1926 {
1927     ++transientErrorsMsgCount;
1928 }
1929
1930 // Check that transient binding errors are not displayed
1931 void tst_qqmlecmascript::transientErrors()
1932 {
1933     {
1934     QQmlComponent component(&engine, testFileUrl("transientErrors.qml"));
1935
1936     transientErrorsMsgCount = 0;
1937     QtMessageHandler old = qInstallMessageHandler(transientErrorsMsgHandler);
1938
1939     QObject *object = component.create();
1940     QVERIFY(object != 0);
1941
1942     qInstallMessageHandler(old);
1943
1944     QCOMPARE(transientErrorsMsgCount, 0);
1945
1946     delete object;
1947     }
1948
1949     // One binding erroring multiple times, but then resolving
1950     {
1951     QQmlComponent component(&engine, testFileUrl("transientErrors.2.qml"));
1952
1953     transientErrorsMsgCount = 0;
1954     QtMessageHandler old = qInstallMessageHandler(transientErrorsMsgHandler);
1955
1956     QObject *object = component.create();
1957     QVERIFY(object != 0);
1958
1959     qInstallMessageHandler(old);
1960
1961     QCOMPARE(transientErrorsMsgCount, 0);
1962
1963     delete object;
1964     }
1965 }
1966
1967 // Check that errors during shutdown are minimized
1968 void tst_qqmlecmascript::shutdownErrors()
1969 {
1970     QQmlComponent component(&engine, testFileUrl("shutdownErrors.qml"));
1971     QObject *object = component.create();
1972     QVERIFY(object != 0);
1973
1974     transientErrorsMsgCount = 0;
1975     QtMessageHandler old = qInstallMessageHandler(transientErrorsMsgHandler);
1976
1977     delete object;
1978
1979     qInstallMessageHandler(old);
1980     QCOMPARE(transientErrorsMsgCount, 0);
1981 }
1982
1983 void tst_qqmlecmascript::compositePropertyType()
1984 {
1985     QQmlComponent component(&engine, testFileUrl("compositePropertyType.qml"));
1986
1987     QTest::ignoreMessage(QtDebugMsg, "hello world");
1988     QObject *object = qobject_cast<QObject *>(component.create());
1989     delete object;
1990 }
1991
1992 // QTBUG-5759
1993 void tst_qqmlecmascript::jsObject()
1994 {
1995     QQmlComponent component(&engine, testFileUrl("jsObject.qml"));
1996     QObject *object = component.create();
1997     QVERIFY(object != 0);
1998
1999     QCOMPARE(object->property("test").toInt(), 92);
2000
2001     delete object;
2002 }
2003
2004 void tst_qqmlecmascript::undefinedResetsProperty()
2005 {
2006     {
2007     QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.qml"));
2008     QObject *object = component.create();
2009     QVERIFY(object != 0);
2010
2011     QCOMPARE(object->property("resettableProperty").toInt(), 92);
2012
2013     object->setProperty("setUndefined", true);
2014
2015     QCOMPARE(object->property("resettableProperty").toInt(), 13);
2016
2017     object->setProperty("setUndefined", false);
2018
2019     QCOMPARE(object->property("resettableProperty").toInt(), 92);
2020
2021     delete object;
2022     }
2023     {
2024     QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.2.qml"));
2025     QObject *object = component.create();
2026     QVERIFY(object != 0);
2027
2028     QCOMPARE(object->property("resettableProperty").toInt(), 19);
2029
2030     QMetaObject::invokeMethod(object, "doReset");
2031
2032     QCOMPARE(object->property("resettableProperty").toInt(), 13);
2033
2034     delete object;
2035     }
2036 }
2037
2038 // Aliases to variant properties should work
2039 void tst_qqmlecmascript::qtbug_22464()
2040 {
2041     QQmlComponent component(&engine, testFileUrl("qtbug_22464.qml"));
2042     QObject *object = component.create();
2043     QVERIFY(object != 0);
2044
2045     QCOMPARE(object->property("test").toBool(), true);
2046
2047     delete object;
2048 }
2049
2050 void tst_qqmlecmascript::qtbug_21580()
2051 {
2052     QQmlComponent component(&engine, testFileUrl("qtbug_21580.qml"));
2053
2054     QObject *object = component.create();
2055     QVERIFY(object != 0);
2056
2057     QCOMPARE(object->property("test").toBool(), true);
2058
2059     delete object;
2060 }
2061
2062 // Causes a v8 binding, but not all v8 bindings to be destroyed during evaluation
2063 void tst_qqmlecmascript::singleV8BindingDestroyedDuringEvaluation()
2064 {
2065     QQmlComponent component(&engine, testFileUrl("singleV8BindingDestroyedDuringEvaluation.qml"));
2066
2067     QObject *object = component.create();
2068     QVERIFY(object != 0);
2069     delete object;
2070 }
2071
2072 // QTBUG-6781
2073 void tst_qqmlecmascript::bug1()
2074 {
2075     QQmlComponent component(&engine, testFileUrl("bug.1.qml"));
2076     QObject *object = component.create();
2077     QVERIFY(object != 0);
2078
2079     QCOMPARE(object->property("test").toInt(), 14);
2080
2081     object->setProperty("a", 11);
2082
2083     QCOMPARE(object->property("test").toInt(), 3);
2084
2085     object->setProperty("b", true);
2086
2087     QCOMPARE(object->property("test").toInt(), 9);
2088
2089     delete object;
2090 }
2091
2092 #ifndef QT_NO_WIDGETS
2093 void tst_qqmlecmascript::bug2()
2094 {
2095     QQmlComponent component(&engine);
2096     component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl());
2097
2098     QObject *object = component.create();
2099     QVERIFY(object != 0);
2100
2101     delete object;
2102 }
2103 #endif
2104
2105 // Don't crash in createObject when the component has errors.
2106 void tst_qqmlecmascript::dynamicCreationCrash()
2107 {
2108     QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
2109     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2110     QVERIFY(object != 0);
2111
2112     QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2113     QMetaObject::invokeMethod(object, "dontCrash");
2114     QObject *created = object->objectProperty();
2115     QVERIFY(created == 0);
2116
2117     delete object;
2118 }
2119
2120 // ownership transferred to JS, ensure that GC runs the dtor
2121 void tst_qqmlecmascript::dynamicCreationOwnership()
2122 {
2123     int dtorCount = 0;
2124     int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
2125
2126     // allow the engine to go out of scope too.
2127     {
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);
2135
2136         for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
2137             QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
2138             if (i % 90 == 0) {
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");
2142             }
2143             if (i % 10 == 0) {
2144                 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2145                 QCoreApplication::processEvents();
2146             }
2147         }
2148
2149         delete object;
2150     }
2151     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2152     QCoreApplication::processEvents();
2153     QCOMPARE(dtorCount, expectedDtorCount);
2154 }
2155
2156 void tst_qqmlecmascript::regExpBug()
2157 {
2158     //QTBUG-9367
2159     {
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]"));
2164         delete object;
2165     }
2166
2167     //QTBUG-23068
2168     {
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());
2173         QVERIFY(!object);
2174         QCOMPARE(component.errorString(), err);
2175     }
2176 }
2177
2178 static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source)
2179 {
2180     QString functionSource = QLatin1String("(function(object) { return ") + 
2181                              QLatin1String(source) + QLatin1String(" })");
2182     v8::TryCatch tc;
2183     v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2184     if (tc.HasCaught())
2185         return false;
2186     v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2187     if (function.IsEmpty())
2188         return false;
2189     v8::Handle<v8::Value> args[] = { o };
2190     function->Call(engine->global(), 1, args);
2191     return tc.HasCaught();
2192 }
2193
2194 static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o, 
2195                                   const char *source, v8::Handle<v8::Value> result)
2196 {
2197     QString functionSource = QLatin1String("(function(object) { return ") + 
2198                              QLatin1String(source) + QLatin1String(" })");
2199     v8::TryCatch tc;
2200     v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2201     if (tc.HasCaught())
2202         return false;
2203     v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2204     if (function.IsEmpty())
2205         return false;
2206     v8::Handle<v8::Value> args[] = { o };
2207
2208     v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2209
2210     if (tc.HasCaught())
2211         return false;
2212
2213     return value->StrictEquals(result);
2214 }
2215
2216 static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o, 
2217                                              const char *source)
2218 {
2219     QString functionSource = QLatin1String("(function(object) { return ") + 
2220                              QLatin1String(source) + QLatin1String(" })");
2221     v8::TryCatch tc;
2222     v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2223     if (tc.HasCaught())
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 };
2229
2230     v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2231
2232     if (tc.HasCaught())
2233         return v8::Handle<v8::Value>();
2234     return value;
2235 }
2236
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)
2240
2241 void tst_qqmlecmascript::callQtInvokables()
2242 {
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();
2246
2247     QQmlEngine qmlengine;
2248     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine);
2249     
2250     QV8Engine *engine = ep->v8engine();
2251
2252     v8::HandleScope handle_scope;
2253     v8::Context::Scope scope(engine->context());
2254
2255     v8::Local<v8::Object> object = engine->newQObject(o)->ToObject();
2256
2257     // Non-existent methods
2258     o->reset();
2259     QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
2260     QCOMPARE(o->error(), false);
2261     QCOMPARE(o->invoked(), -1);
2262     QCOMPARE(o->actuals().count(), 0);
2263
2264     o->reset();
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);
2269
2270     // Insufficient arguments
2271     o->reset();
2272     QVERIFY(EVALUATE_ERROR("object.method_int()"));
2273     QCOMPARE(o->error(), false);
2274     QCOMPARE(o->invoked(), -1);
2275     QCOMPARE(o->actuals().count(), 0);
2276
2277     o->reset();
2278     QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
2279     QCOMPARE(o->error(), false);
2280     QCOMPARE(o->invoked(), -1);
2281     QCOMPARE(o->actuals().count(), 0);
2282
2283     // Excessive arguments
2284     o->reset();
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));
2290
2291     o->reset();
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));
2298
2299     // Test return types
2300     o->reset();
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);
2305
2306     o->reset();
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);
2311
2312     o->reset();
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);
2317
2318     o->reset();
2319     {
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);
2326     }
2327
2328     o->reset();
2329     {
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);
2335     }
2336
2337     o->reset();
2338     QVERIFY(EVALUATE_ERROR("object.method_NoArgs_unknown()"));
2339     QCOMPARE(o->error(), false);
2340     QCOMPARE(o->invoked(), -1);
2341     QCOMPARE(o->actuals().count(), 0);
2342
2343     o->reset();
2344     {
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);
2351     }
2352
2353     o->reset();
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);
2358
2359     // Test arg types
2360     o->reset();
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));
2366
2367     o->reset();
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));
2373
2374     o->reset();
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));
2380
2381     o->reset();
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));
2387
2388     o->reset();
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));
2394
2395     o->reset();
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));
2401
2402     o->reset();
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));
2409
2410     o->reset();
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));
2416
2417     o->reset();
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));
2423
2424     o->reset();
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()));
2430
2431     o->reset();
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));
2437
2438     o->reset();
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()));
2444
2445     o->reset();
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()));
2451
2452     o->reset();
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"));
2458
2459     o->reset();
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"));
2465
2466     o->reset();
2467     {
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));
2474     }
2475
2476     o->reset();
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()));
2482
2483     o->reset();
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()));
2489
2490     o->reset();
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()));
2496
2497     o->reset();
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()));
2503
2504     o->reset();
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()));
2510
2511     o->reset();
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()));
2517
2518     o->reset();
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)));
2524
2525     o->reset();
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)));
2531
2532     o->reset();
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));
2538
2539     o->reset();
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));
2545
2546     o->reset();
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));
2552
2553     o->reset();
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));
2559
2560     o->reset();
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));
2566
2567     o->reset();
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());
2573
2574     o->reset();
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());
2580
2581     o->reset();
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)));
2587
2588     o->reset();
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());
2594
2595     o->reset();
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());
2602
2603     o->reset();
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());
2610
2611     o->reset();
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)));
2618
2619     o->reset();
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());
2626
2627     o->reset();
2628     QVERIFY(EVALUATE_ERROR("object.method_overload()"));
2629     QCOMPARE(o->error(), false);
2630     QCOMPARE(o->invoked(), -1);
2631     QCOMPARE(o->actuals().count(), 0);
2632
2633     o->reset();
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));
2639
2640     o->reset();
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));
2647
2648     o->reset();
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")));
2654
2655     o->reset();
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));
2661
2662     o->reset();
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));
2669
2670     o->reset();
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));
2677
2678     o->reset();
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));
2684
2685     o->reset();
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());
2692
2693     o->reset();
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")));
2700
2701     o->reset();
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());
2707
2708     o->reset();
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());
2714
2715     o->reset();
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));
2721
2722     o->reset();
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));
2728
2729     o->reset();
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")));
2735
2736     o->reset();
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));
2742
2743     o->reset();
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));
2749
2750     o->reset();
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));
2756
2757     o->reset();
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));
2763
2764     o->reset();
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());
2770
2771     o->reset();
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());
2777
2778     o->reset();
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));
2784
2785     o->reset();
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));
2791
2792     o->reset();
2793     QVERIFY(EVALUATE_ERROR("object.method_unknown(null)"));
2794     QCOMPARE(o->error(), false);
2795     QCOMPARE(o->invoked(), -1);
2796     QCOMPARE(o->actuals().count(), 0);
2797 }
2798
2799 // QTBUG-13047 (check that you can pass registered object types as args)
2800 void tst_qqmlecmascript::invokableObjectArg()
2801 {
2802     QQmlComponent component(&engine, testFileUrl("invokableObjectArg.qml"));
2803
2804     QObject *o = component.create();
2805     QVERIFY(o);
2806     MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o);
2807     QVERIFY(qmlobject);
2808     QCOMPARE(qmlobject->myinvokableObject, qmlobject);
2809
2810     delete o;
2811 }
2812
2813 // QTBUG-13047 (check that you can return registered object types from methods)
2814 void tst_qqmlecmascript::invokableObjectRet()
2815 {
2816     QQmlComponent component(&engine, testFileUrl("invokableObjectRet.qml"));
2817
2818     QObject *o = component.create();
2819     QVERIFY(o);
2820     QCOMPARE(o->property("test").toBool(), true);
2821     delete o;
2822 }
2823
2824 void tst_qqmlecmascript::invokableEnumRet()
2825 {
2826     QQmlComponent component(&engine, testFileUrl("invokableEnumRet.qml"));
2827
2828     QObject *o = component.create();
2829     QVERIFY(o);
2830     QCOMPARE(o->property("test").toBool(), true);
2831     delete o;
2832 }
2833
2834 // QTBUG-5675
2835 void tst_qqmlecmascript::listToVariant()
2836 {
2837     QQmlComponent component(&engine, testFileUrl("listToVariant.qml"));
2838
2839     MyQmlContainer container;
2840
2841     QQmlContext context(engine.rootContext());
2842     context.setContextObject(&container);
2843
2844     QObject *object = component.create(&context);
2845     QVERIFY(object != 0);
2846
2847     QVariant v = object->property("test");
2848     QCOMPARE(v.userType(), qMetaTypeId<QQmlListReference>());
2849     QVERIFY(qvariant_cast<QQmlListReference>(v).object() == &container);
2850
2851     delete object;
2852 }
2853
2854 // QTBUG-16316
2855 Q_DECLARE_METATYPE(QQmlListProperty<MyQmlObject>)
2856 void tst_qqmlecmascript::listAssignment()
2857 {
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));
2866     delete obj;
2867 }
2868
2869 // QTBUG-7957
2870 void tst_qqmlecmascript::multiEngineObject()
2871 {
2872     MyQmlObject obj;
2873     obj.setStringProperty("Howdy planet");
2874
2875     QQmlEngine e1;
2876     e1.rootContext()->setContextProperty("thing", &obj);
2877     QQmlComponent c1(&e1, testFileUrl("multiEngineObject.qml"));
2878
2879     QQmlEngine e2;
2880     e2.rootContext()->setContextProperty("thing", &obj);
2881     QQmlComponent c2(&e2, testFileUrl("multiEngineObject.qml"));
2882
2883     QObject *o1 = c1.create();
2884     QObject *o2 = c2.create();
2885
2886     QCOMPARE(o1->property("test").toString(), QString("Howdy planet"));
2887     QCOMPARE(o2->property("test").toString(), QString("Howdy planet"));
2888
2889     delete o2;
2890     delete o1;
2891 }
2892
2893 // Test that references to QObjects are cleanup when the object is destroyed
2894 void tst_qqmlecmascript::deletedObject()
2895 {
2896     QQmlComponent component(&engine, testFileUrl("deletedObject.qml"));
2897
2898     QObject *object = component.create();
2899
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);
2904
2905     delete object;
2906 }
2907
2908 void tst_qqmlecmascript::attachedPropertyScope()
2909 {
2910     QQmlComponent component(&engine, testFileUrl("attachedPropertyScope.qml"));
2911
2912     QObject *object = component.create();
2913     QVERIFY(object != 0);
2914
2915     MyQmlAttachedObject *attached = 
2916         qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
2917     QVERIFY(attached != 0);
2918
2919     QCOMPARE(object->property("value2").toInt(), 0);
2920
2921     attached->emitMySignal();
2922
2923     QCOMPARE(object->property("value2").toInt(), 9);
2924
2925     delete object;
2926 }
2927
2928 void tst_qqmlecmascript::scriptConnect()
2929 {
2930     {
2931         QQmlComponent component(&engine, testFileUrl("scriptConnect.1.qml"));
2932
2933         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2934         QVERIFY(object != 0);
2935
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);
2939
2940         delete object;
2941     }
2942
2943     {
2944         QQmlComponent component(&engine, testFileUrl("scriptConnect.2.qml"));
2945
2946         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2947         QVERIFY(object != 0);
2948
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);
2952
2953         delete object;
2954     }
2955
2956     {
2957         QQmlComponent component(&engine, testFileUrl("scriptConnect.3.qml"));
2958
2959         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2960         QVERIFY(object != 0);
2961
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);
2965
2966         delete object;
2967     }
2968
2969     {
2970         QQmlComponent component(&engine, testFileUrl("scriptConnect.4.qml"));
2971
2972         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2973         QVERIFY(object != 0);
2974
2975         QCOMPARE(object->methodCalled(), false);
2976         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2977         QCOMPARE(object->methodCalled(), true);
2978
2979         delete object;
2980     }
2981
2982     {
2983         QQmlComponent component(&engine, testFileUrl("scriptConnect.5.qml"));
2984
2985         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2986         QVERIFY(object != 0);
2987
2988         QCOMPARE(object->methodCalled(), false);
2989         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2990         QCOMPARE(object->methodCalled(), true);
2991
2992         delete object;
2993     }
2994
2995     {
2996         QQmlComponent component(&engine, testFileUrl("scriptConnect.6.qml"));
2997
2998         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2999         QVERIFY(object != 0);
3000
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);
3004
3005         delete object;
3006     }
3007 }
3008
3009 void tst_qqmlecmascript::scriptDisconnect()
3010 {
3011     {
3012         QQmlComponent component(&engine, testFileUrl("scriptDisconnect.1.qml"));
3013
3014         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3015         QVERIFY(object != 0);
3016
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);
3026
3027         delete object;
3028     }
3029
3030     {
3031         QQmlComponent component(&engine, testFileUrl("scriptDisconnect.2.qml"));
3032
3033         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3034         QVERIFY(object != 0);
3035
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);
3045
3046         delete object;
3047     }
3048
3049     {
3050         QQmlComponent component(&engine, testFileUrl("scriptDisconnect.3.qml"));
3051
3052         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3053         QVERIFY(object != 0);
3054
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);
3064
3065         delete object;
3066     }
3067     {
3068         QQmlComponent component(&engine, testFileUrl("scriptDisconnect.4.qml"));
3069
3070         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3071         QVERIFY(object != 0);
3072
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);
3082
3083         delete object;
3084     }
3085 }
3086
3087 class OwnershipObject : public QObject
3088 {
3089     Q_OBJECT
3090 public:
3091     OwnershipObject() { object = new QObject; }
3092
3093     QPointer<QObject> object;
3094
3095 public slots:
3096     QObject *getObject() { return object; }
3097 };
3098
3099 void tst_qqmlecmascript::ownership()
3100 {
3101     OwnershipObject own;
3102     QQmlContext *context = new QQmlContext(engine.rootContext());
3103     context->setContextObject(&own);
3104
3105     {
3106         QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3107
3108         QVERIFY(own.object != 0);
3109
3110         QObject *object = component.create(context);
3111
3112         engine.collectGarbage();
3113
3114         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3115         QCoreApplication::processEvents();
3116
3117         QVERIFY(own.object == 0);
3118
3119         delete object;
3120     }
3121
3122     own.object = new QObject(&own);
3123
3124     {
3125         QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3126
3127         QVERIFY(own.object != 0);
3128
3129         QObject *object = component.create(context);
3130         
3131         engine.collectGarbage();
3132
3133         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3134         QCoreApplication::processEvents();
3135
3136         QVERIFY(own.object != 0);
3137
3138         delete object;
3139     }
3140
3141     delete context;
3142 }
3143
3144 class CppOwnershipReturnValue : public QObject
3145 {
3146     Q_OBJECT
3147 public:
3148     CppOwnershipReturnValue() : value(0) {}
3149     ~CppOwnershipReturnValue() { delete value; }
3150
3151     Q_INVOKABLE QObject *create() {
3152         value = new QObject;
3153         QQmlEngine::setObjectOwnership(value, QQmlEngine::CppOwnership);
3154         return value;
3155     }
3156
3157     Q_INVOKABLE MyQmlObject *createQmlObject() {
3158         MyQmlObject *rv = new MyQmlObject;
3159         value = rv;
3160         return rv;
3161     }
3162
3163     QPointer<QObject> value;
3164 };
3165
3166 // QTBUG-15695.  
3167 // Test setObjectOwnership(CppOwnership) works even when there is no QQmlData
3168 void tst_qqmlecmascript::cppOwnershipReturnValue()
3169 {
3170     CppOwnershipReturnValue source;
3171
3172     {
3173     QQmlEngine engine;
3174     engine.rootContext()->setContextProperty("source", &source);
3175
3176     QVERIFY(source.value == 0);
3177
3178     QQmlComponent component(&engine);
3179     component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl());
3180
3181     QObject *object = component.create();
3182
3183     QVERIFY(object != 0);
3184     QVERIFY(source.value != 0);
3185
3186     delete object;
3187     }
3188
3189     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3190     QCoreApplication::processEvents();
3191
3192     QVERIFY(source.value != 0);
3193 }
3194
3195 // QTBUG-15697
3196 void tst_qqmlecmascript::ownershipCustomReturnValue()
3197 {
3198     CppOwnershipReturnValue source;
3199
3200     {
3201     QQmlEngine engine;
3202     engine.rootContext()->setContextProperty("source", &source);
3203
3204     QVERIFY(source.value == 0);
3205
3206     QQmlComponent component(&engine);
3207     component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl());
3208
3209     QObject *object = component.create();
3210
3211     QVERIFY(object != 0);
3212     QVERIFY(source.value != 0);
3213
3214     delete object;
3215     }
3216
3217     engine.collectGarbage();
3218     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3219     QCoreApplication::processEvents();
3220
3221     QVERIFY(source.value == 0);
3222 }
3223
3224 //the return value from getObject will be JS ownership,
3225 //unless strong Cpp ownership has been set
3226 class OwnershipChangingObject : public QObject
3227 {
3228     Q_OBJECT
3229 public:
3230     OwnershipChangingObject(): object(0) { }
3231
3232     QPointer<QObject> object;
3233
3234 public slots:
3235     QObject *getObject() { return object; }
3236     void setObject(QObject *obj) { object = obj; }
3237 };
3238
3239 void tst_qqmlecmascript::ownershipRootObject()
3240 {
3241     OwnershipChangingObject own;
3242     QQmlContext *context = new QQmlContext(engine.rootContext());
3243     context->setContextObject(&own);
3244
3245     QQmlComponent component(&engine, testFileUrl("ownershipRootObject.qml"));
3246     QQmlGuard<QObject> object = component.create(context);
3247     QVERIFY(object);
3248
3249     engine.collectGarbage();
3250
3251     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3252     QCoreApplication::processEvents();
3253
3254     QVERIFY(own.object != 0);
3255
3256     delete context;
3257     delete object;
3258 }
3259
3260 void tst_qqmlecmascript::ownershipConsistency()
3261 {
3262     OwnershipChangingObject own;
3263     QQmlContext *context = new QQmlContext(engine.rootContext());
3264     context->setContextObject(&own);
3265
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.
3274
3275     QQmlComponent component(&engine, testFileUrl("ownershipConsistency.qml"));
3276     QQmlGuard<QObject> object = component.create(context);
3277     QVERIFY(object);
3278
3279     engine.collectGarbage();
3280
3281     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3282     QCoreApplication::processEvents();
3283
3284     QVERIFY(own.object != 0);
3285
3286     delete context;
3287     delete object;
3288 }
3289
3290 void tst_qqmlecmascript::ownershipQmlIncubated()
3291 {
3292     QQmlComponent component(&engine, testFileUrl("ownershipQmlIncubated.qml"));
3293     QObject *object = component.create();
3294     QVERIFY(object);
3295
3296     QTRY_VERIFY(object->property("incubatedItem").value<QObject*>() != 0);
3297
3298     QMetaObject::invokeMethod(object, "deleteIncubatedItem");
3299
3300     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3301     QCoreApplication::processEvents();
3302
3303     QVERIFY(object->property("incubatedItem").value<QObject*>() == 0);
3304
3305     delete object;
3306 }
3307
3308 class QListQObjectMethodsObject : public QObject
3309 {
3310     Q_OBJECT
3311 public:
3312     QListQObjectMethodsObject() {
3313         m_objects.append(new MyQmlObject());
3314         m_objects.append(new MyQmlObject());
3315     }
3316
3317     ~QListQObjectMethodsObject() {
3318         qDeleteAll(m_objects);
3319     }
3320
3321 public slots:
3322     QList<QObject *> getObjects() { return m_objects; }
3323
3324 private:
3325     QList<QObject *> m_objects;
3326 };
3327
3328 // Tests that returning a QList<QObject*> from a method works
3329 void tst_qqmlecmascript::qlistqobjectMethods()
3330 {
3331     QListQObjectMethodsObject obj;
3332     QQmlContext *context = new QQmlContext(engine.rootContext());
3333     context->setContextObject(&obj);
3334
3335     QQmlComponent component(&engine, testFileUrl("qlistqobjectMethods.qml"));
3336
3337     QObject *object = component.create(context);
3338
3339     QCOMPARE(object->property("test").toInt(), 2);
3340     QCOMPARE(object->property("test2").toBool(), true);
3341
3342     delete object;
3343     delete context;
3344 }
3345
3346 // QTBUG-9205
3347 void tst_qqmlecmascript::strictlyEquals()
3348 {
3349     QQmlComponent component(&engine, testFileUrl("strictlyEquals.qml"));
3350
3351     QObject *object = component.create();
3352     QVERIFY(object != 0);
3353
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);
3362
3363     delete object;
3364 }
3365
3366 void tst_qqmlecmascript::compiled()
3367 {
3368     QQmlComponent component(&engine, testFileUrl("compiled.qml"));
3369
3370     QObject *object = component.create();
3371     QVERIFY(object != 0);
3372
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);
3379
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);
3386
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);
3391
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));
3401
3402     delete object;
3403 }
3404
3405 // Test that numbers assigned in bindings as strings work consistently
3406 void tst_qqmlecmascript::numberAssignment()
3407 {
3408     QQmlComponent component(&engine, testFileUrl("numberAssignment.qml"));
3409
3410     QObject *object = component.create();
3411     QVERIFY(object != 0);
3412
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));
3418
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));
3423
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));
3428
3429     delete object;
3430 }
3431
3432 void tst_qqmlecmascript::propertySplicing()
3433 {
3434     QQmlComponent component(&engine, testFileUrl("propertySplicing.qml"));
3435
3436     QObject *object = component.create();
3437     QVERIFY(object != 0);
3438
3439     QCOMPARE(object->property("test").toBool(), true);
3440
3441     delete object;
3442 }
3443
3444 // QTBUG-16683
3445 void tst_qqmlecmascript::signalWithUnknownTypes()
3446 {
3447     QQmlComponent component(&engine, testFileUrl("signalWithUnknownTypes.qml"));
3448
3449     MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3450     QVERIFY(object != 0);
3451
3452     MyQmlObject::MyType type;
3453     type.value = 0x8971123;
3454     emit object->signalWithUnknownType(type);
3455
3456     MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant());
3457
3458     QCOMPARE(result.value, type.value);
3459
3460     MyQmlObject::MyOtherType othertype;
3461     othertype.value = 17;
3462     emit object->signalWithCompletelyUnknownType(othertype);
3463
3464     QVERIFY(!object->variant().isValid());
3465
3466     delete object;
3467 }
3468
3469 void tst_qqmlecmascript::signalWithJSValueInVariant_data()
3470 {
3471     QTest::addColumn<QString>("expression");
3472     QTest::addColumn<QString>("compare");
3473
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;
3480
3481     QString comparePropertiesStrict(
3482         "(function(a, b) {"
3483         "  if (typeof b != 'object')"
3484         "    return a === b;"
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]);"
3489         "  }"
3490         "})");
3491     QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })"  << comparePropertiesStrict;
3492     QTest::newRow("[10,20,30]") << "[10,20,30]"  << comparePropertiesStrict;
3493 }
3494
3495 void tst_qqmlecmascript::signalWithJSValueInVariant()
3496 {
3497     QFETCH(QString, expression);
3498     QFETCH(QString, compare);
3499
3500     QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3501     QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3502     QVERIFY(object != 0);
3503
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);
3509
3510     emit object->signalWithVariant(QVariant::fromValue(value));
3511     QVERIFY(object->property("pass").toBool());
3512 }
3513
3514 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines_data()
3515 {
3516     signalWithJSValueInVariant_data();
3517 }
3518
3519 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines()
3520 {
3521     QFETCH(QString, expression);
3522     QFETCH(QString, compare);
3523
3524     QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3525     QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3526     QVERIFY(object != 0);
3527
3528     QJSEngine engine2;
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);
3534
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());
3538 }
3539
3540 void tst_qqmlecmascript::signalWithQJSValue_data()
3541 {
3542     signalWithJSValueInVariant_data();
3543 }
3544
3545 void tst_qqmlecmascript::signalWithQJSValue()
3546 {
3547     QFETCH(QString, expression);
3548     QFETCH(QString, compare);
3549
3550     QQmlComponent component(&engine, testFileUrl("signalWithQJSValue.qml"));
3551     QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3552     QVERIFY(object != 0);
3553
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);
3559
3560     emit object->signalWithQJSValue(value);
3561
3562     QVERIFY(object->property("pass").toBool());
3563     QVERIFY(object->qjsvalue().strictlyEquals(value));
3564 }
3565
3566 void tst_qqmlecmascript::singletonType_data()
3567 {
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");
3577
3578     QTest::newRow("qobject, register + read + method [no qualifier]")
3579             << testFileUrl("singletontype/qobjectSingletonTypeNoQualifier.qml")
3580             << QString()
3581             << QStringList()
3582             << (QStringList() << "qobjectPropertyTest" << "qobjectMethodTest")
3583             << (QVariantList() << 20 << 1)
3584             << QStringList()
3585             << QVariantList()
3586             << QStringList()
3587             << QVariantList();
3588
3589     QTest::newRow("script, register + read [no qualifier]")
3590             << testFileUrl("singletontype/scriptSingletonTypeNoQualifier.qml")
3591             << QString()
3592             << QStringList()
3593             << (QStringList() << "scriptTest")
3594             << (QVariantList() << 13)
3595             << QStringList()
3596             << QVariantList()
3597             << QStringList()
3598             << QVariantList();
3599
3600     QTest::newRow("qobject, register + read + method")
3601             << testFileUrl("singletontype/qobjectSingletonType.qml")
3602             << QString()
3603             << QStringList()
3604             << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
3605                    << "qobjectMinorVersionMethodTest" << "qobjectMinorVersionTest"
3606                    << "qobjectMajorVersionTest" << "qobjectParentedTest")
3607             << (QVariantList() << 20 << 20 << 2 << 1 << 20 << 20 << 26)
3608             << QStringList()
3609             << QVariantList()
3610             << QStringList()
3611             << QVariantList();
3612
3613     QTest::newRow("script, register + read")
3614             << testFileUrl("singletontype/scriptSingletonType.qml")
3615             << QString()
3616             << QStringList()
3617             << (QStringList() << "scriptTest")
3618             << (QVariantList() << 14) // will have incremented, since we create a new engine each row in this test.
3619             << QStringList()
3620             << QVariantList()
3621             << QStringList()
3622             << QVariantList();
3623
3624     QTest::newRow("qobject, writing + readonly constraints")
3625             << testFileUrl("singletontype/qobjectSingletonTypeWriting.qml")
3626             << QString()
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);
3634
3635     QTest::newRow("script, writing + readonly constraints")
3636             << testFileUrl("singletontype/scriptSingletonTypeWriting.qml")
3637             << QString()
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);
3645
3646     QTest::newRow("qobject singleton Type enum values in JS")
3647             << testFileUrl("singletontype/qobjectSingletonTypeEnums.qml")
3648             << QString()
3649             << QStringList()
3650             << (QStringList() << "enumValue" << "enumMethod")
3651             << (QVariantList() << 42 << 30)
3652             << QStringList()
3653             << QVariantList()
3654             << QStringList()
3655             << QVariantList();
3656
3657     QTest::newRow("qobject, invalid major version fail")
3658             << testFileUrl("singletontype/singletonTypeMajorVersionFail.qml")
3659             << QString("QQmlComponent: Component is not ready")
3660             << QStringList()
3661             << QStringList()
3662             << QVariantList()
3663             << QStringList()
3664             << QVariantList()
3665             << QStringList()
3666             << QVariantList();
3667
3668     QTest::newRow("qobject, invalid minor version fail")
3669             << testFileUrl("singletontype/singletonTypeMinorVersionFail.qml")
3670             << QString("QQmlComponent: Component is not ready")
3671             << QStringList()
3672             << QStringList()
3673             << QVariantList()
3674             << QStringList()
3675             << QVariantList()
3676             << QStringList()
3677             << QVariantList();
3678
3679     QTest::newRow("qobject, multiple in namespace")
3680             << testFileUrl("singletontype/singletonTypeMultiple.qml")
3681             << QString()
3682             << QStringList()
3683             << (QStringList() << "first" << "second")
3684             << (QVariantList() << 35 << 42)
3685             << QStringList()
3686             << QVariantList()
3687             << QStringList()
3688             << QVariantList();
3689 }
3690
3691 void tst_qqmlecmascript::singletonType()
3692 {
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);
3702
3703     QQmlEngine cleanEngine; // so tests don't interfere which each other, as singleton types are engine-singletons only.
3704     QQmlComponent component(&cleanEngine, testfile);
3705
3706     if (!errorMessage.isEmpty())
3707         QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3708
3709     if (warningMessages.size())
3710         foreach (const QString &warning, warningMessages)
3711             QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3712
3713     QObject *object = component.create();
3714     if (!errorMessage.isEmpty()) {
3715         QVERIFY(object == 0);
3716     } else {
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));
3724         delete object;
3725     }
3726 }
3727
3728 void tst_qqmlecmascript::singletonTypeCaching_data()
3729 {
3730     QTest::addColumn<QUrl>("testfile");
3731     QTest::addColumn<QStringList>("readProperties");
3732
3733     QTest::newRow("qobject, caching + read")
3734             << testFileUrl("singletontype/qobjectSingletonTypeCaching.qml")
3735             << (QStringList() << "existingUriTest" << "qobjectParentedTest");
3736
3737     QTest::newRow("script, caching + read")
3738             << testFileUrl("singletontype/scriptSingletonTypeCaching.qml")
3739             << (QStringList() << "scriptTest");
3740 }
3741
3742 void tst_qqmlecmascript::singletonTypeCaching()
3743 {
3744     QFETCH(QUrl, testfile);
3745     QFETCH(QStringList, readProperties);
3746
3747     // ensure that the singleton type instances are cached per-engine.
3748
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());
3757     delete object;
3758
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.
3764     delete object2;
3765 }
3766
3767 void tst_qqmlecmascript::singletonTypeImportOrder()
3768 {
3769     QQmlComponent component(&engine, testFileUrl("singletontype/singletonTypeImportOrder.qml"));
3770     QObject *object = component.create();
3771     QVERIFY(object);
3772     QVERIFY(object->property("v") == 1);
3773     delete object;
3774 }
3775
3776 void tst_qqmlecmascript::singletonTypeResolution()
3777 {
3778     QQmlComponent component(&engine, testFileUrl("singletontype/singletonTypeResolution.qml"));
3779     QObject *object = component.create();
3780     QVERIFY(object);
3781     QVERIFY(object->property("success") == true);
3782     delete object;
3783 }
3784
3785 void tst_qqmlecmascript::importScripts_data()
3786 {
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");
3792
3793     QTest::newRow("basic functionality")
3794             << testFileUrl("jsimport/testImport.qml")
3795             << QString()
3796             << QStringList()
3797             << (QStringList() << QLatin1String("importedScriptStringValue")
3798                               << QLatin1String("importedScriptFunctionValue")
3799                               << QLatin1String("importedModuleAttachedPropertyValue")
3800                               << QLatin1String("importedModuleEnumValue"))
3801             << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3802                                << QVariant(20)
3803                                << QVariant(19)
3804                                << QVariant(2));
3805
3806     QTest::newRow("import scoping")
3807             << testFileUrl("jsimport/testImportScoping.qml")
3808             << QString()
3809             << QStringList()
3810             << (QStringList() << QLatin1String("componentError"))
3811             << (QVariantList() << QVariant(5));
3812
3813     QTest::newRow("parent scope shouldn't be inherited by import with imports")
3814             << testFileUrl("jsimportfail/failOne.qml")
3815             << QString()
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()));
3819
3820     QTest::newRow("javascript imports in an import should be private to the import scope")
3821             << testFileUrl("jsimportfail/failTwo.qml")
3822             << QString()
3823             << (QStringList() << QString(testFileUrl("jsimportfail/failTwo.qml").toString() + QLatin1String(":6: ReferenceError: ImportOneJs is not defined")))
3824             << (QStringList() << QLatin1String("importScriptFunctionValue"))
3825             << (QVariantList() << QVariant(QString()));
3826
3827     QTest::newRow("module imports in an import should be private to the import scope")
3828             << testFileUrl("jsimportfail/failThree.qml")
3829             << QString()
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));
3833
3834     QTest::newRow("typenames in an import should be private to the import scope")
3835             << testFileUrl("jsimportfail/failFour.qml")
3836             << QString()
3837             << (QStringList() << QString(testFileUrl("jsimportfail/failFour.qml").toString() + QLatin1String(":6: ReferenceError: JsQtTest is not defined")))
3838             << (QStringList() << QLatin1String("importedModuleEnumValue"))
3839             << (QVariantList() << QVariant(0));
3840
3841     QTest::newRow("import with imports has it's own activation scope")
3842             << testFileUrl("jsimportfail/failFive.qml")
3843             << QString()
3844             << (QStringList() << QString(testFileUrl("jsimportfail/importWithImports.js").toString() + QLatin1String(":8: ReferenceError: Component is not defined")))
3845             << (QStringList() << QLatin1String("componentError"))
3846             << (QVariantList() << QVariant(0));
3847
3848     QTest::newRow("import pragma library script")
3849             << testFileUrl("jsimport/testImportPragmaLibrary.qml")
3850             << QString()
3851             << QStringList()
3852             << (QStringList() << QLatin1String("testValue"))
3853             << (QVariantList() << QVariant(31));
3854
3855     QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3856             << testFileUrl("jsimportfail/testImportPragmaLibrary.qml")
3857             << QString()
3858             << (QStringList() << QString(testFileUrl("jsimportfail/importPragmaLibrary.js").toString() + QLatin1String(":6: ReferenceError: Component is not defined")))
3859             << (QStringList() << QLatin1String("testValue"))
3860             << (QVariantList() << QVariant(0));
3861
3862     QTest::newRow("import pragma library script which has an import")
3863             << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
3864             << QString()
3865             << QStringList()
3866             << (QStringList() << QLatin1String("testValue"))
3867             << (QVariantList() << QVariant(55));
3868
3869     QTest::newRow("import pragma library script which has a pragma library import")
3870             << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3871             << QString()
3872             << QStringList()
3873             << (QStringList() << QLatin1String("testValue"))
3874             << (QVariantList() << QVariant(18));
3875
3876     QTest::newRow("import singleton type into js import")
3877             << testFileUrl("jsimport/testImportSingletonType.qml")
3878             << QString()
3879             << QStringList()
3880             << (QStringList() << QLatin1String("testValue"))
3881             << (QVariantList() << QVariant(20));
3882
3883     QTest::newRow("import module which exports a script")
3884             << testFileUrl("jsimport/testJsImport.qml")
3885             << QString()
3886             << QStringList()
3887             << (QStringList() << QLatin1String("importedScriptStringValue")
3888                               << QLatin1String("renamedScriptStringValue")
3889                               << QLatin1String("reimportedScriptStringValue"))
3890             << (QVariantList() << QVariant(QString("Hello"))
3891                                << QVariant(QString("Hello"))
3892                                << QVariant(QString("Hello")));
3893
3894     QTest::newRow("import module which exports a script which imports a remote module")
3895             << testFileUrl("jsimport/testJsRemoteImport.qml")
3896             << QString()
3897             << QStringList()
3898             << (QStringList() << QLatin1String("importedScriptStringValue")
3899                               << QLatin1String("renamedScriptStringValue")
3900                               << QLatin1String("reimportedScriptStringValue"))
3901             << (QVariantList() << QVariant(QString("Hello"))
3902                                << QVariant(QString("Hello"))
3903                                << QVariant(QString("Hello")));
3904
3905     QTest::newRow("malformed import statement")
3906             << testFileUrl("jsimportfail/malformedImport.qml")
3907             << QString()
3908             << (QStringList() << testFileUrl("jsimportfail/malformedImport.js").toString() + QLatin1String(":1: SyntaxError: Unexpected token ."))
3909             << QStringList()
3910             << QVariantList();
3911
3912     QTest::newRow("malformed file name")
3913             << testFileUrl("jsimportfail/malformedFile.qml")
3914             << QString()
3915             << (QStringList() << testFileUrl("jsimportfail/malformedFile.js").toString() + QLatin1String(":1:9: Imported file must be a script"))
3916             << QStringList()
3917             << QVariantList();
3918
3919     QTest::newRow("missing file qualifier")
3920             << testFileUrl("jsimportfail/missingFileQualifier.qml")
3921             << QString()
3922             << (QStringList() << testFileUrl("jsimportfail/missingFileQualifier.js").toString() + QLatin1String(":1:1: File import requires a qualifier"))
3923             << QStringList()
3924             << QVariantList();
3925
3926     QTest::newRow("malformed file qualifier")
3927             << testFileUrl("jsimportfail/malformedFileQualifier.qml")
3928             << QString()
3929             << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.js").toString() + QLatin1String(":1:20: File import requires a qualifier"))
3930             << QStringList()
3931             << QVariantList();
3932
3933     QTest::newRow("malformed module qualifier 2")
3934             << testFileUrl("jsimportfail/malformedFileQualifier.2.qml")
3935             << QString()
3936             << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.2.js").toString() + QLatin1String(":1:1: Invalid import qualifier"))
3937             << QStringList()
3938             << QVariantList();
3939
3940     QTest::newRow("malformed module uri")
3941             << testFileUrl("jsimportfail/malformedModule.qml")
3942             << QString()
3943             << (QStringList() << testFileUrl("jsimportfail/malformedModule.js").toString() + QLatin1String(":1:17: Invalid module URI"))
3944             << QStringList()
3945             << QVariantList();
3946
3947     QTest::newRow("missing module version")
3948             << testFileUrl("jsimportfail/missingModuleVersion.qml")
3949             << QString()
3950             << (QStringList() << testFileUrl("jsimportfail/missingModuleVersion.js").toString() + QLatin1String(":1:17: Module import requires a version"))
3951             << QStringList()
3952             << QVariantList();
3953
3954     QTest::newRow("malformed module version")
3955             << testFileUrl("jsimportfail/malformedModuleVersion.qml")
3956             << QString()
3957             << (QStringList() << testFileUrl("jsimportfail/malformedModuleVersion.js").toString() + QLatin1String(":1:17: Module import requires a version"))
3958             << QStringList()
3959             << QVariantList();
3960
3961     QTest::newRow("missing module qualifier")
3962             << testFileUrl("jsimportfail/missingModuleQualifier.qml")
3963             << QString()
3964             << (QStringList() << testFileUrl("jsimportfail/missingModuleQualifier.js").toString() + QLatin1String(":1:1: Module import requires a qualifier"))
3965             << QStringList()
3966             << QVariantList();
3967
3968     QTest::newRow("malformed module qualifier")
3969             << testFileUrl("jsimportfail/malformedModuleQualifier.qml")
3970             << QString()
3971             << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.js").toString() + QLatin1String(":1:21: Module import requires a qualifier"))
3972             << QStringList()
3973             << QVariantList();
3974
3975     QTest::newRow("malformed module qualifier 2")
3976             << testFileUrl("jsimportfail/malformedModuleQualifier.2.qml")
3977             << QString()
3978             << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.2.js").toString() + QLatin1String(":1:1: Invalid import qualifier"))
3979             << QStringList()
3980             << QVariantList();
3981 }
3982
3983 void tst_qqmlecmascript::importScripts()
3984 {
3985     QFETCH(QUrl, testfile);
3986     QFETCH(QString, errorMessage);
3987     QFETCH(QStringList, warningMessages);
3988     QFETCH(QStringList, propertyNames);
3989     QFETCH(QVariantList, propertyValues);
3990
3991     TestHTTPServer server(8111);
3992     QVERIFY(server.isValid());
3993     server.serveDirectory(dataDirectory() + "/remote");
3994
3995     QStringList importPathList = engine.importPathList();
3996
3997     QString remotePath(QLatin1String("http://127.0.0.1:8111/"));
3998     engine.addImportPath(remotePath);
3999
4000     QQmlComponent component(&engine, testfile);
4001
4002     if (!errorMessage.isEmpty())
4003         QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
4004
4005     if (warningMessages.size())
4006         foreach (const QString &warning, warningMessages)
4007             QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
4008
4009     QTRY_VERIFY(component.isReady());
4010
4011     QObject *object = component.create();
4012     if (!errorMessage.isEmpty()) {
4013         QVERIFY(object == 0);
4014     } else {
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));
4018         delete object;
4019     }
4020
4021     engine.setImportPathList(importPathList);
4022 }
4023
4024 void tst_qqmlecmascript::scarceResources_other()
4025 {
4026     /* These tests require knowledge of state, since we test values after
4027        performing signal or function invocation. */
4028
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;
4034     QObject *srsc = 0;
4035     QObject *object = 0;
4036
4037     /* property var semantics */
4038
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");
4043     QVERIFY(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.
4059     delete object;
4060
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.
4078     delete object;
4079
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.
4095     delete object;
4096
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.
4107     delete object;
4108
4109     /* property variant semantics */
4110
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");
4116     QVERIFY(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.
4132     delete object;
4133
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.
4151     delete object;
4152
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.
4168     delete object;
4169 }
4170
4171 void tst_qqmlecmascript::scarceResources_data()
4172 {
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");
4180
4181     QPixmap origPixmap(100, 100);
4182     origPixmap.fill(Qt::blue);
4183
4184     /* property var semantics */
4185
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")
4192         << true
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)
4197         << QStringList();
4198
4199     QTest::newRow("var: import scarce resource copy from JS")
4200         << testFileUrl("scarceResourceCopyFromJs.var.qml")
4201         << true
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)
4206         << QStringList();
4207
4208     QTest::newRow("var: import released scarce resource copy from JS")
4209         << testFileUrl("scarceResourceDestroyedCopy.var.qml")
4210         << true
4211         << true // explicitly released, so it will be detached
4212         << (QStringList() << QLatin1String("scarceResourceCopy"))
4213         << (QList<QVariant>() << false)
4214         << (QList<QVariant>() << QVariant())
4215         << QStringList();
4216
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")
4221         << true
4222         << true // auto released, so it will be detached
4223         << (QStringList() << QLatin1String("scarceResourceTest"))
4224         << (QList<QVariant>() << true)
4225         << (QList<QVariant>() << QVariant(100))
4226         << QStringList();
4227     QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4228         << testFileUrl("scarceResourceTestPreserve.var.qml")
4229         << true
4230         << false // won't be detached because we explicitly preserve it
4231         << (QStringList() << QLatin1String("scarceResourceTest"))
4232         << (QList<QVariant>() << true)
4233         << (QList<QVariant>() << QVariant(100))
4234         << QStringList();
4235     QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4236         << testFileUrl("scarceResourceTestMultiple.var.qml")
4237         << true
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))
4242         << QStringList();
4243
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.
4249         << false
4250         << QStringList()
4251         << QList<QVariant>()
4252         << QList<QVariant>()
4253         << QStringList();
4254     QTest::newRow("var: import with binding without explicit preserve")
4255         << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4256         << false
4257         << false
4258         << (QStringList() << QLatin1String("scarceResourceCopy"))
4259         << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4260         << (QList<QVariant>() << QVariant())
4261         << QStringList();
4262     QTest::newRow("var: import with explicit release after binding evaluation")
4263         << testFileUrl("scarceResourceCopyImport.var.qml")
4264         << false
4265         << false
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))
4269         << QStringList();
4270     QTest::newRow("var: import with different js objects")
4271         << testFileUrl("scarceResourceCopyImportDifferent.var.qml")
4272         << false
4273         << false
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))
4277         << QStringList();
4278     QTest::newRow("var: import with different js objects and explicit release")
4279         << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml")
4280         << false
4281         << false
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())
4285         << QStringList();
4286     QTest::newRow("var: import with same js objects and explicit release")
4287         << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml")
4288         << false
4289         << false
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())
4293         << QStringList();
4294     QTest::newRow("var: binding with same js objects and explicit release")
4295         << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml")
4296         << false
4297         << false
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())
4301         << QStringList();
4302
4303
4304     /* property variant semantics */
4305
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")
4312         << true
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)
4317         << QStringList();
4318
4319     QTest::newRow("variant: import scarce resource copy from JS")
4320         << testFileUrl("scarceResourceCopyFromJs.variant.qml")
4321         << true
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)
4326         << QStringList();
4327
4328     QTest::newRow("variant: import released scarce resource copy from JS")
4329         << testFileUrl("scarceResourceDestroyedCopy.variant.qml")
4330         << true
4331         << true // explicitly released, so it will be detached
4332         << (QStringList() << QLatin1String("scarceResourceCopy"))
4333         << (QList<QVariant>() << false)
4334         << (QList<QVariant>() << QVariant())
4335         << QStringList();
4336
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")
4341         << true
4342         << true // auto released, so it will be detached
4343         << (QStringList() << QLatin1String("scarceResourceTest"))
4344         << (QList<QVariant>() << true)
4345         << (QList<QVariant>() << QVariant(100))
4346         << QStringList();
4347     QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect")
4348         << testFileUrl("scarceResourceTestPreserve.variant.qml")
4349         << true
4350         << false // won't be detached because we explicitly preserve it
4351         << (QStringList() << QLatin1String("scarceResourceTest"))
4352         << (QList<QVariant>() << true)
4353         << (QList<QVariant>() << QVariant(100))
4354         << QStringList();
4355     QTest::newRow("variant: import multiple scarce resources")
4356         << testFileUrl("scarceResourceTestMultiple.variant.qml")
4357         << true
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))
4362         << QStringList();
4363
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.
4369         << false
4370         << QStringList()
4371         << QList<QVariant>()
4372         << QList<QVariant>()
4373         << QStringList();
4374     QTest::newRow("variant: import with binding without explicit preserve")
4375         << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4376         << false
4377         << false
4378         << (QStringList() << QLatin1String("scarceResourceCopy"))
4379         << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4380         << (QList<QVariant>() << QVariant())
4381         << QStringList();
4382     QTest::newRow("variant: import with explicit release after binding evaluation")
4383         << testFileUrl("scarceResourceCopyImport.variant.qml")
4384         << false
4385         << false
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())
4389         << QStringList();
4390 }
4391
4392 void tst_qqmlecmascript::scarceResources()
4393 {
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);
4401
4402     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4403     ScarceResourceObject *eo = 0;
4404     QObject *object = 0;
4405
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);
4413
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>());
4419         }
4420     }
4421
4422     if (readDetachStatus) {
4423         eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4424         QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
4425     }
4426
4427     QVERIFY(ep->scarceResources.isEmpty());
4428     delete object;
4429 }
4430
4431 void tst_qqmlecmascript::propertyChangeSlots()
4432 {
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);
4437     delete object;
4438
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);
4446     delete object;
4447
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);
4454     delete object;
4455
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);
4462     delete object;
4463
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);
4470     delete object;
4471 }
4472
4473 void tst_qqmlecmascript::propertyVar_data()
4474 {
4475     QTest::addColumn<QUrl>("qmlFile");
4476
4477     // valid
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");
4494 }
4495
4496 void tst_qqmlecmascript::propertyVar()
4497 {
4498     QFETCH(QUrl, qmlFile);
4499
4500     QQmlComponent component(&engine, qmlFile);
4501     QObject *object = component.create();
4502     QVERIFY(object != 0);
4503
4504     QCOMPARE(object->property("test").toBool(), true);
4505
4506     delete object;
4507 }
4508
4509 void tst_qqmlecmascript::propertyQJSValue_data()
4510 {
4511     QTest::addColumn<QUrl>("qmlFile");
4512
4513     // valid
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");
4530
4531     QTest::newRow("reset property") << testFileUrl("propertyQJSValue.reset.qml");
4532     QTest::newRow("reset property in binding") << testFileUrl("propertyQJSValue.bindingreset.qml");
4533 }
4534
4535 void tst_qqmlecmascript::propertyQJSValue()
4536 {
4537     QFETCH(QUrl, qmlFile);
4538
4539     QQmlComponent component(&engine, qmlFile);
4540     QObject *object = component.create();
4541     QVERIFY(object != 0);
4542
4543     QCOMPARE(object->property("test").toBool(), true);
4544
4545     delete object;
4546 }
4547
4548 // Tests that we can write QVariant values to var properties from C++
4549 void tst_qqmlecmascript::propertyVarCpp()
4550 {
4551     QObject *object = 0;
4552
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);
4572     delete object;
4573 }
4574
4575 static void gc(QQmlEngine &engine)
4576 {
4577     engine.collectGarbage();
4578     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4579     QCoreApplication::processEvents();
4580 }
4581
4582 void tst_qqmlecmascript::propertyVarOwnership()
4583 {
4584     // Referenced JS objects are not collected
4585     {
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);
4592     delete object;
4593     }
4594     // Referenced JS objects are not collected
4595     {
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);
4602     delete object;
4603     }
4604     // Qt objects are not collected until they've been dereferenced
4605     {
4606     QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml"));
4607     QObject *object = component.create();
4608     QVERIFY(object != 0);
4609
4610     QCOMPARE(object->property("test2").toBool(), false);
4611     QCOMPARE(object->property("test2").toBool(), false);
4612
4613     QMetaObject::invokeMethod(object, "runTest");
4614     QCOMPARE(object->property("test1").toBool(), true);
4615
4616     QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4617     QVERIFY(!referencedObject.isNull());
4618     gc(engine);
4619     QVERIFY(!referencedObject.isNull());
4620
4621     QMetaObject::invokeMethod(object, "runTest2");
4622     QCOMPARE(object->property("test2").toBool(), true);
4623     gc(engine);
4624     QVERIFY(referencedObject.isNull());
4625
4626     delete object;
4627     }
4628     // Self reference does not prevent Qt object collection
4629     {
4630     QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml"));
4631     QObject *object = component.create();
4632     QVERIFY(object != 0);
4633
4634     QCOMPARE(object->property("test").toBool(), true);
4635
4636     QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4637     QVERIFY(!referencedObject.isNull());
4638     gc(engine);
4639     QVERIFY(!referencedObject.isNull());
4640
4641     QMetaObject::invokeMethod(object, "runTest");
4642     gc(engine);
4643     QVERIFY(referencedObject.isNull());
4644
4645     delete object;
4646     }
4647     // Garbage collection cannot result in attempted dereference of empty handle
4648     {
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);
4654     delete object;
4655     }
4656 }
4657
4658 void tst_qqmlecmascript::propertyVarImplicitOwnership()
4659 {
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.
4685     delete object;
4686 }
4687
4688 void tst_qqmlecmascript::propertyVarReparent()
4689 {
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.
4727     delete object;
4728 }
4729
4730 void tst_qqmlecmascript::propertyVarReparentNullContext()
4731 {
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).
4765     delete object;
4766     QVERIFY(imageGuard.isNull());                                  // should now be dead.
4767 }
4768
4769 void tst_qqmlecmascript::propertyVarCircular()
4770 {
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.
4793     delete object;
4794 }
4795
4796 void tst_qqmlecmascript::propertyVarCircular2()
4797 {
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());
4814     gc(engine);
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
4822     delete object;
4823 }
4824
4825 void tst_qqmlecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
4826 {
4827     *(int*)(parameter) += 1;
4828     qPersistentDispose(object);
4829 }
4830
4831 void tst_qqmlecmascript::propertyVarInheritance()
4832 {
4833     int propertyVarWeakRefCallbackCount = 0;
4834
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;
4850     {
4851         v8::HandleScope hs;
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);
4860         gc(engine);
4861         QVERIFY(propertyVarWeakRefCallbackCount == 0);
4862     }
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.
4868     gc(engine);
4869     QCOMPARE(propertyVarWeakRefCallbackCount, 2);                  // should have been called for both, since all refs should be weak.
4870     delete object;
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.
4873 }
4874
4875 void tst_qqmlecmascript::propertyVarInheritance2()
4876 {
4877     int propertyVarWeakRefCallbackCount = 0;
4878
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;
4894     {
4895         v8::HandleScope hs;
4896         propertyVarWeakRefCallbackCount = 0;                           // reset callback count.
4897         childObjectVarArrayValueHandle = qPersistentNew(QQmlVMEMetaObject::get(childObject)->vmeProperty(childObject->metaObject()->indexOfProperty("vp")));
4898         childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4899         gc(engine);
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);
4903     }
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.
4908     delete object;
4909 }
4910
4911 // Ensure that QObject type conversion works on binding assignment
4912 void tst_qqmlecmascript::elementAssign()
4913 {
4914     QQmlComponent component(&engine, testFileUrl("elementAssign.qml"));
4915
4916     QObject *object = component.create();
4917     QVERIFY(object != 0);
4918
4919     QCOMPARE(object->property("test").toBool(), true);
4920
4921     delete object;
4922 }
4923
4924 // QTBUG-12457
4925 void tst_qqmlecmascript::objectPassThroughSignals()
4926 {
4927     QQmlComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml"));
4928
4929     QObject *object = component.create();
4930     QVERIFY(object != 0);
4931
4932     QCOMPARE(object->property("test").toBool(), true);
4933
4934     delete object;
4935 }
4936
4937 // QTBUG-21626
4938 void tst_qqmlecmascript::objectConversion()
4939 {
4940     QQmlComponent component(&engine, testFileUrl("objectConversion.qml"));
4941
4942     QObject *object = component.create();
4943     QVERIFY(object != 0);
4944     QVariant retn;
4945     QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
4946     QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
4947
4948     delete object;
4949 }
4950
4951
4952 // QTBUG-20242
4953 void tst_qqmlecmascript::booleanConversion()
4954 {
4955     QQmlComponent component(&engine, testFileUrl("booleanConversion.qml"));
4956
4957     QObject *object = component.create();
4958     QVERIFY(object != 0);
4959
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);
4965
4966     QCOMPARE(object->property("test_false1").toBool(), false);
4967     QCOMPARE(object->property("test_false2").toBool(), false);
4968     QCOMPARE(object->property("test_false3").toBool(), false);
4969
4970     delete object;
4971 }
4972
4973 void tst_qqmlecmascript::handleReferenceManagement()
4974 {
4975
4976     int dtorCount = 0;
4977     {
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");
4987         gc(engine);
4988         QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
4989         delete object;
4990         hrmEngine.collectGarbage();
4991         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4992         QCoreApplication::processEvents();
4993         QCOMPARE(dtorCount, 3);
4994     }
4995
4996     dtorCount = 0;
4997     {
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");
5007         gc(engine);
5008         QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
5009         delete object;
5010         hrmEngine.collectGarbage();
5011         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5012         QCoreApplication::processEvents();
5013         QCOMPARE(dtorCount, 3);
5014     }
5015
5016     dtorCount = 0;
5017     {
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");
5024         QVERIFY(crh != 0);
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);
5036         gc(engine);
5037         QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
5038         delete object;
5039         hrmEngine.collectGarbage();
5040         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5041         QCoreApplication::processEvents();
5042         QCOMPARE(dtorCount, 3);
5043     }
5044
5045     dtorCount = 0;
5046     {
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");
5053         QVERIFY(crh != 0);
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));
5070         gc(engine);
5071         QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
5072         delete object;
5073         hrmEngine.collectGarbage();
5074         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5075         QCoreApplication::processEvents();
5076         QCOMPARE(dtorCount, 3);
5077     }
5078
5079     dtorCount = 0;
5080     {
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");
5092         QVERIFY(crh1 != 0);
5093         QVERIFY(crh2 != 0);
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);
5112         gc(engine);
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.
5116         delete object1;
5117         delete object2;
5118         hrmEngine1.collectGarbage();
5119         hrmEngine2.collectGarbage();
5120         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5121         QCoreApplication::processEvents();
5122         QCOMPARE(dtorCount, 6);
5123     }
5124
5125     dtorCount = 0;
5126     {
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");
5138         QVERIFY(crh1 != 0);
5139         QVERIFY(crh2 != 0);
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));
5171         gc(engine);
5172         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5173         QCoreApplication::processEvents();
5174         QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
5175         delete object1;
5176         delete object2;
5177         hrmEngine1.collectGarbage();
5178         hrmEngine2.collectGarbage();
5179         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5180         QCoreApplication::processEvents();
5181         QCOMPARE(dtorCount, 6);
5182     }
5183
5184     dtorCount = 0;
5185     {
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");
5197         QVERIFY(crh1 != 0);
5198         QVERIFY(crh2 != 0);
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);
5224         gc(*hrmEngine1);
5225         gc(*hrmEngine2);
5226         QCOMPARE(dtorCount, 0);
5227         delete hrmEngine2; // should trigger deletion of objects with JS ownership tracked by this engine
5228         gc(*hrmEngine1);
5229         QCOMPARE(dtorCount, 2); // first2 and second2 should have been deleted.
5230         delete object1;
5231         delete object2;
5232         gc(*hrmEngine1);
5233         QCOMPARE(dtorCount, 6); // deleting object1 and object2 should trigger deletion of first1 and first2.
5234         delete hrmEngine1;
5235         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5236         QCoreApplication::processEvents();
5237         QCOMPARE(dtorCount, 6); // all objects should have been cleaned up prior to deleting hrmEngine1.
5238     }
5239
5240     {
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");
5247         gc(engine);
5248         QMetaObject::invokeMethod(object, "ensureReference");
5249         gc(engine);
5250         QMetaObject::invokeMethod(object, "removeReference");
5251         gc(engine);
5252         QMetaObject::invokeMethod(object, "ensureDeletion");
5253         QCOMPARE(object->property("success").toBool(), true);
5254         delete object;
5255     }
5256
5257     {
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");
5264         gc(engine);
5265         QMetaObject::invokeMethod(object, "ensureReference");
5266         gc(engine);
5267         QMetaObject::invokeMethod(object, "removeReference");
5268         gc(engine);
5269         QMetaObject::invokeMethod(object, "ensureDeletion");
5270         QCOMPARE(object->property("success").toBool(), true);
5271         delete object;
5272     }
5273
5274     {
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");
5281         gc(engine);
5282         QMetaObject::invokeMethod(object, "ensureReference");
5283         gc(engine);
5284         QMetaObject::invokeMethod(object, "manuallyDelete");
5285         gc(engine);
5286         QMetaObject::invokeMethod(object, "ensureDeleted");
5287         QCOMPARE(object->property("success").toBool(), true);
5288         delete object;
5289     }
5290 }
5291
5292 void tst_qqmlecmascript::stringArg()
5293 {
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());
5299
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());
5304
5305     delete object;
5306 }
5307
5308 void tst_qqmlecmascript::readonlyDeclaration()
5309 {
5310     QQmlComponent component(&engine, testFileUrl("readonlyDeclaration.qml"));
5311
5312     QObject *object = component.create();
5313     QVERIFY(object != 0);
5314
5315     QCOMPARE(object->property("test").toBool(), true);
5316
5317     delete object;
5318 }
5319
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()
5326 {
5327     {
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");
5333         QVERIFY(seq != 0);
5334
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);
5354
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")));
5362
5363         QMetaObject::invokeMethod(object, "enumerateSequenceElements");
5364         QCOMPARE(object->property("enumerationMatches").toBool(), true);
5365
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);
5371
5372         QMetaObject::invokeMethod(object, "testReferenceDeletion");
5373         QCOMPARE(object->property("referenceDeletion").toBool(), true);
5374
5375         delete object;
5376     }
5377
5378     {
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");
5384         QVERIFY(seq != 0);
5385
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());
5391
5392         QMetaObject::invokeMethod(object, "performTest");
5393
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
5400
5401         delete object;
5402     }
5403 }
5404
5405 void tst_qqmlecmascript::sequenceConversionWrite()
5406 {
5407     {
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");
5413         QVERIFY(seq != 0);
5414
5415         QMetaObject::invokeMethod(object, "writeSequences");
5416         QCOMPARE(object->property("success").toBool(), true);
5417
5418         QMetaObject::invokeMethod(object, "writeSequenceElements");
5419         QCOMPARE(object->property("success").toBool(), true);
5420
5421         QMetaObject::invokeMethod(object, "writeOtherElements");
5422         QCOMPARE(object->property("success").toBool(), true);
5423
5424         QMetaObject::invokeMethod(object, "testReferenceDeletion");
5425         QCOMPARE(object->property("referenceDeletion").toBool(), true);
5426
5427         delete object;
5428     }
5429
5430     {
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");
5436         QVERIFY(seq != 0);
5437
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());
5441
5442         QMetaObject::invokeMethod(object, "performTest");
5443
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);
5446
5447         delete object;
5448     }
5449 }
5450
5451 void tst_qqmlecmascript::sequenceConversionArray()
5452 {
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);
5466     delete object;
5467 }
5468
5469
5470 void tst_qqmlecmascript::sequenceConversionIndexes()
5471 {
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());
5486     delete object;
5487 }
5488
5489 void tst_qqmlecmascript::sequenceConversionThreads()
5490 {
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);
5497
5498     QMetaObject::invokeMethod(object, "testIntSequence");
5499     QTRY_VERIFY(object->property("finished").toBool());
5500     QVERIFY(object->property("success").toBool());
5501
5502     QMetaObject::invokeMethod(object, "testQrealSequence");
5503     QTRY_VERIFY(object->property("finished").toBool());
5504     QVERIFY(object->property("success").toBool());
5505
5506     QMetaObject::invokeMethod(object, "testBoolSequence");
5507     QTRY_VERIFY(object->property("finished").toBool());
5508     QVERIFY(object->property("success").toBool());
5509
5510     QMetaObject::invokeMethod(object, "testStringSequence");
5511     QTRY_VERIFY(object->property("finished").toBool());
5512     QVERIFY(object->property("success").toBool());
5513
5514     QMetaObject::invokeMethod(object, "testQStringSequence");
5515     QTRY_VERIFY(object->property("finished").toBool());
5516     QVERIFY(object->property("success").toBool());
5517
5518     QMetaObject::invokeMethod(object, "testUrlSequence");
5519     QTRY_VERIFY(object->property("finished").toBool());
5520     QVERIFY(object->property("success").toBool());
5521
5522     QMetaObject::invokeMethod(object, "testVariantSequence");
5523     QTRY_VERIFY(object->property("finished").toBool());
5524     QVERIFY(object->property("success").toBool());
5525
5526     delete object;
5527 }
5528
5529 void tst_qqmlecmascript::sequenceConversionBindings()
5530 {
5531     {
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);
5541         delete object;
5542     }
5543
5544     {
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);
5551         delete object;
5552     }
5553 }
5554
5555 void tst_qqmlecmascript::sequenceConversionCopy()
5556 {
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);
5571     delete object;
5572 }
5573
5574 void tst_qqmlecmascript::assignSequenceTypes()
5575 {
5576     // test binding array to sequence type property
5577     {
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")));
5587     delete object;
5588     }
5589
5590     // test binding literal to sequence type property
5591     {
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")));
5601     delete object;
5602     }
5603
5604     // test binding single value to sequence type property
5605     {
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"))));
5613     delete object;
5614     }
5615
5616     // test assigning array to sequence type property in js function
5617     {
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")));
5627     delete object;
5628     }
5629
5630     // test assigning literal to sequence type property in js function
5631     {
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")));
5641     delete object;
5642     }
5643
5644     // test assigning single value to sequence type property in js function
5645     {
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"))));
5653     delete object;
5654     }
5655
5656     // test QList<QUrl> literal assignment and binding assignment causes url resolution when required
5657     {
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"))));
5672     delete object;
5673     }
5674 }
5675
5676 // Test that assigning a null object works 
5677 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
5678 void tst_qqmlecmascript::nullObjectBinding()
5679 {
5680     QQmlComponent component(&engine, testFileUrl("nullObjectBinding.qml"));
5681
5682     QObject *object = component.create();
5683     QVERIFY(object != 0);
5684
5685     QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
5686
5687     delete object;
5688 }
5689
5690 // Test that bindings don't evaluate once the engine has been destroyed
5691 void tst_qqmlecmascript::deletedEngine()
5692 {
5693     QQmlEngine *engine = new QQmlEngine;
5694     QQmlComponent component(engine, testFileUrl("deletedEngine.qml"));
5695
5696     QObject *object = component.create();
5697     QVERIFY(object != 0);
5698
5699     QCOMPARE(object->property("a").toInt(), 39);
5700     object->setProperty("b", QVariant(9));
5701     QCOMPARE(object->property("a").toInt(), 117);
5702
5703     delete engine;
5704
5705     QCOMPARE(object->property("a").toInt(), 117);
5706     object->setProperty("b", QVariant(10));
5707     QCOMPARE(object->property("a").toInt(), 117);
5708
5709     delete object;
5710 }
5711
5712 // Test the crashing part of QTBUG-9705
5713 void tst_qqmlecmascript::libraryScriptAssert()
5714 {
5715     QQmlComponent component(&engine, testFileUrl("libraryScriptAssert.qml"));
5716
5717     QObject *object = component.create();
5718     QVERIFY(object != 0);
5719
5720     delete object;
5721 }
5722
5723 void tst_qqmlecmascript::variantsAssignedUndefined()
5724 {
5725     QQmlComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml"));
5726
5727     QObject *object = component.create();
5728     QVERIFY(object != 0);
5729
5730     QCOMPARE(object->property("test1").toInt(), 10);
5731     QCOMPARE(object->property("test2").toInt(), 11);
5732
5733     object->setProperty("runTest", true);
5734
5735     QCOMPARE(object->property("test1"), QVariant());
5736     QCOMPARE(object->property("test2"), QVariant());
5737
5738
5739     delete object;
5740 }
5741
5742 void tst_qqmlecmascript::qtbug_9792()
5743 {
5744     QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml"));
5745
5746     QQmlContext *context = new QQmlContext(engine.rootContext());
5747
5748     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
5749     QVERIFY(object != 0);
5750
5751     QTest::ignoreMessage(QtDebugMsg, "Hello world!");
5752     object->basicSignal();
5753
5754     delete context;
5755
5756     transientErrorsMsgCount = 0;
5757     QtMessageHandler old = qInstallMessageHandler(transientErrorsMsgHandler);
5758
5759     object->basicSignal();
5760     
5761     qInstallMessageHandler(old);
5762
5763     QCOMPARE(transientErrorsMsgCount, 0);
5764
5765     delete object;
5766 }
5767
5768 // Verifies that QQmlGuard<>s used in the vmemetaobject are cleaned correctly
5769 void tst_qqmlecmascript::qtcreatorbug_1289()
5770 {
5771     QQmlComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml"));
5772
5773     QObject *o = component.create();
5774     QVERIFY(o != 0);
5775
5776     QObject *nested = qvariant_cast<QObject *>(o->property("object"));
5777     QVERIFY(nested != 0);
5778
5779     QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
5780
5781     delete nested;
5782     nested = qvariant_cast<QObject *>(o->property("object"));
5783     QVERIFY(nested == 0);
5784
5785     // If the bug is present, the next line will crash
5786     delete o;
5787 }
5788
5789 // Test that we shut down without stupid warnings
5790 void tst_qqmlecmascript::noSpuriousWarningsAtShutdown()
5791 {
5792     {
5793     QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml"));
5794
5795     QObject *o = component.create();
5796
5797     transientErrorsMsgCount = 0;
5798     QtMessageHandler old = qInstallMessageHandler(transientErrorsMsgHandler);
5799
5800     delete o;
5801
5802     qInstallMessageHandler(old);
5803
5804     QCOMPARE(transientErrorsMsgCount, 0);
5805     }
5806
5807
5808     {
5809     QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml"));
5810
5811     QObject *o = component.create();
5812
5813     transientErrorsMsgCount = 0;
5814     QtMessageHandler old = qInstallMessageHandler(transientErrorsMsgHandler);
5815
5816     delete o;
5817
5818     qInstallMessageHandler(old);
5819
5820     QCOMPARE(transientErrorsMsgCount, 0);
5821     }
5822 }
5823
5824 void tst_qqmlecmascript::canAssignNullToQObject()
5825 {
5826     {
5827     QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml"));
5828
5829     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5830     QVERIFY(o != 0);
5831
5832     QVERIFY(o->objectProperty() != 0);
5833
5834     o->setProperty("runTest", true);
5835
5836     QVERIFY(o->objectProperty() == 0);
5837
5838     delete o;
5839     }
5840
5841     {
5842     QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml"));
5843
5844     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5845     QVERIFY(o != 0);
5846
5847     QVERIFY(o->objectProperty() == 0);
5848
5849     delete o;
5850     }
5851 }
5852
5853 void tst_qqmlecmascript::functionAssignment_fromBinding()
5854 {
5855     QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml"));
5856
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());
5866
5867     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5868     QVERIFY(o != 0);
5869
5870     QVERIFY(!o->property("a").isValid());
5871
5872     delete o;
5873 }
5874
5875 void tst_qqmlecmascript::functionAssignment_fromJS()
5876 {
5877     QFETCH(QString, triggerProperty);
5878
5879     QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5880     QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5881
5882     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5883     QVERIFY(o != 0);
5884     QVERIFY(!o->property("a").isValid());
5885
5886     o->setProperty("aNumber", QVariant(5));
5887     o->setProperty(triggerProperty.toUtf8().constData(), true);
5888     QCOMPARE(o->property("a"), QVariant(50));
5889
5890     o->setProperty("aNumber", QVariant(10));
5891     QCOMPARE(o->property("a"), QVariant(100));
5892
5893     delete o;
5894 }
5895
5896 void tst_qqmlecmascript::functionAssignment_fromJS_data()
5897 {
5898     QTest::addColumn<QString>("triggerProperty");
5899
5900     QTest::newRow("assign to property") << "assignToProperty";
5901     QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
5902
5903     QTest::newRow("assign to value type") << "assignToValueType";
5904
5905     QTest::newRow("use 'this'") << "assignWithThis";
5906     QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
5907 }
5908
5909 void tst_qqmlecmascript::functionAssignmentfromJS_invalid()
5910 {
5911     QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5912     QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5913
5914     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5915     QVERIFY(o != 0);
5916     QVERIFY(!o->property("a").isValid());
5917
5918     o->setProperty("assignFuncWithoutReturn", true);
5919     QVERIFY(!o->property("a").isValid());
5920
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);
5925
5926     warning = url + ":71:29: Unable to assign QString to int";
5927     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5928     o->setProperty("assignWrongTypeToValueType", true);
5929
5930     delete o;
5931 }
5932
5933 void tst_qqmlecmascript::functionAssignment_afterBinding()
5934 {
5935     QQmlComponent component(&engine, testFileUrl("functionAssignment.3.qml"));
5936
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());
5940
5941     QObject *o = component.create();
5942     QVERIFY(o != 0);
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
5945
5946     delete o;
5947 }
5948
5949 void tst_qqmlecmascript::eval()
5950 {
5951     QQmlComponent component(&engine, testFileUrl("eval.qml"));
5952
5953     QObject *o = component.create();
5954     QVERIFY(o != 0);
5955
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);
5961
5962     delete o;
5963 }
5964
5965 void tst_qqmlecmascript::function()
5966 {
5967     QQmlComponent component(&engine, testFileUrl("function.qml"));
5968
5969     QObject *o = component.create();
5970     QVERIFY(o != 0);
5971
5972     QCOMPARE(o->property("test1").toBool(), true);
5973     QCOMPARE(o->property("test2").toBool(), true);
5974     QCOMPARE(o->property("test3").toBool(), true);
5975
5976     delete o;
5977 }
5978
5979 void tst_qqmlecmascript::functionException()
5980 {
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();
5987     QVERIFY(o != 0);
5988     QMetaObject::invokeMethod(o, "dynamicSlot");
5989     delete o;
5990 }
5991
5992 // Test the "Qt.include" method
5993 void tst_qqmlecmascript::include()
5994 {
5995     // Non-library relative include
5996     {
5997     QQmlComponent component(&engine, testFileUrl("include.qml"));
5998     QObject *o = component.create();
5999     QVERIFY(o != 0);
6000
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);
6007
6008     delete o;
6009     }
6010
6011     // Library relative include
6012     {
6013     QQmlComponent component(&engine, testFileUrl("include_shared.qml"));
6014     QObject *o = component.create();
6015     QVERIFY(o != 0);
6016
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);
6023
6024     delete o;
6025     }
6026
6027     // Callback
6028     {
6029     QQmlComponent component(&engine, testFileUrl("include_callback.qml"));
6030     QObject *o = component.create();
6031     QVERIFY(o != 0);
6032
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);
6039
6040     delete o;
6041     }
6042
6043     // Including file with ".pragma library"
6044     {
6045     QQmlComponent component(&engine, testFileUrl("include_pragma.qml"));
6046     QObject *o = component.create();
6047     QVERIFY(o != 0);
6048     QCOMPARE(o->property("test1").toInt(), 100);
6049
6050     delete o;
6051     }
6052
6053     // Remote - success
6054     {
6055     TestHTTPServer server(8111);
6056     QVERIFY(server.isValid());
6057     server.serveDirectory(dataDirectory());
6058
6059     QQmlComponent component(&engine, testFileUrl("include_remote.qml"));
6060     QObject *o = component.create();
6061     QVERIFY(o != 0);
6062
6063     QTRY_VERIFY(o->property("done").toBool() == true);
6064     QTRY_VERIFY(o->property("done2").toBool() == true);
6065
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);
6071
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);
6077
6078     delete o;
6079     }
6080
6081     // Remote - error
6082     {
6083     TestHTTPServer server(8111);
6084     QVERIFY(server.isValid());
6085     server.serveDirectory(dataDirectory());
6086
6087     QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml"));
6088     QObject *o = component.create();
6089     QVERIFY(o != 0);
6090
6091     QTRY_VERIFY(o->property("done").toBool() == true);
6092
6093     QCOMPARE(o->property("test1").toBool(), true);
6094     QCOMPARE(o->property("test2").toBool(), true);
6095     QCOMPARE(o->property("test3").toBool(), true);
6096
6097     delete o;
6098     }
6099 }
6100
6101 void tst_qqmlecmascript::signalHandlers()
6102 {
6103     QQmlComponent component(&engine, testFileUrl("signalHandlers.qml"));
6104     QObject *o = component.create();
6105     QVERIFY(o != 0);
6106
6107     QVERIFY(o->property("count").toInt() == 0);
6108     QMetaObject::invokeMethod(o, "testSignalCall");
6109     QCOMPARE(o->property("count").toInt(), 1);
6110
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"));
6114
6115     QVERIFY(o->property("funcCount").toInt() == 0);
6116     QMetaObject::invokeMethod(o, "testSignalConnection");
6117     QCOMPARE(o->property("funcCount").toInt(), 1);
6118
6119     QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
6120     QCOMPARE(o->property("funcCount").toInt(), 2);
6121
6122     QMetaObject::invokeMethod(o, "testSignalDefined");
6123     QCOMPARE(o->property("definedResult").toBool(), true);
6124
6125     QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
6126     QCOMPARE(o->property("definedHandlerResult").toBool(), true);
6127
6128     delete o;
6129 }
6130
6131 void tst_qqmlecmascript::qtbug_10696()
6132 {
6133     QQmlComponent component(&engine, testFileUrl("qtbug_10696.qml"));
6134     QObject *o = component.create();
6135     QVERIFY(o != 0);
6136     delete o;
6137 }
6138
6139 void tst_qqmlecmascript::qtbug_11606()
6140 {
6141     QQmlComponent component(&engine, testFileUrl("qtbug_11606.qml"));
6142     QObject *o = component.create();
6143     QVERIFY(o != 0);
6144     QCOMPARE(o->property("test").toBool(), true);
6145     delete o;
6146 }
6147
6148 void tst_qqmlecmascript::qtbug_11600()
6149 {
6150     QQmlComponent component(&engine, testFileUrl("qtbug_11600.qml"));
6151     QObject *o = component.create();
6152     QVERIFY(o != 0);
6153     QCOMPARE(o->property("test").toBool(), true);
6154     delete o;
6155 }
6156
6157 void tst_qqmlecmascript::qtbug_21864()
6158 {
6159     QQmlComponent component(&engine, testFileUrl("qtbug_21864.qml"));
6160     QObject *o = component.create();
6161     QVERIFY(o != 0);
6162     QCOMPARE(o->property("test").toBool(), true);
6163     delete o;
6164 }
6165
6166 void tst_qqmlecmascript::rewriteMultiLineStrings()
6167 {
6168     {
6169         // QTBUG-23387
6170         QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml"));
6171         QObject *o = component.create();
6172         QVERIFY(o != 0);
6173         QTRY_COMPARE(o->property("test").toBool(), true);
6174         delete o;
6175     }
6176
6177     {
6178         QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings_crlf.1.qml"));
6179         QObject *o = component.create();
6180         QVERIFY(o != 0);
6181         delete o;
6182     }
6183 }
6184
6185 void tst_qqmlecmascript::qobjectConnectionListExceptionHandling()
6186 {
6187     // QTBUG-23375
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();
6194     QVERIFY(o != 0);
6195     QCOMPARE(o->property("test").toBool(), true);
6196     delete o;
6197 }
6198
6199 // Reading and writing non-scriptable properties should fail
6200 void tst_qqmlecmascript::nonscriptable()
6201 {
6202     QQmlComponent component(&engine, testFileUrl("nonscriptable.qml"));
6203     QObject *o = component.create();
6204     QVERIFY(o != 0);
6205     QCOMPARE(o->property("readOk").toBool(), true);
6206     QCOMPARE(o->property("writeOk").toBool(), true);
6207     delete o;
6208 }
6209
6210 // deleteLater() should not be callable from QML
6211 void tst_qqmlecmascript::deleteLater()
6212 {
6213     QQmlComponent component(&engine, testFileUrl("deleteLater.qml"));
6214     QObject *o = component.create();
6215     QVERIFY(o != 0);
6216     QCOMPARE(o->property("test").toBool(), true);
6217     delete o;
6218 }
6219
6220 // objectNameChanged() should be usable from QML
6221 void tst_qqmlecmascript::objectNameChangedSignal()
6222 {
6223     QQmlComponent component(&engine, testFileUrl("objectNameChangedSignal.qml"));
6224     QObject *o = component.create();
6225     QVERIFY(o != 0);
6226     QCOMPARE(o->property("test").toBool(), false);
6227     o->setObjectName("obj");
6228     QCOMPARE(o->property("test").toBool(), true);
6229     delete o;
6230 }
6231
6232 // destroyed() should not be usable from QML
6233 void tst_qqmlecmascript::destroyedSignal()
6234 {
6235     QQmlComponent component(&engine, testFileUrl("destroyedSignal.qml"));
6236     QVERIFY(component.isError());
6237
6238     QString expectedErrorString = component.url().toString() + QLatin1String(":5:5: Cannot assign to non-existent property \"onDestroyed\"");
6239     QCOMPARE(component.errors().at(0).toString(), expectedErrorString);
6240 }
6241
6242 void tst_qqmlecmascript::in()
6243 {
6244     QQmlComponent component(&engine, testFileUrl("in.qml"));
6245     QObject *o = component.create();
6246     QVERIFY(o != 0);
6247     QCOMPARE(o->property("test1").toBool(), true);
6248     QCOMPARE(o->property("test2").toBool(), true);
6249     delete o;
6250 }
6251
6252 void tst_qqmlecmascript::typeOf()
6253 {
6254     QQmlComponent component(&engine, testFileUrl("typeOf.qml"));
6255
6256     QObject *o = component.create();
6257     QVERIFY(o != 0);
6258
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"));
6268
6269     delete o;
6270 }
6271
6272 void tst_qqmlecmascript::qtbug_24448()
6273 {
6274     QQmlComponent component(&engine, testFileUrl("qtbug_24448.qml"));
6275     QScopedPointer<QObject> o(component.create());
6276     QVERIFY(o != 0);
6277     QVERIFY(o->property("test").toBool());
6278 }
6279
6280 void tst_qqmlecmascript::sharedAttachedObject()
6281 {
6282     QQmlComponent component(&engine, testFileUrl("sharedAttachedObject.qml"));
6283     QObject *o = component.create();
6284     QVERIFY(o != 0);
6285     QCOMPARE(o->property("test1").toBool(), true);
6286     QCOMPARE(o->property("test2").toBool(), true);
6287     delete o;
6288 }
6289
6290 // QTBUG-13999
6291 void tst_qqmlecmascript::objectName()
6292 {
6293     QQmlComponent component(&engine, testFileUrl("objectName.qml"));
6294     QObject *o = component.create();
6295     QVERIFY(o != 0);
6296
6297     QCOMPARE(o->property("test1").toString(), QString("hello"));
6298     QCOMPARE(o->property("test2").toString(), QString("ell"));
6299
6300     o->setObjectName("world");
6301
6302     QCOMPARE(o->property("test1").toString(), QString("world"));
6303     QCOMPARE(o->property("test2").toString(), QString("orl"));
6304
6305     delete o;
6306 }
6307
6308 void tst_qqmlecmascript::writeRemovesBinding()
6309 {
6310     QQmlComponent component(&engine, testFileUrl("writeRemovesBinding.qml"));
6311     QObject *o = component.create();
6312     QVERIFY(o != 0);
6313
6314     QCOMPARE(o->property("test").toBool(), true);
6315
6316     delete o;
6317 }
6318
6319 // Test bindings assigned to alias properties actually assign to the alias' target
6320 void tst_qqmlecmascript::aliasBindingsAssignCorrectly()
6321 {
6322     QQmlComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml"));
6323     QObject *o = component.create();
6324     QVERIFY(o != 0);
6325
6326     QCOMPARE(o->property("test").toBool(), true);
6327
6328     delete o;
6329 }
6330
6331 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
6332 void tst_qqmlecmascript::aliasBindingsOverrideTarget()
6333 {
6334     { 
6335     QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml"));
6336     QObject *o = component.create();
6337     QVERIFY(o != 0);
6338
6339     QCOMPARE(o->property("test").toBool(), true);
6340
6341     delete o;
6342     }
6343
6344     {
6345     QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml"));
6346     QObject *o = component.create();
6347     QVERIFY(o != 0);
6348
6349     QCOMPARE(o->property("test").toBool(), true);
6350
6351     delete o;
6352     }
6353
6354     {
6355     QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml"));
6356     QObject *o = component.create();
6357     QVERIFY(o != 0);
6358
6359     QCOMPARE(o->property("test").toBool(), true);
6360
6361     delete o;
6362     }
6363 }
6364
6365 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
6366 void tst_qqmlecmascript::aliasWritesOverrideBindings()
6367 {
6368     {
6369     QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml"));
6370     QObject *o = component.create();
6371     QVERIFY(o != 0);
6372
6373     QCOMPARE(o->property("test").toBool(), true);
6374
6375     delete o;
6376     }
6377
6378     {
6379     QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml"));
6380     QObject *o = component.create();
6381     QVERIFY(o != 0);
6382
6383     QCOMPARE(o->property("test").toBool(), true);
6384
6385     delete o;
6386     }
6387
6388     {
6389     QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml"));
6390     QObject *o = component.create();
6391     QVERIFY(o != 0);
6392
6393     QCOMPARE(o->property("test").toBool(), true);
6394
6395     delete o;
6396     }
6397 }
6398
6399 // Allow an alais to a composite element
6400 // QTBUG-20200
6401 void tst_qqmlecmascript::aliasToCompositeElement()
6402 {
6403     QQmlComponent component(&engine, testFileUrl("aliasToCompositeElement.qml"));
6404
6405     QObject *object = component.create();
6406     QVERIFY(object != 0);
6407
6408     delete object;
6409 }
6410
6411 void tst_qqmlecmascript::qtbug_20344()
6412 {
6413     QQmlComponent component(&engine, testFileUrl("qtbug_20344.qml"));
6414
6415     QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
6416     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6417
6418     QObject *object = component.create();
6419     QVERIFY(object != 0);
6420
6421     delete object;
6422 }
6423
6424 void tst_qqmlecmascript::revisionErrors()
6425 {
6426     {
6427         QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml"));
6428         QString url = component.url().toString();
6429
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";
6433
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);
6439         delete object;
6440     }
6441     {
6442         QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml"));
6443         QString url = component.url().toString();
6444
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";
6453
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);
6461         delete object;
6462     }
6463     {
6464         QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml"));
6465         QString url = component.url().toString();
6466
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);
6477         delete object;
6478     }
6479 }
6480
6481 void tst_qqmlecmascript::revision()
6482 {
6483     {
6484         QQmlComponent component(&engine, testFileUrl("metaobjectRevision.qml"));
6485         QString url = component.url().toString();
6486
6487         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6488         QVERIFY(object != 0);
6489         delete object;
6490     }
6491     {
6492         QQmlComponent component(&engine, testFileUrl("metaobjectRevision2.qml"));
6493         QString url = component.url().toString();
6494
6495         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6496         QVERIFY(object != 0);
6497         delete object;
6498     }
6499     {
6500         QQmlComponent component(&engine, testFileUrl("metaobjectRevision3.qml"));
6501         QString url = component.url().toString();
6502
6503         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6504         QVERIFY(object != 0);
6505         delete object;
6506     }
6507     // Test that non-root classes can resolve revisioned methods
6508     {
6509         QQmlComponent component(&engine, testFileUrl("metaobjectRevision4.qml"));
6510
6511         QObject *object = component.create();
6512         QVERIFY(object != 0);
6513         QCOMPARE(object->property("test").toReal(), 11.);
6514         delete object;
6515     }
6516 }
6517
6518 void tst_qqmlecmascript::realToInt()
6519 {
6520     QQmlComponent component(&engine, testFileUrl("realToInt.qml"));
6521     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6522     QVERIFY(object != 0);
6523
6524     QMetaObject::invokeMethod(object, "test1");
6525     QCOMPARE(object->value(), int(4));
6526     QMetaObject::invokeMethod(object, "test2");
6527     QCOMPARE(object->value(), int(8));
6528 }
6529
6530 void tst_qqmlecmascript::urlProperty()
6531 {
6532     {
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);
6541     }
6542 }
6543
6544 void tst_qqmlecmascript::urlPropertyWithEncoding()
6545 {
6546     {
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");
6551         QUrl encoded;
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);
6556     }
6557 }
6558
6559 void tst_qqmlecmascript::urlListPropertyWithEncoding()
6560 {
6561     {
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);
6570         QUrl encoded;
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));
6576         delete object;
6577     }
6578 }
6579
6580 void tst_qqmlecmascript::dynamicString()
6581 {
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!"));
6587 }
6588
6589 void tst_qqmlecmascript::deleteLaterObjectMethodCall()
6590 {
6591     QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml"));
6592     QObject *object = component.create();
6593     QVERIFY(object != 0);
6594 }
6595
6596 void tst_qqmlecmascript::automaticSemicolon()
6597 {
6598     QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml"));
6599     QObject *object = component.create();
6600     QVERIFY(object != 0);
6601 }
6602
6603 void tst_qqmlecmascript::unaryExpression()
6604 {
6605     QQmlComponent component(&engine, testFileUrl("unaryExpression.qml"));
6606     QObject *object = component.create();
6607     QVERIFY(object != 0);
6608 }
6609
6610 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
6611 void tst_qqmlecmascript::doubleEvaluate()
6612 {
6613     QQmlComponent component(&engine, testFileUrl("doubleEvaluate.qml"));
6614     QObject *object = component.create();
6615     QVERIFY(object != 0);
6616     WriteCounter *wc = qobject_cast<WriteCounter *>(object);
6617     QVERIFY(wc != 0);
6618     QCOMPARE(wc->count(), 1);
6619
6620     wc->setProperty("x", 9);
6621
6622     QCOMPARE(wc->count(), 2);
6623
6624     delete object;
6625 }
6626
6627 static QStringList messages;
6628 static void captureMsgHandler(QtMsgType, const QMessageLogContext &, const QString &msg)
6629 {
6630     messages.append(msg);
6631 }
6632
6633 void tst_qqmlecmascript::nonNotifyable()
6634 {
6635     QV4Compiler::enableV4(false);
6636     QQmlComponent component(&engine, testFileUrl("nonNotifyable.qml"));
6637     QV4Compiler::enableV4(true);
6638
6639     QtMessageHandler old = qInstallMessageHandler(captureMsgHandler);
6640     messages.clear();
6641     QObject *object = component.create();
6642     qInstallMessageHandler(old);
6643
6644     QVERIFY(object != 0);
6645
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");
6652
6653     QCOMPARE(messages.length(), 2);
6654     QCOMPARE(messages.at(0), expected1);
6655     QCOMPARE(messages.at(1), expected2);
6656
6657     delete object;
6658 }
6659
6660 void tst_qqmlecmascript::forInLoop()
6661 {
6662     QQmlComponent component(&engine, testFileUrl("forInLoop.qml"));
6663     QObject *object = component.create();
6664     QVERIFY(object != 0);
6665
6666     QMetaObject::invokeMethod(object, "listProperty");
6667
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"));
6673
6674     //TODO: should test for in loop for other objects (such as QObjects) as well.
6675
6676     delete object;
6677 }
6678
6679 // An object the binding depends on is deleted while the binding is still running
6680 void tst_qqmlecmascript::deleteWhileBindingRunning()
6681 {
6682     QQmlComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml"));
6683     QObject *object = component.create();
6684     QVERIFY(object != 0);
6685     delete object;
6686 }
6687
6688 void tst_qqmlecmascript::qtbug_22679()
6689 {
6690     MyQmlObject object;
6691     object.setStringProperty(QLatin1String("Please work correctly"));
6692     engine.rootContext()->setContextProperty("contextProp", &object);
6693
6694     QQmlComponent component(&engine, testFileUrl("qtbug_22679.qml"));
6695     qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6696     QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6697
6698     QObject *o = component.create();
6699     QVERIFY(o != 0);
6700     QCOMPARE(warningsSpy.count(), 0);
6701     delete o;
6702 }
6703
6704 void tst_qqmlecmascript::qtbug_22843_data()
6705 {
6706     QTest::addColumn<bool>("library");
6707
6708     QTest::newRow("without .pragma library") << false;
6709     QTest::newRow("with .pragma library") << true;
6710 }
6711
6712 void tst_qqmlecmascript::qtbug_22843()
6713 {
6714     QFETCH(bool, library);
6715
6716     QString fileName("qtbug_22843");
6717     if (library)
6718         fileName += QLatin1String(".library");
6719     fileName += QLatin1String(".qml");
6720
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'");
6725
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));
6740         delete object;
6741     }
6742 }
6743
6744
6745 void tst_qqmlecmascript::switchStatement()
6746 {
6747     {
6748         QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml"));
6749         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6750         QVERIFY(object != 0);
6751
6752         // `object->value()' is the number of executed statements
6753
6754         object->setStringProperty("A");
6755         QCOMPARE(object->value(), 5);
6756
6757         object->setStringProperty("S");
6758         QCOMPARE(object->value(), 3);
6759
6760         object->setStringProperty("D");
6761         QCOMPARE(object->value(), 3);
6762
6763         object->setStringProperty("F");
6764         QCOMPARE(object->value(), 4);
6765
6766         object->setStringProperty("something else");
6767         QCOMPARE(object->value(), 1);
6768     }
6769
6770     {
6771         QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml"));
6772         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6773         QVERIFY(object != 0);
6774
6775         // `object->value()' is the number of executed statements
6776
6777         object->setStringProperty("A");
6778         QCOMPARE(object->value(), 5);
6779
6780         object->setStringProperty("S");
6781         QCOMPARE(object->value(), 3);
6782
6783         object->setStringProperty("D");
6784         QCOMPARE(object->value(), 3);
6785
6786         object->setStringProperty("F");
6787         QCOMPARE(object->value(), 3);
6788
6789         object->setStringProperty("something else");
6790         QCOMPARE(object->value(), 4);
6791     }
6792
6793     {
6794         QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml"));
6795         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6796         QVERIFY(object != 0);
6797
6798         // `object->value()' is the number of executed statements
6799
6800         object->setStringProperty("A");
6801         QCOMPARE(object->value(), 5);
6802
6803         object->setStringProperty("S");
6804         QCOMPARE(object->value(), 3);
6805
6806         object->setStringProperty("D");
6807         QCOMPARE(object->value(), 3);
6808
6809         object->setStringProperty("F");
6810         QCOMPARE(object->value(), 3);
6811
6812         object->setStringProperty("something else");
6813         QCOMPARE(object->value(), 6);
6814     }
6815
6816     {
6817         QQmlComponent component(&engine, testFileUrl("switchStatement.4.qml"));
6818
6819         QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
6820         QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6821
6822         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6823         QVERIFY(object != 0);
6824
6825         // `object->value()' is the number of executed statements
6826
6827         object->setStringProperty("A");
6828         QCOMPARE(object->value(), 5);
6829
6830         object->setStringProperty("S");
6831         QCOMPARE(object->value(), 3);
6832
6833         object->setStringProperty("D");
6834         QCOMPARE(object->value(), 3);
6835
6836         object->setStringProperty("F");
6837         QCOMPARE(object->value(), 3);
6838
6839         QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6840
6841         object->setStringProperty("something else");
6842     }
6843
6844     {
6845         QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml"));
6846         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6847         QVERIFY(object != 0);
6848
6849         // `object->value()' is the number of executed statements
6850
6851         object->setStringProperty("A");
6852         QCOMPARE(object->value(), 1);
6853
6854         object->setStringProperty("S");
6855         QCOMPARE(object->value(), 1);
6856
6857         object->setStringProperty("D");
6858         QCOMPARE(object->value(), 1);
6859
6860         object->setStringProperty("F");
6861         QCOMPARE(object->value(), 1);
6862
6863         object->setStringProperty("something else");
6864         QCOMPARE(object->value(), 1);
6865     }
6866
6867     {
6868         QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml"));
6869         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6870         QVERIFY(object != 0);
6871
6872         // `object->value()' is the number of executed statements
6873
6874         object->setStringProperty("A");
6875         QCOMPARE(object->value(), 123);
6876
6877         object->setStringProperty("S");
6878         QCOMPARE(object->value(), 123);
6879
6880         object->setStringProperty("D");
6881         QCOMPARE(object->value(), 321);
6882
6883         object->setStringProperty("F");
6884         QCOMPARE(object->value(), 321);
6885
6886         object->setStringProperty("something else");
6887         QCOMPARE(object->value(), 0);
6888     }
6889 }
6890
6891 void tst_qqmlecmascript::withStatement()
6892 {
6893     {
6894         QQmlComponent component(&engine, testFileUrl("withStatement.1.qml"));
6895         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6896         QVERIFY(object != 0);
6897
6898         QCOMPARE(object->value(), 123);
6899     }
6900 }
6901
6902 void tst_qqmlecmascript::tryStatement()
6903 {
6904     {
6905         QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml"));
6906         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6907         QVERIFY(object != 0);
6908
6909         QCOMPARE(object->value(), 123);
6910     }
6911
6912     {
6913         QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml"));
6914         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6915         QVERIFY(object != 0);
6916
6917         QCOMPARE(object->value(), 321);
6918     }
6919
6920     {
6921         QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml"));
6922         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6923         QVERIFY(object != 0);
6924
6925         QCOMPARE(object->value(), 1);
6926     }
6927
6928     {
6929         QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml"));
6930         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6931         QVERIFY(object != 0);
6932
6933         QCOMPARE(object->value(), 1);
6934     }
6935 }
6936
6937 class CppInvokableWithQObjectDerived : public QObject
6938 {
6939     Q_OBJECT
6940 public:
6941     CppInvokableWithQObjectDerived() {}
6942     ~CppInvokableWithQObjectDerived() {}
6943
6944     Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data)
6945     {
6946         MyQmlObject *obj = new MyQmlObject();
6947         obj->setStringProperty(data);
6948         return obj;
6949     }
6950
6951     Q_INVOKABLE QString getStringProperty(MyQmlObject *obj)
6952     {
6953         return obj->stringProperty();
6954     }
6955 };
6956
6957 void tst_qqmlecmascript::invokableWithQObjectDerived()
6958 {
6959     CppInvokableWithQObjectDerived invokable;
6960
6961     {
6962     QQmlEngine engine;
6963     engine.rootContext()->setContextProperty("invokable", &invokable);
6964
6965     QQmlComponent component(&engine, testFileUrl("qobjectDerivedArgument.qml"));
6966
6967     QObject *object = component.create();
6968
6969     QVERIFY(object != 0);
6970     QVERIFY(object->property("result").value<bool>() == true);
6971
6972     delete object;
6973     }
6974 }
6975
6976 void tst_qqmlecmascript::realTypePrecision()
6977 {
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);
6988 }
6989
6990 void tst_qqmlecmascript::registeredFlagMethod()
6991 {
6992     QQmlEngine engine;
6993     QQmlComponent component(&engine, testFileUrl("registeredFlagMethod.qml"));
6994     MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6995     QVERIFY(object != 0);
6996
6997     QCOMPARE(object->buttons(), 0);
6998     emit object->basicSignal();
6999     QCOMPARE(object->buttons(), Qt::RightButton);
7000
7001     delete object;
7002 }
7003
7004 // QTBUG-23138
7005 void tst_qqmlecmascript::replaceBinding()
7006 {
7007     QQmlEngine engine;
7008     QQmlComponent c(&engine, testFileUrl("replaceBinding.qml"));
7009     QObject *obj = c.create();
7010     QVERIFY(obj != 0);
7011
7012     QVERIFY(obj->property("success").toBool());
7013     delete obj;
7014 }
7015
7016 void tst_qqmlecmascript::deleteRootObjectInCreation()
7017 {
7018     {
7019     QQmlEngine engine;
7020     QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.qml"));
7021     QObject *obj = c.create();
7022     QVERIFY(obj != 0);
7023     QVERIFY(obj->property("rootIndestructible").toBool());
7024     QVERIFY(!obj->property("childDestructible").toBool());
7025     QTest::qWait(1);
7026     QVERIFY(obj->property("childDestructible").toBool());
7027     delete obj;
7028     }
7029
7030     {
7031     QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.2.qml"));
7032     QObject *object = c.create();
7033     QVERIFY(object != 0);
7034     QVERIFY(object->property("testConditionsMet").toBool());
7035     delete object;
7036     }
7037 }
7038
7039 void tst_qqmlecmascript::onDestruction()
7040 {
7041     {
7042         // Delete object manually to invoke the associated handlers,
7043         // prior to engine destruction.
7044         QQmlEngine engine;
7045         QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
7046         QObject *obj = c.create();
7047         QVERIFY(obj != 0);
7048         delete obj;
7049     }
7050
7051     {
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.
7056         QQmlEngine engine;
7057         QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
7058         QObject *obj = c.create();
7059         QVERIFY(obj != 0);
7060     }
7061 }
7062
7063 struct EventProcessor : public QObject
7064 {
7065     Q_OBJECT
7066 public:
7067     Q_INVOKABLE void process()
7068     {
7069         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
7070         QCoreApplication::processEvents();
7071     }
7072 };
7073
7074 void tst_qqmlecmascript::bindingSuppression()
7075 {
7076     QQmlEngine engine;
7077     EventProcessor processor;
7078     engine.rootContext()->setContextProperty("pendingEvents", &processor);
7079
7080     transientErrorsMsgCount = 0;
7081     QtMessageHandler old = qInstallMessageHandler(transientErrorsMsgHandler);
7082
7083     QQmlComponent c(&engine, testFileUrl("bindingSuppression.qml"));
7084     QObject *obj = c.create();
7085     QVERIFY(obj != 0);
7086     delete obj;
7087
7088     qInstallMessageHandler(old);
7089     QCOMPARE(transientErrorsMsgCount, 0);
7090 }
7091
7092 void tst_qqmlecmascript::signalEmitted()
7093 {
7094     {
7095         // calling destroy on the sibling.
7096         QQmlEngine engine;
7097         QQmlComponent c(&engine, testFileUrl("signalEmitted.2.qml"));
7098         QObject *obj = c.create();
7099         QVERIFY(obj != 0);
7100         QTRY_VERIFY(obj->property("success").toBool());
7101         delete obj;
7102     }
7103
7104     {
7105         // allowing gc to clean up the sibling.
7106         QQmlEngine engine;
7107         QQmlComponent c(&engine, testFileUrl("signalEmitted.3.qml"));
7108         QObject *obj = c.create();
7109         QVERIFY(obj != 0);
7110         gc(engine); // should collect c1.
7111         QTRY_VERIFY(obj->property("success").toBool());
7112         delete obj;
7113     }
7114
7115     {
7116         // allowing gc to clean up the sibling after manually destroying target.
7117         QQmlEngine engine;
7118         QQmlComponent c(&engine, testFileUrl("signalEmitted.4.qml"));
7119         QObject *obj = c.create();
7120         QVERIFY(obj != 0);
7121         gc(engine); // should collect c1.
7122         QMetaObject::invokeMethod(obj, "destroyC2");
7123         QTRY_VERIFY(obj->property("success").toBool()); // handles events (incl. delete later).
7124         delete obj;
7125     }
7126 }
7127
7128 // QTBUG-25647
7129 void tst_qqmlecmascript::threadSignal()
7130 {
7131     {
7132     QQmlComponent c(&engine, testFileUrl("threadSignal.qml"));
7133     QObject *object = c.create();
7134     QVERIFY(object != 0);
7135     QTRY_VERIFY(object->property("passed").toBool());
7136     delete object;
7137     }
7138     {
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);
7146     delete object;
7147     }
7148 }
7149
7150 // ensure that the qqmldata::destroyed() handler doesn't cause problems
7151 void tst_qqmlecmascript::qqmldataDestroyed()
7152 {
7153     // gc cleans up a qobject, later the qqmldata destroyed handler will run.
7154     {
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();
7164         // shouldn't crash.
7165         delete object;
7166     }
7167
7168     // in this case, the object has CPP ownership, and the gc will
7169     // be triggered during its beginCreate stage.
7170     {
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.
7179         delete object;
7180         // shouldn't have crashed.
7181     }
7182 }
7183
7184 void tst_qqmlecmascript::secondAlias()
7185 {
7186     QQmlComponent c(&engine, testFileUrl("secondAlias.qml"));
7187     QObject *object = c.create();
7188     QVERIFY(object != 0);
7189     QCOMPARE(object->property("test").toInt(), 200);
7190     delete object;
7191 }
7192
7193 // An alias to a var property works
7194 void tst_qqmlecmascript::varAlias()
7195 {
7196     QQmlComponent c(&engine, testFileUrl("varAlias.qml"));
7197     QObject *object = c.create();
7198     QVERIFY(object != 0);
7199     QCOMPARE(object->property("test").toInt(), 192);
7200     delete object;
7201 }
7202
7203 // Used to trigger an assert in the lazy meta object creation stage
7204 void tst_qqmlecmascript::overrideDataAssert()
7205 {
7206     QQmlComponent c(&engine, testFileUrl("overrideDataAssert.qml"));
7207     QObject *object = c.create();
7208     QVERIFY(object != 0);
7209     object->metaObject();
7210     delete object;
7211 }
7212
7213 void tst_qqmlecmascript::fallbackBindings_data()
7214 {
7215     QTest::addColumn<QString>("source");
7216
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";
7225 }
7226
7227 void tst_qqmlecmascript::fallbackBindings()
7228 {
7229     QFETCH(QString, source);
7230
7231     QQmlComponent component(&engine, testFileUrl(source));
7232     QScopedPointer<QObject> object(component.create());
7233     QVERIFY(object != 0);
7234
7235     QCOMPARE(object->property("success").toBool(), true);
7236 }
7237
7238 void tst_qqmlecmascript::propertyOverride()
7239 {
7240     QQmlComponent component(&engine, testFileUrl("propertyOverride.qml"));
7241     QScopedPointer<QObject> object(component.create());
7242     QVERIFY(object != 0);
7243
7244     QCOMPARE(object->property("success").toBool(), true);
7245 }
7246
7247 void tst_qqmlecmascript::sequenceSort_data()
7248 {
7249     QTest::addColumn<QString>("function");
7250     QTest::addColumn<bool>("useComparer");
7251
7252     QTest::newRow("qtbug_25269") << "test_qtbug_25269" << false;
7253
7254     const char *types[] = { "alphabet", "numbers", "reals" };
7255     const char *sort[] = { "insertionSort", "quickSort" };
7256
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;
7265             }
7266         }
7267     }
7268 }
7269
7270 void tst_qqmlecmascript::sequenceSort()
7271 {
7272     QFETCH(QString, function);
7273     QFETCH(bool, useComparer);
7274
7275     QQmlComponent component(&engine, testFileUrl("sequenceSort.qml"));
7276
7277     QObject *object = component.create();
7278     if (object == 0)
7279         qDebug() << component.errorString();
7280     QVERIFY(object != 0);
7281
7282     QVariant q;
7283     QMetaObject::invokeMethod(object, function.toAscii().constData(), Q_RETURN_ARG(QVariant, q), Q_ARG(QVariant, useComparer));
7284     QVERIFY(q.toBool() == true);
7285
7286     delete object;
7287 }
7288
7289 void tst_qqmlecmascript::concatenatedStringPropertyAccess()
7290 {
7291     QQmlComponent component(&engine, testFileUrl("concatenatedStringPropertyAccess.qml"));
7292     QObject *object = component.create();
7293     QVERIFY(object);
7294     QVERIFY(object->property("success").toBool());
7295     delete object;
7296 }
7297
7298 void tst_qqmlecmascript::jsOwnedObjectsDeletedOnEngineDestroy()
7299 {
7300     QQmlEngine *myEngine = new QQmlEngine;
7301
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);
7310
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);
7314
7315     myEngine->rootContext()->setContextProperty("deleteObject", &deleteObject);
7316     QQmlComponent component(myEngine, testFileUrl("jsOwnedObjectsDeletedOnEngineDestroy.qml"));
7317     QObject *object = component.create();
7318     QVERIFY(object);
7319
7320     // Destroying the engine should delete all JS owned QObjects
7321     QSignalSpy spy1(object1, SIGNAL(destroyed()));
7322     QSignalSpy spy2(object2, SIGNAL(destroyed()));
7323     delete myEngine;
7324     QCOMPARE(spy1.count(), 1);
7325     QCOMPARE(spy2.count(), 1);
7326
7327     delete object;
7328 }
7329
7330 QTEST_MAIN(tst_qqmlecmascript)
7331
7332 #include "tst_qqmlecmascript.moc"