6fa6c3b49e0abecb95bc5d039bb1af371d7e54e1
[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 bindingLoop();
83     void basicExpressions();
84     void basicExpressions_data();
85     void arrayExpressions();
86     void contextPropertiesTriggerReeval();
87     void objectPropertiesTriggerReeval();
88     void deferredProperties();
89     void deferredPropertiesErrors();
90     void extensionObjects();
91     void overrideExtensionProperties();
92     void attachedProperties();
93     void enums();
94     void valueTypeFunctions();
95     void constantsOverrideBindings();
96     void outerBindingOverridesInnerBinding();
97     void aliasPropertyAndBinding();
98     void aliasPropertyReset();
99     void nonExistentAttachedObject();
100     void scope();
101     void importScope();
102     void signalParameterTypes();
103     void objectsCompareAsEqual();
104     void componentCreation_data();
105     void componentCreation();
106     void dynamicCreation_data();
107     void dynamicCreation();
108     void dynamicDestruction();
109     void objectToString();
110     void objectHasOwnProperty();
111     void selfDeletingBinding();
112     void extendedObjectPropertyLookup();
113     void extendedObjectPropertyLookup2();
114     void scriptErrors();
115     void functionErrors();
116     void propertyAssignmentErrors();
117     void signalTriggeredBindings();
118     void listProperties();
119     void exceptionClearsOnReeval();
120     void exceptionSlotProducesWarning();
121     void exceptionBindingProducesWarning();
122     void compileInvalidBinding();
123     void transientErrors();
124     void shutdownErrors();
125     void compositePropertyType();
126     void jsObject();
127     void undefinedResetsProperty();
128     void listToVariant();
129     void listAssignment();
130     void multiEngineObject();
131     void deletedObject();
132     void attachedPropertyScope();
133     void scriptConnect();
134     void scriptDisconnect();
135     void ownership();
136     void cppOwnershipReturnValue();
137     void ownershipCustomReturnValue();
138     void ownershipRootObject();
139     void ownershipConsistency();
140     void ownershipQmlIncubated();
141     void qlistqobjectMethods();
142     void strictlyEquals();
143     void compiled();
144     void numberAssignment();
145     void propertySplicing();
146     void signalWithUnknownTypes();
147     void signalWithJSValueInVariant_data();
148     void signalWithJSValueInVariant();
149     void signalWithJSValueInVariant_twoEngines_data();
150     void signalWithJSValueInVariant_twoEngines();
151     void signalWithQJSValue_data();
152     void signalWithQJSValue();
153     void singletonType_data();
154     void singletonType();
155     void singletonTypeImportOrder();
156     void singletonTypeResolution();
157     void singletonTypeConflicts1();
158     void singletonTypeConflicts2();
159     void singletonTypeConflicts3();
160     void importScripts_data();
161     void importScripts();
162     void scarceResources();
163     void scarceResources_data();
164     void scarceResources_other();
165     void propertyChangeSlots();
166     void propertyVar_data();
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
285 private:
286     static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
287     QQmlEngine engine;
288 };
289
290 void tst_qqmlecmascript::initTestCase()
291 {
292     QQmlDataTest::initTestCase();
293     registerTypes();
294
295     QString dataDir(dataDirectory() + QLatin1Char('/') + QLatin1String("lib"));
296     engine.addImportPath(dataDir);
297 }
298
299 void tst_qqmlecmascript::assignBasicTypes()
300 {
301     {
302     QQmlComponent component(&engine, testFileUrl("assignBasicTypes.qml"));
303     MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
304     QVERIFY(object != 0);
305     QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
306     QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
307     QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue);
308     QCOMPARE(object->stringProperty(), QString("Hello World!"));
309     QCOMPARE(object->uintProperty(), uint(10));
310     QCOMPARE(object->intProperty(), -19);
311     QCOMPARE((float)object->realProperty(), float(23.2));
312     QCOMPARE((float)object->doubleProperty(), float(-19.75));
313     QCOMPARE((float)object->floatProperty(), float(8.5));
314     QCOMPARE(object->colorProperty(), QColor("red"));
315     QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
316     QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
317     QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
318     QCOMPARE(object->pointProperty(), QPoint(99,13));
319     QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
320     QCOMPARE(object->sizeProperty(), QSize(99, 13));
321     QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
322     QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
323     QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
324     QCOMPARE(object->boolProperty(), true);
325     QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
326     QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
327     QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
328     delete object;
329     }
330     {
331     QQmlComponent component(&engine, testFileUrl("assignBasicTypes.2.qml"));
332     MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
333     QVERIFY(object != 0);
334     QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
335     QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
336     QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue);
337     QCOMPARE(object->stringProperty(), QString("Hello World!"));
338     QCOMPARE(object->uintProperty(), uint(10));
339     QCOMPARE(object->intProperty(), -19);
340     QCOMPARE((float)object->realProperty(), float(23.2));
341     QCOMPARE((float)object->doubleProperty(), float(-19.75));
342     QCOMPARE((float)object->floatProperty(), float(8.5));
343     QCOMPARE(object->colorProperty(), QColor("red"));
344     QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
345     QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
346     QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
347     QCOMPARE(object->pointProperty(), QPoint(99,13));
348     QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
349     QCOMPARE(object->sizeProperty(), QSize(99, 13));
350     QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
351     QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
352     QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
353     QCOMPARE(object->boolProperty(), true);
354     QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
355     QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2));
356     QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
357     delete object;
358     }
359 }
360
361 void tst_qqmlecmascript::assignDate_data()
362 {
363     QTest::addColumn<QUrl>("source");
364
365     QTest::newRow("Component.onComplete JS Parse") << testFileUrl("assignDate.qml");
366     QTest::newRow("Component.onComplete JS") << testFileUrl("assignDate.1.qml");
367     QTest::newRow("Binding JS") << testFileUrl("assignDate.2.qml");
368     QTest::newRow("Binding UTC") << testFileUrl("assignDate.3.qml");
369     QTest::newRow("Binding JS UTC") << testFileUrl("assignDate.4.qml");
370     QTest::newRow("Binding UTC+2") << testFileUrl("assignDate.5.qml");
371     QTest::newRow("Binding JS UTC+2 ") << testFileUrl("assignDate.6.qml");
372 }
373
374 void tst_qqmlecmascript::assignDate()
375 {
376     QFETCH(QUrl, source);
377
378     QQmlComponent component(&engine, source);
379     QScopedPointer<QObject> obj(component.create());
380     MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
381     QVERIFY(object != 0);
382
383     // Dates received from JS are automatically converted to local time
384     QDate expectedDate(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 0), Qt::UTC).toLocalTime().date());
385     QDateTime expectedDateTime(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 1), Qt::UTC).toLocalTime());
386     QDateTime expectedDateTime2(QDateTime(QDate(2009, 5, 12), QTime(23, 59, 59), Qt::UTC).toLocalTime());
387
388     QCOMPARE(object->dateProperty(), expectedDate);
389     QCOMPARE(object->dateTimeProperty(), expectedDateTime);
390     QCOMPARE(object->dateTimeProperty2(), expectedDateTime2);
391     QCOMPARE(object->boolProperty(), true);
392 }
393
394 void tst_qqmlecmascript::exportDate_data()
395 {
396     QTest::addColumn<QUrl>("source");
397     QTest::addColumn<QDateTime>("datetime");
398
399     // Verify that we can export datetime information to QML and that consumers can access
400     // the data correctly provided they know the TZ info associated with the value
401
402     const QDate date(2009, 5, 12);
403     const QTime early(0, 0, 1);
404     const QTime late(23, 59, 59);
405     const int offset(((11 * 60) + 30) * 60);
406
407     QTest::newRow("Localtime early") << testFileUrl("exportDate.qml") << QDateTime(date, early, Qt::LocalTime);
408     QTest::newRow("Localtime late") << testFileUrl("exportDate.2.qml") << QDateTime(date, late, Qt::LocalTime);
409     QTest::newRow("UTC early") << testFileUrl("exportDate.3.qml") << QDateTime(date, early, Qt::UTC);
410     QTest::newRow("UTC late") << testFileUrl("exportDate.4.qml") << QDateTime(date, late, Qt::UTC);
411     {
412         QDateTime dt(date, early, Qt::OffsetFromUTC);
413         dt.setUtcOffset(offset);
414         QTest::newRow("+11:30 early") << testFileUrl("exportDate.5.qml") << dt;
415     }
416     {
417         QDateTime dt(date, late, Qt::OffsetFromUTC);
418         dt.setUtcOffset(offset);
419         QTest::newRow("+11:30 late") << testFileUrl("exportDate.6.qml") << dt;
420     }
421     {
422         QDateTime dt(date, early, Qt::OffsetFromUTC);
423         dt.setUtcOffset(-offset);
424         QTest::newRow("-11:30 early") << testFileUrl("exportDate.7.qml") << dt;
425     }
426     {
427         QDateTime dt(date, late, Qt::OffsetFromUTC);
428         dt.setUtcOffset(-offset);
429         QTest::newRow("-11:30 late") << testFileUrl("exportDate.8.qml") << dt;
430     }
431 }
432
433 void tst_qqmlecmascript::exportDate()
434 {
435     QFETCH(QUrl, source);
436     QFETCH(QDateTime, datetime);
437
438     DateTimeExporter exporter(datetime);
439
440     QQmlEngine e;
441     e.rootContext()->setContextProperty("datetimeExporter", &exporter);
442
443     QQmlComponent component(&e, source);
444     QScopedPointer<QObject> obj(component.create());
445     MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data());
446     QVERIFY(object != 0);
447     QCOMPARE(object->boolProperty(), true);
448 }
449
450 void tst_qqmlecmascript::idShortcutInvalidates()
451 {
452     {
453         QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.qml"));
454         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
455         QVERIFY(object != 0);
456         QVERIFY(object->objectProperty() != 0);
457         delete object->objectProperty();
458         QVERIFY(object->objectProperty() == 0);
459         delete object;
460     }
461
462     {
463         QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.1.qml"));
464         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
465         QVERIFY(object != 0);
466         QVERIFY(object->objectProperty() != 0);
467         delete object->objectProperty();
468         QVERIFY(object->objectProperty() == 0);
469         delete object;
470     }
471 }
472
473 void tst_qqmlecmascript::boolPropertiesEvaluateAsBool()
474 {
475     {
476         QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.1.qml"));
477         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
478         QVERIFY(object != 0);
479         QCOMPARE(object->stringProperty(), QLatin1String("pass"));
480         delete object;
481     }
482     {
483         QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.2.qml"));
484         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
485         QVERIFY(object != 0);
486         QCOMPARE(object->stringProperty(), QLatin1String("pass"));
487         delete object;
488     }
489 }
490
491 void tst_qqmlecmascript::signalAssignment()
492 {
493     {
494         QQmlComponent component(&engine, testFileUrl("signalAssignment.1.qml"));
495         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
496         QVERIFY(object != 0);
497         QCOMPARE(object->string(), QString());
498         emit object->basicSignal();
499         QCOMPARE(object->string(), QString("pass"));
500         delete object;
501     }
502
503     {
504         QQmlComponent component(&engine, testFileUrl("signalAssignment.2.qml"));
505         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
506         QVERIFY(object != 0);
507         QCOMPARE(object->string(), QString());
508         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
509         QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
510         delete object;
511     }
512
513     {
514         QQmlComponent component(&engine, testFileUrl("signalAssignment.3.qml"));
515         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
516         QVERIFY(object != 0);
517         QCOMPARE(object->string(), QString());
518         emit object->unnamedArgumentSignal(19, 10.25, "Hello world!");
519         QCOMPARE(object->string(), QString("pass 19 Hello world!"));
520         delete object;
521     }
522
523     {
524         QQmlComponent component(&engine, testFileUrl("signalAssignment.4.qml"));
525         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
526         QVERIFY(object != 0);
527         QCOMPARE(object->string(), QString());
528         emit object->signalWithGlobalName(19);
529         QCOMPARE(object->string(), QString("pass 5"));
530         delete object;
531     }
532 }
533
534 void tst_qqmlecmascript::methods()
535 {
536     {
537         QQmlComponent component(&engine, testFileUrl("methods.1.qml"));
538         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
539         QVERIFY(object != 0);
540         QCOMPARE(object->methodCalled(), false);
541         QCOMPARE(object->methodIntCalled(), false);
542         emit object->basicSignal();
543         QCOMPARE(object->methodCalled(), true);
544         QCOMPARE(object->methodIntCalled(), false);
545         delete object;
546     }
547
548     {
549         QQmlComponent component(&engine, testFileUrl("methods.2.qml"));
550         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
551         QVERIFY(object != 0);
552         QCOMPARE(object->methodCalled(), false);
553         QCOMPARE(object->methodIntCalled(), false);
554         emit object->basicSignal();
555         QCOMPARE(object->methodCalled(), false);
556         QCOMPARE(object->methodIntCalled(), true);
557         delete object;
558     }
559
560     {
561         QQmlComponent component(&engine, testFileUrl("methods.3.qml"));
562         QObject *object = component.create();
563         QVERIFY(object != 0);
564         QCOMPARE(object->property("test").toInt(), 19);
565         delete object;
566     }
567
568     {
569         QQmlComponent component(&engine, testFileUrl("methods.4.qml"));
570         QObject *object = component.create();
571         QVERIFY(object != 0);
572         QCOMPARE(object->property("test").toInt(), 19);
573         QCOMPARE(object->property("test2").toInt(), 17);
574         QCOMPARE(object->property("test3").toInt(), 16);
575         delete object;
576     }
577
578     {
579         QQmlComponent component(&engine, testFileUrl("methods.5.qml"));
580         QObject *object = component.create();
581         QVERIFY(object != 0);
582         QCOMPARE(object->property("test").toInt(), 9);
583         delete object;
584     }
585 }
586
587 void tst_qqmlecmascript::bindingLoop()
588 {
589     QQmlComponent component(&engine, testFileUrl("bindingLoop.qml"));
590     QString warning = component.url().toString() + ":5:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\"";
591     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
592     QObject *object = component.create();
593     QVERIFY(object != 0);
594     delete object;
595 }
596
597 void tst_qqmlecmascript::basicExpressions_data()
598 {
599     QTest::addColumn<QString>("expression");
600     QTest::addColumn<QVariant>("result");
601     QTest::addColumn<bool>("nest");
602
603     QTest::newRow("Syntax error (self test)") << "{console.log({'a':1'}.a)}" << QVariant() << false;
604     QTest::newRow("Context property") << "a" << QVariant(1944) << false;
605     QTest::newRow("Context property") << "a" << QVariant(1944) << true;
606     QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << false;
607     QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << true;
608     QTest::newRow("Overridden context property") << "b" << QVariant("Milk") << false;
609     QTest::newRow("Overridden context property") << "b" << QVariant("Cow") << true;
610     QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << false;
611     QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << true;
612     QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object2") << false;
613     QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object3") << true;
614     QTest::newRow("Default object property") << "horseLegs" << QVariant(4) << false;
615     QTest::newRow("Default object property") << "antLegs" << QVariant(6) << false;
616     QTest::newRow("Default object property") << "emuLegs" << QVariant(2) << false;
617     QTest::newRow("Nested default object property") << "horseLegs" << QVariant(4) << true;
618     QTest::newRow("Nested default object property") << "antLegs" << QVariant(7) << true;
619     QTest::newRow("Nested default object property") << "emuLegs" << QVariant(2) << true;
620     QTest::newRow("Nested default object property") << "humanLegs" << QVariant(2) << true;
621     QTest::newRow("Context property override default object property") << "millipedeLegs" << QVariant(100) << true;
622 }
623
624 void tst_qqmlecmascript::basicExpressions()
625 {
626     QFETCH(QString, expression);
627     QFETCH(QVariant, result);
628     QFETCH(bool, nest);
629
630     MyQmlObject object1;
631     MyQmlObject object2;
632     MyQmlObject object3;
633     MyDefaultObject1 default1;
634     MyDefaultObject3 default3;
635     object1.setStringProperty("Object1");
636     object2.setStringProperty("Object2");
637     object3.setStringProperty("Object3");
638
639     QQmlContext context(engine.rootContext());
640     QQmlContext nestedContext(&context);
641
642     context.setContextObject(&default1);
643     context.setContextProperty("a", QVariant(1944));
644     context.setContextProperty("b", QVariant("Milk"));
645     context.setContextProperty("object", &object1);
646     context.setContextProperty("objectOverride", &object2);
647     nestedContext.setContextObject(&default3);
648     nestedContext.setContextProperty("b", QVariant("Cow"));
649     nestedContext.setContextProperty("objectOverride", &object3);
650     nestedContext.setContextProperty("millipedeLegs", QVariant(100));
651
652     MyExpression expr(nest?&nestedContext:&context, expression);
653     QCOMPARE(expr.evaluate(), result);
654 }
655
656 void tst_qqmlecmascript::arrayExpressions()
657 {
658     QObject obj1;
659     QObject obj2;
660     QObject obj3;
661
662     QQmlContext context(engine.rootContext());
663     context.setContextProperty("a", &obj1);
664     context.setContextProperty("b", &obj2);
665     context.setContextProperty("c", &obj3);
666
667     MyExpression expr(&context, "[a, b, c, 10]");
668     QVariant result = expr.evaluate();
669     QCOMPARE(result.userType(), qMetaTypeId<QVariantList>());
670     QVariantList list = qvariant_cast<QVariantList>(result);
671     QCOMPARE(list.count(), 4);
672     QCOMPARE(list.at(0).value<QObject*>(), &obj1);
673     QCOMPARE(list.at(1).value<QObject*>(), &obj2);
674     QCOMPARE(list.at(2).value<QObject*>(), &obj3);
675     QCOMPARE(list.at(3).value<int>(), 10);
676 }
677
678 // Tests that modifying a context property will reevaluate expressions
679 void tst_qqmlecmascript::contextPropertiesTriggerReeval()
680 {
681     QQmlContext context(engine.rootContext());
682     MyQmlObject object1;
683     MyQmlObject object2;
684     MyQmlObject *object3 = new MyQmlObject;
685
686     object1.setStringProperty("Hello");
687     object2.setStringProperty("World");
688
689     context.setContextProperty("testProp", QVariant(1));
690     context.setContextProperty("testObj", &object1);
691     context.setContextProperty("testObj2", object3);
692
693     { 
694         MyExpression expr(&context, "testProp + 1");
695         QCOMPARE(expr.changed, false);
696         QCOMPARE(expr.evaluate(), QVariant(2));
697
698         context.setContextProperty("testProp", QVariant(2));
699         QCOMPARE(expr.changed, true);
700         QCOMPARE(expr.evaluate(), QVariant(3));
701     }
702
703     { 
704         MyExpression expr(&context, "testProp + testProp + testProp");
705         QCOMPARE(expr.changed, false);
706         QCOMPARE(expr.evaluate(), QVariant(6));
707
708         context.setContextProperty("testProp", QVariant(4));
709         QCOMPARE(expr.changed, true);
710         QCOMPARE(expr.evaluate(), QVariant(12));
711     }
712
713     { 
714         MyExpression expr(&context, "testObj.stringProperty");
715         QCOMPARE(expr.changed, false);
716         QCOMPARE(expr.evaluate(), QVariant("Hello"));
717
718         context.setContextProperty("testObj", &object2);
719         QCOMPARE(expr.changed, true);
720         QCOMPARE(expr.evaluate(), QVariant("World"));
721     }
722
723     { 
724         MyExpression expr(&context, "testObj.stringProperty /**/");
725         QCOMPARE(expr.changed, false);
726         QCOMPARE(expr.evaluate(), QVariant("World"));
727
728         context.setContextProperty("testObj", &object1);
729         QCOMPARE(expr.changed, true);
730         QCOMPARE(expr.evaluate(), QVariant("Hello"));
731     }
732
733     { 
734         MyExpression expr(&context, "testObj2");
735         QCOMPARE(expr.changed, false);
736         QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3));
737     }
738
739     delete object3;
740 }
741
742 void tst_qqmlecmascript::objectPropertiesTriggerReeval()
743 {
744     QQmlContext context(engine.rootContext());
745     MyQmlObject object1;
746     MyQmlObject object2;
747     MyQmlObject object3;
748     context.setContextProperty("testObj", &object1);
749
750     object1.setStringProperty(QLatin1String("Hello"));
751     object2.setStringProperty(QLatin1String("Dog"));
752     object3.setStringProperty(QLatin1String("Cat"));
753
754     { 
755         MyExpression expr(&context, "testObj.stringProperty");
756         QCOMPARE(expr.changed, false);
757         QCOMPARE(expr.evaluate(), QVariant("Hello"));
758
759         object1.setStringProperty(QLatin1String("World"));
760         QCOMPARE(expr.changed, true);
761         QCOMPARE(expr.evaluate(), QVariant("World"));
762     }
763
764     { 
765         MyExpression expr(&context, "testObj.objectProperty.stringProperty");
766         QCOMPARE(expr.changed, false);
767         QCOMPARE(expr.evaluate(), QVariant());
768
769         object1.setObjectProperty(&object2);
770         QCOMPARE(expr.changed, true);
771         expr.changed = false;
772         QCOMPARE(expr.evaluate(), QVariant("Dog"));
773
774         object1.setObjectProperty(&object3);
775         QCOMPARE(expr.changed, true);
776         expr.changed = false;
777         QCOMPARE(expr.evaluate(), QVariant("Cat"));
778
779         object1.setObjectProperty(0);
780         QCOMPARE(expr.changed, true);
781         expr.changed = false;
782         QCOMPARE(expr.evaluate(), QVariant());
783
784         object1.setObjectProperty(&object3);
785         QCOMPARE(expr.changed, true);
786         expr.changed = false;
787         QCOMPARE(expr.evaluate(), QVariant("Cat"));
788
789         object3.setStringProperty("Donkey");
790         QCOMPARE(expr.changed, true);
791         expr.changed = false;
792         QCOMPARE(expr.evaluate(), QVariant("Donkey"));
793     }
794 }
795
796 void tst_qqmlecmascript::deferredProperties()
797 {
798     QQmlComponent component(&engine, testFileUrl("deferredProperties.qml"));
799     MyDeferredObject *object = 
800         qobject_cast<MyDeferredObject *>(component.create());
801     QVERIFY(object != 0);
802     QCOMPARE(object->value(), 0);
803     QVERIFY(object->objectProperty() == 0);
804     QVERIFY(object->objectProperty2() != 0);
805     qmlExecuteDeferred(object);
806     QCOMPARE(object->value(), 10);
807     QVERIFY(object->objectProperty() != 0);
808     MyQmlObject *qmlObject = 
809         qobject_cast<MyQmlObject *>(object->objectProperty());
810     QVERIFY(qmlObject != 0);
811     QCOMPARE(qmlObject->value(), 10);
812     object->setValue(19);
813     QCOMPARE(qmlObject->value(), 19);
814
815     delete object;
816 }
817
818 // Check errors on deferred properties are correctly emitted
819 void tst_qqmlecmascript::deferredPropertiesErrors()
820 {
821     QQmlComponent component(&engine, testFileUrl("deferredPropertiesErrors.qml"));
822     MyDeferredObject *object = 
823         qobject_cast<MyDeferredObject *>(component.create());
824     QVERIFY(object != 0);
825     QCOMPARE(object->value(), 0);
826     QVERIFY(object->objectProperty() == 0);
827     QVERIFY(object->objectProperty2() == 0);
828
829     QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject*";
830     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
831
832     qmlExecuteDeferred(object);
833
834     delete object;
835 }
836
837 void tst_qqmlecmascript::extensionObjects()
838 {
839     QQmlComponent component(&engine, testFileUrl("extensionObjects.qml"));
840     MyExtendedObject *object = 
841         qobject_cast<MyExtendedObject *>(component.create());
842     QVERIFY(object != 0);
843     QCOMPARE(object->baseProperty(), 13);
844     QCOMPARE(object->coreProperty(), 9);
845     object->setProperty("extendedProperty", QVariant(11));
846     object->setProperty("baseExtendedProperty", QVariant(92));
847     QCOMPARE(object->coreProperty(), 11);
848     QCOMPARE(object->baseProperty(), 92);
849
850     MyExtendedObject *nested = qobject_cast<MyExtendedObject*>(qvariant_cast<QObject *>(object->property("nested")));
851     QVERIFY(nested);
852     QCOMPARE(nested->baseProperty(), 13);
853     QCOMPARE(nested->coreProperty(), 9);
854     nested->setProperty("extendedProperty", QVariant(11));
855     nested->setProperty("baseExtendedProperty", QVariant(92));
856     QCOMPARE(nested->coreProperty(), 11);
857     QCOMPARE(nested->baseProperty(), 92);
858
859     delete object;
860 }
861
862 void tst_qqmlecmascript::overrideExtensionProperties()
863 {
864     QQmlComponent component(&engine, testFileUrl("extensionObjectsPropertyOverride.qml"));
865     OverrideDefaultPropertyObject *object =
866         qobject_cast<OverrideDefaultPropertyObject *>(component.create());
867     QVERIFY(object != 0);
868     QVERIFY(object->secondProperty() != 0);
869     QVERIFY(object->firstProperty() == 0);
870
871     delete object;
872 }
873
874 void tst_qqmlecmascript::attachedProperties()
875 {
876     {
877         QQmlComponent component(&engine, testFileUrl("attachedProperty.qml"));
878         QObject *object = component.create();
879         QVERIFY(object != 0);
880         QCOMPARE(object->property("a").toInt(), 19);
881         QCOMPARE(object->property("b").toInt(), 19);
882         QCOMPARE(object->property("c").toInt(), 19);
883         QCOMPARE(object->property("d").toInt(), 19);
884         delete object;
885     }
886
887     {
888         QQmlComponent component(&engine, testFileUrl("attachedProperty.2.qml"));
889         QObject *object = component.create();
890         QVERIFY(object != 0);
891         QCOMPARE(object->property("a").toInt(), 26);
892         QCOMPARE(object->property("b").toInt(), 26);
893         QCOMPARE(object->property("c").toInt(), 26);
894         QCOMPARE(object->property("d").toInt(), 26);
895
896         delete object;
897     }
898
899     {
900         QQmlComponent component(&engine, testFileUrl("writeAttachedProperty.qml"));
901         QObject *object = component.create();
902         QVERIFY(object != 0);
903
904         QMetaObject::invokeMethod(object, "writeValue2");
905
906         MyQmlAttachedObject *attached =
907             qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
908         QVERIFY(attached != 0);
909
910         QCOMPARE(attached->value2(), 9);
911         delete object;
912     }
913 }
914
915 void tst_qqmlecmascript::enums()
916 {
917     // Existent enums
918     {
919     QQmlComponent component(&engine, testFileUrl("enums.1.qml"));
920     QObject *object = component.create();
921     QVERIFY(object != 0);
922
923     QCOMPARE(object->property("enumProperty").toInt(), (int)MyQmlObject::EnumValue2);
924     QCOMPARE(object->property("relatedEnumProperty").toInt(), (int)MyEnumContainer::RelatedValue);
925     QCOMPARE(object->property("unrelatedEnumProperty").toInt(), (int)MyEnumContainer::RelatedValue);
926     QCOMPARE(object->property("qtEnumProperty").toInt(), (int)Qt::CaseInsensitive);
927     QCOMPARE(object->property("a").toInt(), 0);
928     QCOMPARE(object->property("b").toInt(), 1);
929     QCOMPARE(object->property("c").toInt(), 2);
930     QCOMPARE(object->property("d").toInt(), 3);
931     QCOMPARE(object->property("e").toInt(), 0);
932     QCOMPARE(object->property("f").toInt(), 1);
933     QCOMPARE(object->property("g").toInt(), 2);
934     QCOMPARE(object->property("h").toInt(), 3);
935     QCOMPARE(object->property("i").toInt(), 19);
936     QCOMPARE(object->property("j").toInt(), 19);
937     QCOMPARE(object->property("k").toInt(), 42);
938     QCOMPARE(object->property("l").toInt(), 333);
939
940     delete object;
941     }
942     // Non-existent enums
943     {
944     QUrl file = testFileUrl("enums.2.qml");
945     QString w1 = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'MyEnum' for property 'MyUnregisteredEnumTypeObject::enumProperty'");
946     QString w2 = QLatin1String("QQmlExpression: Expression ") + testFileUrl("enums.2.qml").toString() + QLatin1String(":9 depends on non-NOTIFYable properties:");
947     QString w3 = QLatin1String("    MyUnregisteredEnumTypeObject::enumProperty");
948     QString w4 = file.toString() + ":7: Unable to assign [undefined] to int";
949     QString w5 = file.toString() + ":8: Unable to assign [undefined] to int";
950     QString w6 = file.toString() + ":9: Unable to assign [undefined] to int";
951     QString w7 = file.toString() + ":13: Unable to assign [undefined] to [unknown property type]";
952     QString w8 = file.toString() + ":31: Unable to assign int to [unknown property type]";
953     QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
954     QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
955     QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
956     QTest::ignoreMessage(QtWarningMsg, qPrintable(w4));
957     QTest::ignoreMessage(QtWarningMsg, qPrintable(w5));
958     QTest::ignoreMessage(QtWarningMsg, qPrintable(w6));
959     QTest::ignoreMessage(QtWarningMsg, qPrintable(w7));
960     QTest::ignoreMessage(QtWarningMsg, qPrintable(w8));
961
962     QQmlComponent component(&engine, testFileUrl("enums.2.qml"));
963     QObject *object = component.create();
964     QVERIFY(object != 0);
965     QCOMPARE(object->property("a").toInt(), 0);
966     QCOMPARE(object->property("b").toInt(), 0);
967     QCOMPARE(object->property("c").toInt(), 0);
968
969     QString w9 = file.toString() + ":18: Error: Cannot assign JavaScript function to [unknown property type]";
970     QTest::ignoreMessage(QtWarningMsg, qPrintable(w9));
971     QMetaObject::invokeMethod(object, "testAssignmentOne");
972
973     QString w10 = file.toString() + ":21: Error: Cannot assign [undefined] to [unknown property type]";
974     QTest::ignoreMessage(QtWarningMsg, qPrintable(w10));
975     QMetaObject::invokeMethod(object, "testAssignmentTwo");
976
977     QString w11 = file.toString() + ":24: Error: Cannot assign [undefined] to [unknown property type]";
978     QTest::ignoreMessage(QtWarningMsg, qPrintable(w11));
979     QMetaObject::invokeMethod(object, "testAssignmentThree");
980
981     QString w12 = file.toString() + ":34: Error: Cannot assign int to an unregistered type";
982     QTest::ignoreMessage(QtWarningMsg, qPrintable(w12));
983     QMetaObject::invokeMethod(object, "testAssignmentFour");
984
985     delete object;
986     }
987     // Enums as literals
988     {
989     QQmlComponent component(&engine, testFileUrl("enums.3.qml"));
990     QObject *object = component.create();
991     QVERIFY(object != 0);
992
993     // check the values are what we expect
994     QCOMPARE(object->property("a").toInt(), 4);
995     QCOMPARE(object->property("b").toInt(), 5);
996     QCOMPARE(object->property("c").toInt(), 9);
997     QCOMPARE(object->property("d").toInt(), 13);
998     QCOMPARE(object->property("e").toInt(), 2);
999     QCOMPARE(object->property("f").toInt(), 3);
1000     QCOMPARE(object->property("h").toInt(), 2);
1001     QCOMPARE(object->property("i").toInt(), 3);
1002     QCOMPARE(object->property("j").toInt(), -1);
1003     QCOMPARE(object->property("k").toInt(), 42);
1004
1005     // count of change signals
1006     QCOMPARE(object->property("ac").toInt(), 0);
1007     QCOMPARE(object->property("bc").toInt(), 0);
1008     QCOMPARE(object->property("cc").toInt(), 0);
1009     QCOMPARE(object->property("dc").toInt(), 0);
1010     QCOMPARE(object->property("ec").toInt(), 0);
1011     QCOMPARE(object->property("fc").toInt(), 0);
1012     QCOMPARE(object->property("hc").toInt(), 1); // namespace -> binding
1013     QCOMPARE(object->property("ic").toInt(), 1); // namespace -> binding
1014     QCOMPARE(object->property("jc").toInt(), 0);
1015     QCOMPARE(object->property("kc").toInt(), 0);
1016
1017     delete object;
1018     }
1019 }
1020
1021 void tst_qqmlecmascript::valueTypeFunctions()
1022 {
1023     QQmlComponent component(&engine, testFileUrl("valueTypeFunctions.qml"));
1024     MyTypeObject *obj = qobject_cast<MyTypeObject*>(component.create());
1025     QVERIFY(obj != 0);
1026     QCOMPARE(obj->rectProperty(), QRect(0,0,100,100));
1027     QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5));
1028
1029     delete obj;
1030 }
1031
1032 /* 
1033 Tests that writing a constant to a property with a binding on it disables the
1034 binding.
1035 */
1036 void tst_qqmlecmascript::constantsOverrideBindings()
1037 {
1038     // From ECMAScript
1039     {
1040         QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.1.qml"));
1041         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1042         QVERIFY(object != 0);
1043
1044         QCOMPARE(object->property("c2").toInt(), 0);
1045         object->setProperty("c1", QVariant(9));
1046         QCOMPARE(object->property("c2").toInt(), 9);
1047
1048         emit object->basicSignal();
1049
1050         QCOMPARE(object->property("c2").toInt(), 13);
1051         object->setProperty("c1", QVariant(8));
1052         QCOMPARE(object->property("c2").toInt(), 13);
1053
1054         delete object;
1055     }
1056
1057     // During construction
1058     {
1059         QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.2.qml"));
1060         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1061         QVERIFY(object != 0);
1062
1063         QCOMPARE(object->property("c1").toInt(), 0);
1064         QCOMPARE(object->property("c2").toInt(), 10);
1065         object->setProperty("c1", QVariant(9));
1066         QCOMPARE(object->property("c1").toInt(), 9);
1067         QCOMPARE(object->property("c2").toInt(), 10);
1068
1069         delete object;
1070     }
1071
1072 #if 0
1073     // From C++
1074     {
1075         QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.3.qml"));
1076         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1077         QVERIFY(object != 0);
1078
1079         QCOMPARE(object->property("c2").toInt(), 0);
1080         object->setProperty("c1", QVariant(9));
1081         QCOMPARE(object->property("c2").toInt(), 9);
1082
1083         object->setProperty("c2", QVariant(13));
1084         QCOMPARE(object->property("c2").toInt(), 13);
1085         object->setProperty("c1", QVariant(7));
1086         QCOMPARE(object->property("c1").toInt(), 7);
1087         QCOMPARE(object->property("c2").toInt(), 13);
1088
1089         delete object;
1090     }
1091 #endif
1092
1093     // Using an alias
1094     {
1095         QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.4.qml"));
1096         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1097         QVERIFY(object != 0);
1098
1099         QCOMPARE(object->property("c1").toInt(), 0);
1100         QCOMPARE(object->property("c3").toInt(), 10);
1101         object->setProperty("c1", QVariant(9));
1102         QCOMPARE(object->property("c1").toInt(), 9);
1103         QCOMPARE(object->property("c3").toInt(), 10);
1104
1105         delete object;
1106     }
1107 }
1108
1109 /*
1110 Tests that assigning a binding to a property that already has a binding causes
1111 the original binding to be disabled.
1112 */
1113 void tst_qqmlecmascript::outerBindingOverridesInnerBinding()
1114 {
1115     QQmlComponent component(&engine, 
1116                            testFileUrl("outerBindingOverridesInnerBinding.qml"));
1117     MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1118     QVERIFY(object != 0);
1119
1120     QCOMPARE(object->property("c1").toInt(), 0);
1121     QCOMPARE(object->property("c2").toInt(), 0);
1122     QCOMPARE(object->property("c3").toInt(), 0);
1123
1124     object->setProperty("c1", QVariant(9));
1125     QCOMPARE(object->property("c1").toInt(), 9);
1126     QCOMPARE(object->property("c2").toInt(), 0);
1127     QCOMPARE(object->property("c3").toInt(), 0);
1128
1129     object->setProperty("c3", QVariant(8));
1130     QCOMPARE(object->property("c1").toInt(), 9);
1131     QCOMPARE(object->property("c2").toInt(), 8);
1132     QCOMPARE(object->property("c3").toInt(), 8);
1133
1134     delete object;
1135 }
1136
1137 /*
1138 Access a non-existent attached object.  
1139
1140 Tests for a regression where this used to crash.
1141 */
1142 void tst_qqmlecmascript::nonExistentAttachedObject()
1143 {
1144     QQmlComponent component(&engine, testFileUrl("nonExistentAttachedObject.qml"));
1145
1146     QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString";
1147     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
1148
1149     QObject *object = component.create();
1150     QVERIFY(object != 0);
1151
1152     delete object;
1153 }
1154
1155 void tst_qqmlecmascript::scope()
1156 {
1157     {
1158         QQmlComponent component(&engine, testFileUrl("scope.qml"));
1159         QObject *object = component.create();
1160         QVERIFY(object != 0);
1161
1162         QCOMPARE(object->property("test1").toInt(), 1);
1163         QCOMPARE(object->property("test2").toInt(), 2);
1164         QCOMPARE(object->property("test3").toString(), QString("1Test"));
1165         QCOMPARE(object->property("test4").toString(), QString("2Test"));
1166         QCOMPARE(object->property("test5").toInt(), 1);
1167         QCOMPARE(object->property("test6").toInt(), 1);
1168         QCOMPARE(object->property("test7").toInt(), 2);
1169         QCOMPARE(object->property("test8").toInt(), 2);
1170         QCOMPARE(object->property("test9").toInt(), 1);
1171         QCOMPARE(object->property("test10").toInt(), 3);
1172
1173         delete object;
1174     }
1175
1176     {
1177         QQmlComponent component(&engine, testFileUrl("scope.2.qml"));
1178         QObject *object = component.create();
1179         QVERIFY(object != 0);
1180
1181         QCOMPARE(object->property("test1").toInt(), 19);
1182         QCOMPARE(object->property("test2").toInt(), 19);
1183         QCOMPARE(object->property("test3").toInt(), 14);
1184         QCOMPARE(object->property("test4").toInt(), 14);
1185         QCOMPARE(object->property("test5").toInt(), 24);
1186         QCOMPARE(object->property("test6").toInt(), 24);
1187
1188         delete object;
1189     }
1190
1191     {
1192         QQmlComponent component(&engine, testFileUrl("scope.3.qml"));
1193         QObject *object = component.create();
1194         QVERIFY(object != 0);
1195
1196         QCOMPARE(object->property("test1").toBool(), true);
1197         QCOMPARE(object->property("test2").toBool(), true);
1198         QCOMPARE(object->property("test3").toBool(), true);
1199
1200         delete object;
1201     }
1202
1203     // Signal argument scope
1204     {
1205         QQmlComponent component(&engine, testFileUrl("scope.4.qml"));
1206         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1207         QVERIFY(object != 0);
1208
1209         QCOMPARE(object->property("test").toInt(), 0);
1210         QCOMPARE(object->property("test2").toString(), QString());
1211
1212         emit object->argumentSignal(13, "Argument Scope", 9, MyQmlObject::EnumValue4, Qt::RightButton);
1213
1214         QCOMPARE(object->property("test").toInt(), 13);
1215         QCOMPARE(object->property("test2").toString(), QString("Argument Scope"));
1216
1217         delete object;
1218     }
1219
1220     {
1221         QQmlComponent component(&engine, testFileUrl("scope.5.qml"));
1222         QObject *object = component.create();
1223         QVERIFY(object != 0);
1224
1225         QCOMPARE(object->property("test1").toBool(), true);
1226         QCOMPARE(object->property("test2").toBool(), true);
1227
1228         delete object;
1229     }
1230
1231     {
1232         QQmlComponent component(&engine, testFileUrl("scope.6.qml"));
1233         QObject *object = component.create();
1234         QVERIFY(object != 0);
1235
1236         QCOMPARE(object->property("test").toBool(), true);
1237
1238         delete object;
1239     }
1240 }
1241
1242 // In 4.7, non-library javascript files that had no imports shared the imports of their
1243 // importing context
1244 void tst_qqmlecmascript::importScope()
1245 {
1246     QQmlComponent component(&engine, testFileUrl("importScope.qml"));
1247     QObject *o = component.create();
1248     QVERIFY(o != 0);
1249
1250     QCOMPARE(o->property("test").toInt(), 240);
1251
1252     delete o;
1253 }
1254
1255 /*
1256 Tests that "any" type passes through a synthesized signal parameter.  This
1257 is essentially a test of QQmlMetaType::copy()
1258 */
1259 void tst_qqmlecmascript::signalParameterTypes()
1260 {
1261     QQmlComponent component(&engine, testFileUrl("signalParameterTypes.qml"));
1262     MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1263     QVERIFY(object != 0);
1264
1265     emit object->basicSignal();
1266
1267     QCOMPARE(object->property("intProperty").toInt(), 10);
1268     QCOMPARE(object->property("realProperty").toReal(), 19.2);
1269     QVERIFY(object->property("colorProperty").value<QColor>() == QColor(255, 255, 0, 255));
1270     QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255)));
1271     QVERIFY(object->property("enumProperty") == MyQmlObject::EnumValue3);
1272     QVERIFY(object->property("qtEnumProperty") == Qt::LeftButton);
1273
1274     delete object;
1275 }
1276
1277 /*
1278 Test that two JS objects for the same QObject compare as equal.
1279 */
1280 void tst_qqmlecmascript::objectsCompareAsEqual()
1281 {
1282     QQmlComponent component(&engine, testFileUrl("objectsCompareAsEqual.qml"));
1283     QObject *object = component.create();
1284     QVERIFY(object != 0);
1285
1286     QCOMPARE(object->property("test1").toBool(), true);
1287     QCOMPARE(object->property("test2").toBool(), true);
1288     QCOMPARE(object->property("test3").toBool(), true);
1289     QCOMPARE(object->property("test4").toBool(), true);
1290     QCOMPARE(object->property("test5").toBool(), true);
1291
1292     delete object;
1293 }
1294
1295 /*
1296 Confirm bindings and alias properties can coexist.
1297
1298 Tests for a regression where the binding would not reevaluate.
1299 */
1300 void tst_qqmlecmascript::aliasPropertyAndBinding()
1301 {
1302     QQmlComponent component(&engine, testFileUrl("aliasPropertyAndBinding.qml"));
1303     QObject *object = component.create();
1304     QVERIFY(object != 0);
1305
1306     QCOMPARE(object->property("c2").toInt(), 3);
1307     QCOMPARE(object->property("c3").toInt(), 3);
1308
1309     object->setProperty("c2", QVariant(19));
1310
1311     QCOMPARE(object->property("c2").toInt(), 19);
1312     QCOMPARE(object->property("c3").toInt(), 19);
1313
1314     delete object;
1315 }
1316
1317 /*
1318 Ensure that we can write undefined value to an alias property,
1319 and that the aliased property is reset correctly if possible.
1320 */
1321 void tst_qqmlecmascript::aliasPropertyReset()
1322 {
1323     QObject *object = 0;
1324
1325     // test that a manual write (of undefined) to a resettable aliased property succeeds
1326     QQmlComponent c1(&engine, testFileUrl("aliasreset/aliasPropertyReset.1.qml"));
1327     object = c1.create();
1328     QVERIFY(object != 0);
1329     QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1330     QCOMPARE(object->property("aliasIsUndefined"), QVariant(false));
1331     QMetaObject::invokeMethod(object, "resetAliased");
1332     QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1333     QCOMPARE(object->property("aliasIsUndefined"), QVariant(true));
1334     delete object;
1335
1336     // test that a manual write (of undefined) to a resettable alias property succeeds
1337     QQmlComponent c2(&engine, testFileUrl("aliasreset/aliasPropertyReset.2.qml"));
1338     object = c2.create();
1339     QVERIFY(object != 0);
1340     QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1341     QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false));
1342     QMetaObject::invokeMethod(object, "resetAlias");
1343     QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1344     QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(true));
1345     delete object;
1346
1347     // test that an alias to a bound property works correctly
1348     QQmlComponent c3(&engine, testFileUrl("aliasreset/aliasPropertyReset.3.qml"));
1349     object = c3.create();
1350     QVERIFY(object != 0);
1351     QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1352     QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false));
1353     QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1354     QMetaObject::invokeMethod(object, "resetAlias");
1355     QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1356     QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(true));
1357     QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1358     delete object;
1359
1360     // test that a manual write (of undefined) to a resettable alias property
1361     // whose aliased property's object has been deleted, does not crash.
1362     QQmlComponent c4(&engine, testFileUrl("aliasreset/aliasPropertyReset.4.qml"));
1363     object = c4.create();
1364     QVERIFY(object != 0);
1365     QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1366     QObject *loader = object->findChild<QObject*>("loader");
1367     QVERIFY(loader != 0);
1368     delete loader;
1369     QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // deletion should have caused value unset.
1370     QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash.
1371     QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1372     QMetaObject::invokeMethod(object, "setAlias");   // shouldn't crash, and shouldn't change value (since it's no longer referencing anything).
1373     QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0);
1374     delete object;
1375
1376     // test that binding an alias property to an undefined value works correctly
1377     QQmlComponent c5(&engine, testFileUrl("aliasreset/aliasPropertyReset.5.qml"));
1378     object = c5.create();
1379     QVERIFY(object != 0);
1380     QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // bound to undefined value.
1381     delete object;
1382
1383     // test that a manual write (of undefined) to a non-resettable property fails properly
1384     QUrl url = testFileUrl("aliasreset/aliasPropertyReset.error.1.qml");
1385     QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int");
1386     QQmlComponent e1(&engine, url);
1387     object = e1.create();
1388     QVERIFY(object != 0);
1389     QCOMPARE(object->property("intAlias").value<int>(), 12);
1390     QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1391     QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1392     QMetaObject::invokeMethod(object, "resetAlias");
1393     QCOMPARE(object->property("intAlias").value<int>(), 12);
1394     QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1395     delete object;
1396 }
1397
1398 void tst_qqmlecmascript::componentCreation_data()
1399 {
1400     QTest::addColumn<QString>("method");
1401     QTest::addColumn<QString>("creationError");
1402     QTest::addColumn<QString>("createdParent");
1403
1404     QTest::newRow("url")
1405         << "url"
1406         << ""
1407         << "";
1408     QTest::newRow("urlMode")
1409         << "urlMode"
1410         << ""
1411         << "";
1412     QTest::newRow("urlParent")
1413         << "urlParent"
1414         << ""
1415         << "obj";
1416     QTest::newRow("urlNullParent")
1417         << "urlNullParent"
1418         << ""
1419         << "null";
1420     QTest::newRow("urlModeParent")
1421         << "urlModeParent"
1422         << ""
1423         << "obj";
1424     QTest::newRow("urlModeNullParent")
1425         << "urlModeNullParent"
1426         << ""
1427         << "null";
1428     QTest::newRow("invalidSecondArg")
1429         << "invalidSecondArg"
1430         << ":40: Error: Qt.createComponent(): Invalid arguments"
1431         << "";
1432     QTest::newRow("invalidThirdArg")
1433         << "invalidThirdArg"
1434         << ":45: Error: Qt.createComponent(): Invalid parent object"
1435         << "";
1436     QTest::newRow("invalidMode")
1437         << "invalidMode"
1438         << ":50: Error: Qt.createComponent(): Invalid arguments"
1439         << "";
1440 }
1441
1442 /*
1443 Test using createComponent to dynamically generate a component.
1444 */
1445 void tst_qqmlecmascript::componentCreation()
1446 {
1447     QFETCH(QString, method);
1448     QFETCH(QString, creationError);
1449     QFETCH(QString, createdParent);
1450
1451     QUrl testUrl(testFileUrl("componentCreation.qml"));
1452
1453     if (!creationError.isEmpty()) {
1454         QString warning = testUrl.toString() + creationError;
1455         QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1456     }
1457
1458     QQmlComponent component(&engine, testUrl);
1459     MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create());
1460     QVERIFY(object != 0);
1461
1462     QMetaObject::invokeMethod(object, method.toUtf8());
1463     QQmlComponent *created = object->componentProperty();
1464
1465     if (creationError.isEmpty()) {
1466         QVERIFY(created);
1467
1468         QObject *expectedParent;
1469         if (createdParent == QLatin1String("obj")) {
1470             expectedParent = object;
1471         } else if ((createdParent == QLatin1String("null")) || createdParent.isEmpty()) {
1472             expectedParent = 0;
1473         }
1474         QCOMPARE(created->parent(), expectedParent);
1475     }
1476 }
1477
1478 void tst_qqmlecmascript::dynamicCreation_data()
1479 {
1480     QTest::addColumn<QString>("method");
1481     QTest::addColumn<QString>("createdName");
1482
1483     QTest::newRow("One") << "createOne" << "objectOne";
1484     QTest::newRow("Two") << "createTwo" << "objectTwo";
1485     QTest::newRow("Three") << "createThree" << "objectThree";
1486 }
1487
1488 /*
1489 Test using createQmlObject to dynamically generate an item
1490 Also using createComponent is tested.
1491 */
1492 void tst_qqmlecmascript::dynamicCreation()
1493 {
1494     QFETCH(QString, method);
1495     QFETCH(QString, createdName);
1496
1497     QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
1498     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1499     QVERIFY(object != 0);
1500
1501     QMetaObject::invokeMethod(object, method.toUtf8());
1502     QObject *created = object->objectProperty();
1503     QVERIFY(created);
1504     QCOMPARE(created->objectName(), createdName);
1505
1506     delete object;
1507 }
1508
1509 /*
1510    Tests the destroy function
1511 */
1512 void tst_qqmlecmascript::dynamicDestruction()
1513 {
1514     {
1515     QQmlComponent component(&engine, testFileUrl("dynamicDeletion.qml"));
1516     QQmlGuard<MyQmlObject> object = qobject_cast<MyQmlObject*>(component.create());
1517     QVERIFY(object != 0);
1518     QQmlGuard<QObject> createdQmlObject = 0;
1519
1520     QMetaObject::invokeMethod(object, "create");
1521     createdQmlObject = object->objectProperty();
1522     QVERIFY(createdQmlObject);
1523     QCOMPARE(createdQmlObject->objectName(), QString("emptyObject"));
1524
1525     QMetaObject::invokeMethod(object, "killOther");
1526     QVERIFY(createdQmlObject);
1527
1528     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1529     QCoreApplication::processEvents();
1530     QVERIFY(createdQmlObject);
1531     for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1532         if (createdQmlObject) {
1533             QTest::qWait(100);
1534             QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1535             QCoreApplication::processEvents();
1536         }
1537     }
1538     QVERIFY(!createdQmlObject);
1539
1540     QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
1541     QMetaObject::invokeMethod(object, "killMe");
1542     QVERIFY(object);
1543     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1544     QCoreApplication::processEvents();
1545     QVERIFY(!object);
1546     }
1547
1548     {
1549     QQmlComponent component(&engine, testFileUrl("dynamicDeletion.2.qml"));
1550     QObject *o = component.create();
1551     QVERIFY(o != 0);
1552
1553     QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1554
1555     QMetaObject::invokeMethod(o, "create");
1556
1557     QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) != 0);
1558
1559     QMetaObject::invokeMethod(o, "destroy");
1560
1561     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1562     QCoreApplication::processEvents();
1563
1564     QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1565
1566     delete o;
1567     }
1568
1569     {
1570     // QTBUG-23451
1571     QQmlGuard<QObject> createdQmlObject = 0;
1572     QQmlComponent component(&engine, testFileUrl("dynamicDeletion.3.qml"));
1573     QObject *o = component.create();
1574     QVERIFY(o != 0);
1575     QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1576     QMetaObject::invokeMethod(o, "create");
1577     createdQmlObject = qvariant_cast<QObject*>(o->property("objectProperty"));
1578     QVERIFY(createdQmlObject);
1579     QMetaObject::invokeMethod(o, "destroy");
1580     QVERIFY(qvariant_cast<bool>(o->property("test")) == false);
1581     for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1582         QTest::qWait(100);
1583         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1584         QCoreApplication::processEvents();
1585     }
1586     QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
1587     QVERIFY(qvariant_cast<bool>(o->property("test")) == true);
1588     delete o;
1589     }
1590 }
1591
1592 /*
1593    tests that id.toString() works
1594 */
1595 void tst_qqmlecmascript::objectToString()
1596 {
1597     QQmlComponent component(&engine, testFileUrl("qmlToString.qml"));
1598     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1599     QVERIFY(object != 0);
1600     QMetaObject::invokeMethod(object, "testToString");
1601     QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_"));
1602     QVERIFY(object->stringProperty().endsWith(", \"objName\")"));
1603
1604     delete object;
1605 }
1606
1607 /*
1608   tests that id.hasOwnProperty() works
1609 */
1610 void tst_qqmlecmascript::objectHasOwnProperty()
1611 {
1612     QUrl url = testFileUrl("qmlHasOwnProperty.qml");
1613     QString warning1 = url.toString() + ":59: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1614     QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1615     QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1616
1617     QQmlComponent component(&engine, url);
1618     QObject *object = component.create();
1619     QVERIFY(object != 0);
1620
1621     // test QObjects in QML
1622     QMetaObject::invokeMethod(object, "testHasOwnPropertySuccess");
1623     QVERIFY(object->property("result").value<bool>() == true);
1624     QMetaObject::invokeMethod(object, "testHasOwnPropertyFailure");
1625     QVERIFY(object->property("result").value<bool>() == false);
1626
1627     // now test other types in QML
1628     QObject *child = object->findChild<QObject*>("typeObj");
1629     QVERIFY(child != 0);
1630     QMetaObject::invokeMethod(child, "testHasOwnPropertySuccess");
1631     QCOMPARE(child->property("valueTypeHasOwnProperty").toBool(), true);
1632     QCOMPARE(child->property("valueTypeHasOwnProperty2").toBool(), true);
1633     QCOMPARE(child->property("variantTypeHasOwnProperty").toBool(), true);
1634     QCOMPARE(child->property("stringTypeHasOwnProperty").toBool(), true);
1635     QCOMPARE(child->property("listTypeHasOwnProperty").toBool(), true);
1636     QCOMPARE(child->property("emptyListTypeHasOwnProperty").toBool(), true);
1637     QCOMPARE(child->property("enumTypeHasOwnProperty").toBool(), true);
1638     QCOMPARE(child->property("typenameHasOwnProperty").toBool(), true);
1639     QCOMPARE(child->property("typenameHasOwnProperty2").toBool(), true);
1640     QCOMPARE(child->property("singletonTypeTypeHasOwnProperty").toBool(), true);
1641     QCOMPARE(child->property("singletonTypePropertyTypeHasOwnProperty").toBool(), true);
1642
1643     QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1644     QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureOne");
1645     QCOMPARE(child->property("enumNonValueHasOwnProperty").toBool(), false);
1646     QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1647     QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureTwo");
1648     QCOMPARE(child->property("singletonTypeNonPropertyHasOwnProperty").toBool(), false);
1649     QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1650     QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureThree");
1651     QCOMPARE(child->property("listAtInvalidHasOwnProperty").toBool(), false);
1652
1653     delete object;
1654 }
1655
1656 /*
1657 Tests bindings that indirectly cause their own deletion work.
1658
1659 This test is best run under valgrind to ensure no invalid memory access occur.
1660 */
1661 void tst_qqmlecmascript::selfDeletingBinding()
1662 {
1663     {
1664         QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.qml"));
1665         QObject *object = component.create();
1666         QVERIFY(object != 0);
1667         object->setProperty("triggerDelete", true);
1668         delete object;
1669     }
1670
1671     {
1672         QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.2.qml"));
1673         QObject *object = component.create();
1674         QVERIFY(object != 0);
1675         object->setProperty("triggerDelete", true);
1676         delete object;
1677     }
1678 }
1679
1680 /*
1681 Test that extended object properties can be accessed.
1682
1683 This test a regression where this used to crash.  The issue was specificially
1684 for extended objects that did not include a synthesized meta object (so non-root
1685 and no synthesiszed properties).
1686 */
1687 void tst_qqmlecmascript::extendedObjectPropertyLookup()
1688 {
1689     QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup.qml"));
1690     QObject *object = component.create();
1691     QVERIFY(object != 0);
1692     delete object;
1693 }
1694
1695 /*
1696 Test that extended object properties can be accessed correctly.
1697 */
1698 void tst_qqmlecmascript::extendedObjectPropertyLookup2()
1699 {
1700     QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup2.qml"));
1701     QObject *object = component.create();
1702     QVERIFY(object != 0);
1703
1704     QVariant returnValue;
1705     QVERIFY(QMetaObject::invokeMethod(object, "getValue", Q_RETURN_ARG(QVariant, returnValue)));
1706     QCOMPARE(returnValue.toInt(), 42);
1707
1708     delete object;
1709 }
1710 /*
1711 Test file/lineNumbers for binding/Script errors.
1712 */
1713 void tst_qqmlecmascript::scriptErrors()
1714 {
1715     QQmlComponent component(&engine, testFileUrl("scriptErrors.qml"));
1716     QString url = component.url().toString();
1717
1718     QString warning1 = url.left(url.length() - 3) + "js:2: Error: Invalid write to global property \"a\"";
1719     QString warning2 = url + ":5: ReferenceError: a is not defined";
1720     QString warning3 = url.left(url.length() - 3) + "js:4: Error: Invalid write to global property \"a\"";
1721     QString warning4 = url + ":13: ReferenceError: a is not defined";
1722     QString warning5 = url + ":11: ReferenceError: a is not defined";
1723     QString warning6 = url + ":10: Unable to assign [undefined] to int";
1724     QString warning7 = url + ":15: Error: Cannot assign to read-only property \"trueProperty\"";
1725     QString warning8 = url + ":16: Error: Cannot assign to non-existent property \"fakeProperty\"";
1726
1727     QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
1728     QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
1729     QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
1730     QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
1731     QTest::ignoreMessage(QtWarningMsg, warning6.toLatin1().constData());
1732     MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1733     QVERIFY(object != 0);
1734
1735     QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
1736     emit object->basicSignal();
1737
1738     QTest::ignoreMessage(QtWarningMsg, warning7.toLatin1().constData());
1739     emit object->anotherBasicSignal();
1740
1741     QTest::ignoreMessage(QtWarningMsg, warning8.toLatin1().constData());
1742     emit object->thirdBasicSignal();
1743
1744     delete object;
1745 }
1746
1747 /*
1748 Test file/lineNumbers for inline functions.
1749 */
1750 void tst_qqmlecmascript::functionErrors()
1751 {
1752     QQmlComponent component(&engine, testFileUrl("functionErrors.qml"));
1753     QString url = component.url().toString();
1754
1755     QString warning = url + ":5: Error: Invalid write to global property \"a\"";
1756
1757     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1758
1759     QObject *object = component.create();
1760     QVERIFY(object != 0);
1761     delete object;
1762
1763     // test that if an exception occurs while invoking js function from cpp, it is reported as expected.
1764     QQmlComponent componentTwo(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
1765     url = componentTwo.url().toString();
1766     object = componentTwo.create();
1767     QVERIFY(object != 0);
1768
1769     QString srpname = object->property("srp_name").toString();
1770     
1771     warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srpname
1772                   + QLatin1String(" is not a function");
1773     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed.
1774     QMetaObject::invokeMethod(object, "retrieveScarceResource");
1775     delete object;
1776 }
1777
1778 /*
1779 Test various errors that can occur when assigning a property from script
1780 */
1781 void tst_qqmlecmascript::propertyAssignmentErrors()
1782 {
1783     QQmlComponent component(&engine, testFileUrl("propertyAssignmentErrors.qml"));
1784
1785     QString url = component.url().toString();
1786
1787     QObject *object = component.create();
1788     QVERIFY(object != 0);
1789
1790     QCOMPARE(object->property("test1").toBool(), true);
1791     QCOMPARE(object->property("test2").toBool(), true);
1792
1793     delete object;
1794 }
1795     
1796 /*
1797 Test bindings still work when the reeval is triggered from within
1798 a signal script.
1799 */
1800 void tst_qqmlecmascript::signalTriggeredBindings()
1801 {
1802     QQmlComponent component(&engine, testFileUrl("signalTriggeredBindings.qml"));
1803     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1804     QVERIFY(object != 0);
1805
1806     QCOMPARE(object->property("base").toReal(), 50.);
1807     QCOMPARE(object->property("test1").toReal(), 50.);
1808     QCOMPARE(object->property("test2").toReal(), 50.);
1809
1810     object->basicSignal();
1811
1812     QCOMPARE(object->property("base").toReal(), 200.);
1813     QCOMPARE(object->property("test1").toReal(), 200.);
1814     QCOMPARE(object->property("test2").toReal(), 200.);
1815
1816     object->argumentSignal(10, QString(), 10, MyQmlObject::EnumValue4, Qt::RightButton);
1817
1818     QCOMPARE(object->property("base").toReal(), 400.);
1819     QCOMPARE(object->property("test1").toReal(), 400.);
1820     QCOMPARE(object->property("test2").toReal(), 400.);
1821
1822     delete object;
1823 }
1824
1825 /*
1826 Test that list properties can be iterated from ECMAScript
1827 */
1828 void tst_qqmlecmascript::listProperties()
1829 {
1830     QQmlComponent component(&engine, testFileUrl("listProperties.qml"));
1831     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1832     QVERIFY(object != 0);
1833
1834     QCOMPARE(object->property("test1").toInt(), 21);
1835     QCOMPARE(object->property("test2").toInt(), 2);
1836     QCOMPARE(object->property("test3").toBool(), true);
1837     QCOMPARE(object->property("test4").toBool(), true);
1838
1839     delete object;
1840 }
1841
1842 void tst_qqmlecmascript::exceptionClearsOnReeval()
1843 {
1844     QQmlComponent component(&engine, testFileUrl("exceptionClearsOnReeval.qml"));
1845     QString url = component.url().toString();
1846
1847     QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null";
1848
1849     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1850     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1851     QVERIFY(object != 0);
1852
1853     QCOMPARE(object->property("test").toBool(), false);
1854
1855     MyQmlObject object2;
1856     MyQmlObject object3;
1857     object2.setObjectProperty(&object3);
1858     object->setObjectProperty(&object2);
1859
1860     QCOMPARE(object->property("test").toBool(), true);
1861
1862     delete object;
1863 }
1864
1865 void tst_qqmlecmascript::exceptionSlotProducesWarning()
1866 {
1867     QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning.qml"));
1868     QString url = component.url().toString();
1869
1870     QString warning = component.url().toString() + ":6: Error: JS exception";
1871
1872     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1873     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1874     QVERIFY(object != 0);
1875     delete object;
1876 }
1877
1878 void tst_qqmlecmascript::exceptionBindingProducesWarning()
1879 {
1880     QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning2.qml"));
1881     QString url = component.url().toString();
1882
1883     QString warning = component.url().toString() + ":5: Error: JS exception";
1884
1885     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1886     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1887     QVERIFY(object != 0);
1888     delete object;
1889 }
1890
1891 void tst_qqmlecmascript::compileInvalidBinding()
1892 {
1893     // QTBUG-23387: ensure that invalid bindings don't cause a crash.
1894     QQmlComponent component(&engine, testFileUrl("v8bindingException.qml"));
1895     QObject *object = component.create();
1896     QVERIFY(object != 0);
1897     delete object;
1898 }
1899
1900 static int transientErrorsMsgCount = 0;
1901 static void transientErrorsMsgHandler(QtMsgType, const char *)
1902 {
1903     ++transientErrorsMsgCount;
1904 }
1905
1906 // Check that transient binding errors are not displayed
1907 void tst_qqmlecmascript::transientErrors()
1908 {
1909     {
1910     QQmlComponent component(&engine, testFileUrl("transientErrors.qml"));
1911
1912     transientErrorsMsgCount = 0;
1913     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1914
1915     QObject *object = component.create();
1916     QVERIFY(object != 0);
1917
1918     qInstallMsgHandler(old);
1919
1920     QCOMPARE(transientErrorsMsgCount, 0);
1921
1922     delete object;
1923     }
1924
1925     // One binding erroring multiple times, but then resolving
1926     {
1927     QQmlComponent component(&engine, testFileUrl("transientErrors.2.qml"));
1928
1929     transientErrorsMsgCount = 0;
1930     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1931
1932     QObject *object = component.create();
1933     QVERIFY(object != 0);
1934
1935     qInstallMsgHandler(old);
1936
1937     QCOMPARE(transientErrorsMsgCount, 0);
1938
1939     delete object;
1940     }
1941 }
1942
1943 // Check that errors during shutdown are minimized
1944 void tst_qqmlecmascript::shutdownErrors()
1945 {
1946     QQmlComponent component(&engine, testFileUrl("shutdownErrors.qml"));
1947     QObject *object = component.create();
1948     QVERIFY(object != 0);
1949
1950     transientErrorsMsgCount = 0;
1951     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1952
1953     delete object;
1954
1955     qInstallMsgHandler(old);
1956     QCOMPARE(transientErrorsMsgCount, 0);
1957 }
1958
1959 void tst_qqmlecmascript::compositePropertyType()
1960 {
1961     QQmlComponent component(&engine, testFileUrl("compositePropertyType.qml"));
1962
1963     QTest::ignoreMessage(QtDebugMsg, "hello world");
1964     QObject *object = qobject_cast<QObject *>(component.create());
1965     delete object;
1966 }
1967
1968 // QTBUG-5759
1969 void tst_qqmlecmascript::jsObject()
1970 {
1971     QQmlComponent component(&engine, testFileUrl("jsObject.qml"));
1972     QObject *object = component.create();
1973     QVERIFY(object != 0);
1974
1975     QCOMPARE(object->property("test").toInt(), 92);
1976
1977     delete object;
1978 }
1979
1980 void tst_qqmlecmascript::undefinedResetsProperty()
1981 {
1982     {
1983     QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.qml"));
1984     QObject *object = component.create();
1985     QVERIFY(object != 0);
1986
1987     QCOMPARE(object->property("resettableProperty").toInt(), 92);
1988
1989     object->setProperty("setUndefined", true);
1990
1991     QCOMPARE(object->property("resettableProperty").toInt(), 13);
1992
1993     object->setProperty("setUndefined", false);
1994
1995     QCOMPARE(object->property("resettableProperty").toInt(), 92);
1996
1997     delete object;
1998     }
1999     {
2000     QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.2.qml"));
2001     QObject *object = component.create();
2002     QVERIFY(object != 0);
2003
2004     QCOMPARE(object->property("resettableProperty").toInt(), 19);
2005
2006     QMetaObject::invokeMethod(object, "doReset");
2007
2008     QCOMPARE(object->property("resettableProperty").toInt(), 13);
2009
2010     delete object;
2011     }
2012 }
2013
2014 // Aliases to variant properties should work
2015 void tst_qqmlecmascript::qtbug_22464()
2016 {
2017     QQmlComponent component(&engine, testFileUrl("qtbug_22464.qml"));
2018     QObject *object = component.create();
2019     QVERIFY(object != 0);
2020
2021     QCOMPARE(object->property("test").toBool(), true);
2022
2023     delete object;
2024 }
2025
2026 void tst_qqmlecmascript::qtbug_21580()
2027 {
2028     QQmlComponent component(&engine, testFileUrl("qtbug_21580.qml"));
2029
2030     QObject *object = component.create();
2031     QVERIFY(object != 0);
2032
2033     QCOMPARE(object->property("test").toBool(), true);
2034
2035     delete object;
2036 }
2037
2038 // Causes a v8 binding, but not all v8 bindings to be destroyed during evaluation
2039 void tst_qqmlecmascript::singleV8BindingDestroyedDuringEvaluation()
2040 {
2041     QQmlComponent component(&engine, testFileUrl("singleV8BindingDestroyedDuringEvaluation.qml"));
2042
2043     QObject *object = component.create();
2044     QVERIFY(object != 0);
2045     delete object;
2046 }
2047
2048 // QTBUG-6781
2049 void tst_qqmlecmascript::bug1()
2050 {
2051     QQmlComponent component(&engine, testFileUrl("bug.1.qml"));
2052     QObject *object = component.create();
2053     QVERIFY(object != 0);
2054
2055     QCOMPARE(object->property("test").toInt(), 14);
2056
2057     object->setProperty("a", 11);
2058
2059     QCOMPARE(object->property("test").toInt(), 3);
2060
2061     object->setProperty("b", true);
2062
2063     QCOMPARE(object->property("test").toInt(), 9);
2064
2065     delete object;
2066 }
2067
2068 #ifndef QT_NO_WIDGETS
2069 void tst_qqmlecmascript::bug2()
2070 {
2071     QQmlComponent component(&engine);
2072     component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl());
2073
2074     QObject *object = component.create();
2075     QVERIFY(object != 0);
2076
2077     delete object;
2078 }
2079 #endif
2080
2081 // Don't crash in createObject when the component has errors.
2082 void tst_qqmlecmascript::dynamicCreationCrash()
2083 {
2084     QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml"));
2085     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2086     QVERIFY(object != 0);
2087
2088     QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2089     QMetaObject::invokeMethod(object, "dontCrash");
2090     QObject *created = object->objectProperty();
2091     QVERIFY(created == 0);
2092
2093     delete object;
2094 }
2095
2096 // ownership transferred to JS, ensure that GC runs the dtor
2097 void tst_qqmlecmascript::dynamicCreationOwnership()
2098 {
2099     int dtorCount = 0;
2100     int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
2101
2102     // allow the engine to go out of scope too.
2103     {
2104         QQmlEngine dcoEngine;
2105         QQmlComponent component(&dcoEngine, testFileUrl("dynamicCreationOwnership.qml"));
2106         QObject *object = component.create();
2107         QVERIFY(object != 0);
2108         MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo");
2109         QVERIFY(mdcdo != 0);
2110         mdcdo->setDtorCount(&dtorCount);
2111
2112         for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
2113             QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
2114             if (i % 90 == 0) {
2115                 // we do this once manually, but it should be done automatically
2116                 // when the engine goes out of scope (since it should gc in dtor)
2117                 QMetaObject::invokeMethod(object, "performGc");
2118             }
2119             if (i % 10 == 0) {
2120                 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2121                 QCoreApplication::processEvents();
2122             }
2123         }
2124
2125         delete object;
2126     }
2127     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2128     QCoreApplication::processEvents();
2129     QCOMPARE(dtorCount, expectedDtorCount);
2130 }
2131
2132 void tst_qqmlecmascript::regExpBug()
2133 {
2134     //QTBUG-9367
2135     {
2136         QQmlComponent component(&engine, testFileUrl("regExp.qml"));
2137         MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2138         QVERIFY(object != 0);
2139         QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]"));
2140         delete object;
2141     }
2142
2143     //QTBUG-23068
2144     {
2145         QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString());
2146         QQmlComponent component(&engine, testFileUrl("regExp.2.qml"));
2147         QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
2148         MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
2149         QVERIFY(!object);
2150         QCOMPARE(component.errorString(), err);
2151     }
2152 }
2153
2154 static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source)
2155 {
2156     QString functionSource = QLatin1String("(function(object) { return ") + 
2157                              QLatin1String(source) + QLatin1String(" })");
2158     v8::TryCatch tc;
2159     v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2160     if (tc.HasCaught())
2161         return false;
2162     v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2163     if (function.IsEmpty())
2164         return false;
2165     v8::Handle<v8::Value> args[] = { o };
2166     function->Call(engine->global(), 1, args);
2167     return tc.HasCaught();
2168 }
2169
2170 static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o, 
2171                                   const char *source, v8::Handle<v8::Value> result)
2172 {
2173     QString functionSource = QLatin1String("(function(object) { return ") + 
2174                              QLatin1String(source) + QLatin1String(" })");
2175     v8::TryCatch tc;
2176     v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2177     if (tc.HasCaught())
2178         return false;
2179     v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2180     if (function.IsEmpty())
2181         return false;
2182     v8::Handle<v8::Value> args[] = { o };
2183
2184     v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2185
2186     if (tc.HasCaught())
2187         return false;
2188
2189     return value->StrictEquals(result);
2190 }
2191
2192 static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o, 
2193                                              const char *source)
2194 {
2195     QString functionSource = QLatin1String("(function(object) { return ") + 
2196                              QLatin1String(source) + QLatin1String(" })");
2197     v8::TryCatch tc;
2198     v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
2199     if (tc.HasCaught())
2200         return v8::Handle<v8::Value>();
2201     v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
2202     if (function.IsEmpty())
2203         return v8::Handle<v8::Value>();
2204     v8::Handle<v8::Value> args[] = { o };
2205
2206     v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
2207
2208     if (tc.HasCaught())
2209         return v8::Handle<v8::Value>();
2210     return value;
2211 }
2212
2213 #define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
2214 #define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result)
2215 #define EVALUATE(source) evaluate(engine, object, source)
2216
2217 void tst_qqmlecmascript::callQtInvokables()
2218 {
2219     MyInvokableObject o;
2220
2221     QQmlEngine qmlengine;
2222     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine);
2223     
2224     QV8Engine *engine = ep->v8engine();
2225
2226     v8::HandleScope handle_scope;
2227     v8::Context::Scope scope(engine->context());
2228
2229     v8::Local<v8::Object> object = engine->newQObject(&o)->ToObject();
2230
2231     // Non-existent methods
2232     o.reset();
2233     QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
2234     QCOMPARE(o.error(), false);
2235     QCOMPARE(o.invoked(), -1);
2236     QCOMPARE(o.actuals().count(), 0);
2237
2238     o.reset();
2239     QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)"));
2240     QCOMPARE(o.error(), false);
2241     QCOMPARE(o.invoked(), -1);
2242     QCOMPARE(o.actuals().count(), 0);
2243
2244     // Insufficient arguments
2245     o.reset();
2246     QVERIFY(EVALUATE_ERROR("object.method_int()"));
2247     QCOMPARE(o.error(), false);
2248     QCOMPARE(o.invoked(), -1);
2249     QCOMPARE(o.actuals().count(), 0);
2250
2251     o.reset();
2252     QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
2253     QCOMPARE(o.error(), false);
2254     QCOMPARE(o.invoked(), -1);
2255     QCOMPARE(o.actuals().count(), 0);
2256
2257     // Excessive arguments
2258     o.reset();
2259     QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", v8::Undefined()));
2260     QCOMPARE(o.error(), false);
2261     QCOMPARE(o.invoked(), 8);
2262     QCOMPARE(o.actuals().count(), 1);
2263     QCOMPARE(o.actuals().at(0), QVariant(10));
2264
2265     o.reset();
2266     QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", v8::Undefined()));
2267     QCOMPARE(o.error(), false);
2268     QCOMPARE(o.invoked(), 9);
2269     QCOMPARE(o.actuals().count(), 2);
2270     QCOMPARE(o.actuals().at(0), QVariant(10));
2271     QCOMPARE(o.actuals().at(1), QVariant(11));
2272
2273     // Test return types
2274     o.reset();
2275     QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", v8::Undefined()));
2276     QCOMPARE(o.error(), false);
2277     QCOMPARE(o.invoked(), 0);
2278     QCOMPARE(o.actuals().count(), 0);
2279
2280     o.reset();
2281     QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", v8::Integer::New(6)));
2282     QCOMPARE(o.error(), false);
2283     QCOMPARE(o.invoked(), 1);
2284     QCOMPARE(o.actuals().count(), 0);
2285
2286     o.reset();
2287     QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", v8::Number::New(19.75)));
2288     QCOMPARE(o.error(), false);
2289     QCOMPARE(o.invoked(), 2);
2290     QCOMPARE(o.actuals().count(), 0);
2291
2292     o.reset();
2293     {
2294     v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QPointF()");
2295     QVERIFY(!ret.IsEmpty());
2296     QCOMPARE(engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5)));
2297     QCOMPARE(o.error(), false);
2298     QCOMPARE(o.invoked(), 3);
2299     QCOMPARE(o.actuals().count(), 0);
2300     }
2301
2302     o.reset();
2303     {
2304     v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QObject()");
2305     QCOMPARE(engine->toQObject(ret), (QObject *)&o);
2306     QCOMPARE(o.error(), false);
2307     QCOMPARE(o.invoked(), 4);
2308     QCOMPARE(o.actuals().count(), 0);
2309     }
2310
2311     o.reset();
2312     QVERIFY(EVALUATE_ERROR("object.method_NoArgs_unknown()"));
2313     QCOMPARE(o.error(), false);
2314     QCOMPARE(o.invoked(), -1);
2315     QCOMPARE(o.actuals().count(), 0);
2316
2317     o.reset();
2318     {
2319     v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QScriptValue()");
2320     QVERIFY(ret->IsString());
2321     QCOMPARE(engine->toString(ret), QString("Hello world"));
2322     QCOMPARE(o.error(), false);
2323     QCOMPARE(o.invoked(), 6);
2324     QCOMPARE(o.actuals().count(), 0);
2325     }
2326
2327     o.reset();
2328     QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks")));
2329     QCOMPARE(o.error(), false);
2330     QCOMPARE(o.invoked(), 7);
2331     QCOMPARE(o.actuals().count(), 0);
2332
2333     // Test arg types
2334     o.reset();
2335     QVERIFY(EVALUATE_VALUE("object.method_int(94)", v8::Undefined()));
2336     QCOMPARE(o.error(), false);
2337     QCOMPARE(o.invoked(), 8);
2338     QCOMPARE(o.actuals().count(), 1);
2339     QCOMPARE(o.actuals().at(0), QVariant(94));
2340
2341     o.reset();
2342     QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", v8::Undefined()));
2343     QCOMPARE(o.error(), false);
2344     QCOMPARE(o.invoked(), 8);
2345     QCOMPARE(o.actuals().count(), 1);
2346     QCOMPARE(o.actuals().at(0), QVariant(94));
2347
2348     o.reset();
2349     QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", v8::Undefined()));
2350     QCOMPARE(o.error(), false);
2351     QCOMPARE(o.invoked(), 8);
2352     QCOMPARE(o.actuals().count(), 1);
2353     QCOMPARE(o.actuals().at(0), QVariant(0));
2354
2355     o.reset();
2356     QVERIFY(EVALUATE_VALUE("object.method_int(null)", v8::Undefined()));
2357     QCOMPARE(o.error(), false);
2358     QCOMPARE(o.invoked(), 8);
2359     QCOMPARE(o.actuals().count(), 1);
2360     QCOMPARE(o.actuals().at(0), QVariant(0));
2361
2362     o.reset();
2363     QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", v8::Undefined()));
2364     QCOMPARE(o.error(), false);
2365     QCOMPARE(o.invoked(), 8);
2366     QCOMPARE(o.actuals().count(), 1);
2367     QCOMPARE(o.actuals().at(0), QVariant(0));
2368
2369     o.reset();
2370     QVERIFY(EVALUATE_VALUE("object.method_int(object)", v8::Undefined()));
2371     QCOMPARE(o.error(), false);
2372     QCOMPARE(o.invoked(), 8);
2373     QCOMPARE(o.actuals().count(), 1);
2374     QCOMPARE(o.actuals().at(0), QVariant(0));
2375
2376     o.reset();
2377     QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", v8::Undefined()));
2378     QCOMPARE(o.error(), false);
2379     QCOMPARE(o.invoked(), 9);
2380     QCOMPARE(o.actuals().count(), 2);
2381     QCOMPARE(o.actuals().at(0), QVariant(122));
2382     QCOMPARE(o.actuals().at(1), QVariant(9));
2383
2384     o.reset();
2385     QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", v8::Undefined()));
2386     QCOMPARE(o.error(), false);
2387     QCOMPARE(o.invoked(), 10);
2388     QCOMPARE(o.actuals().count(), 1);
2389     QCOMPARE(o.actuals().at(0), QVariant(94.3));
2390
2391     o.reset();
2392     QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", v8::Undefined()));
2393     QCOMPARE(o.error(), false);
2394     QCOMPARE(o.invoked(), 10);
2395     QCOMPARE(o.actuals().count(), 1);
2396     QCOMPARE(o.actuals().at(0), QVariant(94.3));
2397
2398     o.reset();
2399     QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", v8::Undefined()));
2400     QCOMPARE(o.error(), false);
2401     QCOMPARE(o.invoked(), 10);
2402     QCOMPARE(o.actuals().count(), 1);
2403     QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2404
2405     o.reset();
2406     QVERIFY(EVALUATE_VALUE("object.method_real(null)", v8::Undefined()));
2407     QCOMPARE(o.error(), false);
2408     QCOMPARE(o.invoked(), 10);
2409     QCOMPARE(o.actuals().count(), 1);
2410     QCOMPARE(o.actuals().at(0), QVariant(0));
2411
2412     o.reset();
2413     QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", v8::Undefined()));
2414     QCOMPARE(o.error(), false);
2415     QCOMPARE(o.invoked(), 10);
2416     QCOMPARE(o.actuals().count(), 1);
2417     QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2418
2419     o.reset();
2420     QVERIFY(EVALUATE_VALUE("object.method_real(object)", v8::Undefined()));
2421     QCOMPARE(o.error(), false);
2422     QCOMPARE(o.invoked(), 10);
2423     QCOMPARE(o.actuals().count(), 1);
2424     QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2425
2426     o.reset();
2427     QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", v8::Undefined()));
2428     QCOMPARE(o.error(), false);
2429     QCOMPARE(o.invoked(), 11);
2430     QCOMPARE(o.actuals().count(), 1);
2431     QCOMPARE(o.actuals().at(0), QVariant("Hello world"));
2432
2433     o.reset();
2434     QVERIFY(EVALUATE_VALUE("object.method_QString(19)", v8::Undefined()));
2435     QCOMPARE(o.error(), false);
2436     QCOMPARE(o.invoked(), 11);
2437     QCOMPARE(o.actuals().count(), 1);
2438     QCOMPARE(o.actuals().at(0), QVariant("19"));
2439
2440     o.reset();
2441     {
2442     QString expected = "MyInvokableObject(0x" + QString::number((quintptr)&o, 16) + ")";
2443     QVERIFY(EVALUATE_VALUE("object.method_QString(object)", v8::Undefined()));
2444     QCOMPARE(o.error(), false);
2445     QCOMPARE(o.invoked(), 11);
2446     QCOMPARE(o.actuals().count(), 1);
2447     QCOMPARE(o.actuals().at(0), QVariant(expected));
2448     }
2449
2450     o.reset();
2451     QVERIFY(EVALUATE_VALUE("object.method_QString(null)", v8::Undefined()));
2452     QCOMPARE(o.error(), false);
2453     QCOMPARE(o.invoked(), 11);
2454     QCOMPARE(o.actuals().count(), 1);
2455     QCOMPARE(o.actuals().at(0), QVariant(QString()));
2456
2457     o.reset();
2458     QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", v8::Undefined()));
2459     QCOMPARE(o.error(), false);
2460     QCOMPARE(o.invoked(), 11);
2461     QCOMPARE(o.actuals().count(), 1);
2462     QCOMPARE(o.actuals().at(0), QVariant(QString()));
2463
2464     o.reset();
2465     QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", v8::Undefined()));
2466     QCOMPARE(o.error(), false);
2467     QCOMPARE(o.invoked(), 12);
2468     QCOMPARE(o.actuals().count(), 1);
2469     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2470
2471     o.reset();
2472     QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", v8::Undefined()));
2473     QCOMPARE(o.error(), false);
2474     QCOMPARE(o.invoked(), 12);
2475     QCOMPARE(o.actuals().count(), 1);
2476     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2477
2478     o.reset();
2479     QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", v8::Undefined()));
2480     QCOMPARE(o.error(), false);
2481     QCOMPARE(o.invoked(), 12);
2482     QCOMPARE(o.actuals().count(), 1);
2483     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2484
2485     o.reset();
2486     QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", v8::Undefined()));
2487     QCOMPARE(o.error(), false);
2488     QCOMPARE(o.invoked(), 12);
2489     QCOMPARE(o.actuals().count(), 1);
2490     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2491
2492     o.reset();
2493     QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", v8::Undefined()));
2494     QCOMPARE(o.error(), false);
2495     QCOMPARE(o.invoked(), 12);
2496     QCOMPARE(o.actuals().count(), 1);
2497     QCOMPARE(o.actuals().at(0), QVariant(QPointF(99.3, -10.2)));
2498
2499     o.reset();
2500     QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", v8::Undefined()));
2501     QCOMPARE(o.error(), false);
2502     QCOMPARE(o.invoked(), 12);
2503     QCOMPARE(o.actuals().count(), 1);
2504     QCOMPARE(o.actuals().at(0), QVariant(QPointF(9, 12)));
2505
2506     o.reset();
2507     QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", v8::Undefined()));
2508     QCOMPARE(o.error(), false);
2509     QCOMPARE(o.invoked(), 13);
2510     QCOMPARE(o.actuals().count(), 1);
2511     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2512
2513     o.reset();
2514     QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", v8::Undefined()));
2515     QCOMPARE(o.error(), false);
2516     QCOMPARE(o.invoked(), 13);
2517     QCOMPARE(o.actuals().count(), 1);
2518     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2519
2520     o.reset();
2521     QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", v8::Undefined()));
2522     QCOMPARE(o.error(), false);
2523     QCOMPARE(o.invoked(), 13);
2524     QCOMPARE(o.actuals().count(), 1);
2525     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2526
2527     o.reset();
2528     QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", v8::Undefined()));
2529     QCOMPARE(o.error(), false);
2530     QCOMPARE(o.invoked(), 13);
2531     QCOMPARE(o.actuals().count(), 1);
2532     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2533
2534     o.reset();
2535     QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", v8::Undefined()));
2536     QCOMPARE(o.error(), false);
2537     QCOMPARE(o.invoked(), 13);
2538     QCOMPARE(o.actuals().count(), 1);
2539     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)&o));
2540
2541     o.reset();
2542     QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined()));
2543     QCOMPARE(o.error(), false);
2544     QCOMPARE(o.invoked(), 14);
2545     QCOMPARE(o.actuals().count(), 1);
2546     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isNull());
2547
2548     o.reset();
2549     QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined()));
2550     QCOMPARE(o.error(), false);
2551     QCOMPARE(o.invoked(), 14);
2552     QCOMPARE(o.actuals().count(), 1);
2553     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isUndefined());
2554
2555     o.reset();
2556     QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined()));
2557     QCOMPARE(o.error(), false);
2558     QCOMPARE(o.invoked(), 14);
2559     QCOMPARE(o.actuals().count(), 1);
2560     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).strictlyEquals(QJSValue(19)));
2561
2562     o.reset();
2563     QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined()));
2564     QCOMPARE(o.error(), false);
2565     QCOMPARE(o.invoked(), 14);
2566     QCOMPARE(o.actuals().count(), 1);
2567     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isArray());
2568
2569     o.reset();
2570     QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined()));
2571     QCOMPARE(o.error(), false);
2572     QCOMPARE(o.invoked(), 15);
2573     QCOMPARE(o.actuals().count(), 2);
2574     QCOMPARE(o.actuals().at(0), QVariant(4));
2575     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isNull());
2576
2577     o.reset();
2578     QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined()));
2579     QCOMPARE(o.error(), false);
2580     QCOMPARE(o.invoked(), 15);
2581     QCOMPARE(o.actuals().count(), 2);
2582     QCOMPARE(o.actuals().at(0), QVariant(8));
2583     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isUndefined());
2584
2585     o.reset();
2586     QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined()));
2587     QCOMPARE(o.error(), false);
2588     QCOMPARE(o.invoked(), 15);
2589     QCOMPARE(o.actuals().count(), 2);
2590     QCOMPARE(o.actuals().at(0), QVariant(3));
2591     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).strictlyEquals(QJSValue(19)));
2592
2593     o.reset();
2594     QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined()));
2595     QCOMPARE(o.error(), false);
2596     QCOMPARE(o.invoked(), 15);
2597     QCOMPARE(o.actuals().count(), 2);
2598     QCOMPARE(o.actuals().at(0), QVariant(44));
2599     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isArray());
2600
2601     o.reset();
2602     QVERIFY(EVALUATE_ERROR("object.method_overload()"));
2603     QCOMPARE(o.error(), false);
2604     QCOMPARE(o.invoked(), -1);
2605     QCOMPARE(o.actuals().count(), 0);
2606
2607     o.reset();
2608     QVERIFY(EVALUATE_VALUE("object.method_overload(10)", v8::Undefined()));
2609     QCOMPARE(o.error(), false);
2610     QCOMPARE(o.invoked(), 16);
2611     QCOMPARE(o.actuals().count(), 1);
2612     QCOMPARE(o.actuals().at(0), QVariant(10));
2613
2614     o.reset();
2615     QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", v8::Undefined()));
2616     QCOMPARE(o.error(), false);
2617     QCOMPARE(o.invoked(), 17);
2618     QCOMPARE(o.actuals().count(), 2);
2619     QCOMPARE(o.actuals().at(0), QVariant(10));
2620     QCOMPARE(o.actuals().at(1), QVariant(11));
2621
2622     o.reset();
2623     QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", v8::Undefined()));
2624     QCOMPARE(o.error(), false);
2625     QCOMPARE(o.invoked(), 18);
2626     QCOMPARE(o.actuals().count(), 1);
2627     QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2628
2629     o.reset();
2630     QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", v8::Undefined()));
2631     QCOMPARE(o.error(), false);
2632     QCOMPARE(o.invoked(), 19);
2633     QCOMPARE(o.actuals().count(), 1);
2634     QCOMPARE(o.actuals().at(0), QVariant(9));
2635
2636     o.reset();
2637     QVERIFY(EVALUATE_VALUE("object.method_default(10)", v8::Integer::New(19)));
2638     QCOMPARE(o.error(), false);
2639     QCOMPARE(o.invoked(), 20);
2640     QCOMPARE(o.actuals().count(), 2);
2641     QCOMPARE(o.actuals().at(0), QVariant(10));
2642     QCOMPARE(o.actuals().at(1), QVariant(19));
2643
2644     o.reset();
2645     QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", v8::Integer::New(13)));
2646     QCOMPARE(o.error(), false);
2647     QCOMPARE(o.invoked(), 20);
2648     QCOMPARE(o.actuals().count(), 2);
2649     QCOMPARE(o.actuals().at(0), QVariant(10));
2650     QCOMPARE(o.actuals().at(1), QVariant(13));
2651
2652     o.reset();
2653     QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", v8::Undefined()));
2654     QCOMPARE(o.error(), false);
2655     QCOMPARE(o.invoked(), -3);
2656     QCOMPARE(o.actuals().count(), 1);
2657     QCOMPARE(o.actuals().at(0), QVariant(9));
2658
2659     o.reset();
2660     QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", v8::Undefined()));
2661     QCOMPARE(o.error(), false);
2662     QCOMPARE(o.invoked(), 21);
2663     QCOMPARE(o.actuals().count(), 2);
2664     QCOMPARE(o.actuals().at(0), QVariant(9));
2665     QCOMPARE(o.actuals().at(1), QVariant());
2666
2667     o.reset();
2668     QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", v8::Undefined()));
2669     QCOMPARE(o.error(), false);
2670     QCOMPARE(o.invoked(), 21);
2671     QCOMPARE(o.actuals().count(), 2);
2672     QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2673     QCOMPARE(o.actuals().at(1), QVariant(QString("World")));
2674
2675     o.reset();
2676     QVERIFY(EVALUATE_VALUE("object.method_QJsonObject({foo:123})", v8::Undefined()));
2677     QCOMPARE(o.error(), false);
2678     QCOMPARE(o.invoked(), 22);
2679     QCOMPARE(o.actuals().count(), 1);
2680     QCOMPARE(qvariant_cast<QJsonObject>(o.actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2681
2682     o.reset();
2683     QVERIFY(EVALUATE_VALUE("object.method_QJsonArray([123])", v8::Undefined()));
2684     QCOMPARE(o.error(), false);
2685     QCOMPARE(o.invoked(), 23);
2686     QCOMPARE(o.actuals().count(), 1);
2687     QCOMPARE(qvariant_cast<QJsonArray>(o.actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2688
2689     o.reset();
2690     QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(123)", v8::Undefined()));
2691     QCOMPARE(o.error(), false);
2692     QCOMPARE(o.invoked(), 24);
2693     QCOMPARE(o.actuals().count(), 1);
2694     QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(123));
2695
2696     o.reset();
2697     QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(42.35)", v8::Undefined()));
2698     QCOMPARE(o.error(), false);
2699     QCOMPARE(o.invoked(), 24);
2700     QCOMPARE(o.actuals().count(), 1);
2701     QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(42.35));
2702
2703     o.reset();
2704     QVERIFY(EVALUATE_VALUE("object.method_QJsonValue('ciao')", v8::Undefined()));
2705     QCOMPARE(o.error(), false);
2706     QCOMPARE(o.invoked(), 24);
2707     QCOMPARE(o.actuals().count(), 1);
2708     QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QStringLiteral("ciao")));
2709
2710     o.reset();
2711     QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(true)", v8::Undefined()));
2712     QCOMPARE(o.error(), false);
2713     QCOMPARE(o.invoked(), 24);
2714     QCOMPARE(o.actuals().count(), 1);
2715     QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(true));
2716
2717     o.reset();
2718     QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(false)", v8::Undefined()));
2719     QCOMPARE(o.error(), false);
2720     QCOMPARE(o.invoked(), 24);
2721     QCOMPARE(o.actuals().count(), 1);
2722     QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(false));
2723
2724     o.reset();
2725     QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(null)", v8::Undefined()));
2726     QCOMPARE(o.error(), false);
2727     QCOMPARE(o.invoked(), 24);
2728     QCOMPARE(o.actuals().count(), 1);
2729     QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Null));
2730
2731     o.reset();
2732     QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(undefined)", v8::Undefined()));
2733     QCOMPARE(o.error(), false);
2734     QCOMPARE(o.invoked(), 24);
2735     QCOMPARE(o.actuals().count(), 1);
2736     QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2737
2738     o.reset();
2739     QVERIFY(EVALUATE_VALUE("object.method_overload({foo:123})", v8::Undefined()));
2740     QCOMPARE(o.error(), false);
2741     QCOMPARE(o.invoked(), 25);
2742     QCOMPARE(o.actuals().count(), 1);
2743     QCOMPARE(qvariant_cast<QJsonObject>(o.actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
2744
2745     o.reset();
2746     QVERIFY(EVALUATE_VALUE("object.method_overload([123])", v8::Undefined()));
2747     QCOMPARE(o.error(), false);
2748     QCOMPARE(o.invoked(), 26);
2749     QCOMPARE(o.actuals().count(), 1);
2750     QCOMPARE(qvariant_cast<QJsonArray>(o.actuals().at(0)), QJsonDocument::fromJson("[123]").array());
2751
2752     o.reset();
2753     QVERIFY(EVALUATE_VALUE("object.method_overload(null)", v8::Undefined()));
2754     QCOMPARE(o.error(), false);
2755     QCOMPARE(o.invoked(), 27);
2756     QCOMPARE(o.actuals().count(), 1);
2757     QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Null));
2758
2759     o.reset();
2760     QVERIFY(EVALUATE_VALUE("object.method_overload(undefined)", v8::Undefined()));
2761     QCOMPARE(o.error(), false);
2762     QCOMPARE(o.invoked(), 27);
2763     QCOMPARE(o.actuals().count(), 1);
2764     QCOMPARE(qvariant_cast<QJsonValue>(o.actuals().at(0)), QJsonValue(QJsonValue::Undefined));
2765
2766     o.reset();
2767     QVERIFY(EVALUATE_ERROR("object.method_unknown(null)"));
2768     QCOMPARE(o.error(), false);
2769     QCOMPARE(o.invoked(), -1);
2770     QCOMPARE(o.actuals().count(), 0);
2771 }
2772
2773 // QTBUG-13047 (check that you can pass registered object types as args)
2774 void tst_qqmlecmascript::invokableObjectArg()
2775 {
2776     QQmlComponent component(&engine, testFileUrl("invokableObjectArg.qml"));
2777
2778     QObject *o = component.create();
2779     QVERIFY(o);
2780     MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o);
2781     QVERIFY(qmlobject);
2782     QCOMPARE(qmlobject->myinvokableObject, qmlobject);
2783
2784     delete o;
2785 }
2786
2787 // QTBUG-13047 (check that you can return registered object types from methods)
2788 void tst_qqmlecmascript::invokableObjectRet()
2789 {
2790     QQmlComponent component(&engine, testFileUrl("invokableObjectRet.qml"));
2791
2792     QObject *o = component.create();
2793     QVERIFY(o);
2794     QCOMPARE(o->property("test").toBool(), true);
2795     delete o;
2796 }
2797
2798 void tst_qqmlecmascript::invokableEnumRet()
2799 {
2800     QQmlComponent component(&engine, testFileUrl("invokableEnumRet.qml"));
2801
2802     QObject *o = component.create();
2803     QVERIFY(o);
2804     QCOMPARE(o->property("test").toBool(), true);
2805     delete o;
2806 }
2807
2808 // QTBUG-5675
2809 void tst_qqmlecmascript::listToVariant()
2810 {
2811     QQmlComponent component(&engine, testFileUrl("listToVariant.qml"));
2812
2813     MyQmlContainer container;
2814
2815     QQmlContext context(engine.rootContext());
2816     context.setContextObject(&container);
2817
2818     QObject *object = component.create(&context);
2819     QVERIFY(object != 0);
2820
2821     QVariant v = object->property("test");
2822     QCOMPARE(v.userType(), qMetaTypeId<QQmlListReference>());
2823     QVERIFY(qvariant_cast<QQmlListReference>(v).object() == &container);
2824
2825     delete object;
2826 }
2827
2828 // QTBUG-16316
2829 Q_DECLARE_METATYPE(QQmlListProperty<MyQmlObject>)
2830 void tst_qqmlecmascript::listAssignment()
2831 {
2832     QQmlComponent component(&engine, testFileUrl("listAssignment.qml"));
2833     QObject *obj = component.create();
2834     QCOMPARE(obj->property("list1length").toInt(), 2);
2835     QQmlListProperty<MyQmlObject> list1 = obj->property("list1").value<QQmlListProperty<MyQmlObject> >();
2836     QQmlListProperty<MyQmlObject> list2 = obj->property("list2").value<QQmlListProperty<MyQmlObject> >();
2837     QCOMPARE(list1.count(&list1), list2.count(&list2));
2838     QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0));
2839     QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1));
2840     delete obj;
2841 }
2842
2843 // QTBUG-7957
2844 void tst_qqmlecmascript::multiEngineObject()
2845 {
2846     MyQmlObject obj;
2847     obj.setStringProperty("Howdy planet");
2848
2849     QQmlEngine e1;
2850     e1.rootContext()->setContextProperty("thing", &obj);
2851     QQmlComponent c1(&e1, testFileUrl("multiEngineObject.qml"));
2852
2853     QQmlEngine e2;
2854     e2.rootContext()->setContextProperty("thing", &obj);
2855     QQmlComponent c2(&e2, testFileUrl("multiEngineObject.qml"));
2856
2857     QObject *o1 = c1.create();
2858     QObject *o2 = c2.create();
2859
2860     QCOMPARE(o1->property("test").toString(), QString("Howdy planet"));
2861     QCOMPARE(o2->property("test").toString(), QString("Howdy planet"));
2862
2863     delete o2;
2864     delete o1;
2865 }
2866
2867 // Test that references to QObjects are cleanup when the object is destroyed
2868 void tst_qqmlecmascript::deletedObject()
2869 {
2870     QQmlComponent component(&engine, testFileUrl("deletedObject.qml"));
2871
2872     QObject *object = component.create();
2873
2874     QCOMPARE(object->property("test1").toBool(), true);
2875     QCOMPARE(object->property("test2").toBool(), true);
2876     QCOMPARE(object->property("test3").toBool(), true);
2877     QCOMPARE(object->property("test4").toBool(), true);
2878
2879     delete object;
2880 }
2881
2882 void tst_qqmlecmascript::attachedPropertyScope()
2883 {
2884     QQmlComponent component(&engine, testFileUrl("attachedPropertyScope.qml"));
2885
2886     QObject *object = component.create();
2887     QVERIFY(object != 0);
2888
2889     MyQmlAttachedObject *attached = 
2890         qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
2891     QVERIFY(attached != 0);
2892
2893     QCOMPARE(object->property("value2").toInt(), 0);
2894
2895     attached->emitMySignal();
2896
2897     QCOMPARE(object->property("value2").toInt(), 9);
2898
2899     delete object;
2900 }
2901
2902 void tst_qqmlecmascript::scriptConnect()
2903 {
2904     {
2905         QQmlComponent component(&engine, testFileUrl("scriptConnect.1.qml"));
2906
2907         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2908         QVERIFY(object != 0);
2909
2910         QCOMPARE(object->property("test").toBool(), false);
2911         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2912         QCOMPARE(object->property("test").toBool(), true);
2913
2914         delete object;
2915     }
2916
2917     {
2918         QQmlComponent component(&engine, testFileUrl("scriptConnect.2.qml"));
2919
2920         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2921         QVERIFY(object != 0);
2922
2923         QCOMPARE(object->property("test").toBool(), false);
2924         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2925         QCOMPARE(object->property("test").toBool(), true);
2926
2927         delete object;
2928     }
2929
2930     {
2931         QQmlComponent component(&engine, testFileUrl("scriptConnect.3.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.4.qml"));
2945
2946         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2947         QVERIFY(object != 0);
2948
2949         QCOMPARE(object->methodCalled(), false);
2950         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2951         QCOMPARE(object->methodCalled(), true);
2952
2953         delete object;
2954     }
2955
2956     {
2957         QQmlComponent component(&engine, testFileUrl("scriptConnect.5.qml"));
2958
2959         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2960         QVERIFY(object != 0);
2961
2962         QCOMPARE(object->methodCalled(), false);
2963         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2964         QCOMPARE(object->methodCalled(), true);
2965
2966         delete object;
2967     }
2968
2969     {
2970         QQmlComponent component(&engine, testFileUrl("scriptConnect.6.qml"));
2971
2972         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2973         QVERIFY(object != 0);
2974
2975         QCOMPARE(object->property("test").toInt(), 0);
2976         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2977         QCOMPARE(object->property("test").toInt(), 2);
2978
2979         delete object;
2980     }
2981 }
2982
2983 void tst_qqmlecmascript::scriptDisconnect()
2984 {
2985     {
2986         QQmlComponent component(&engine, testFileUrl("scriptDisconnect.1.qml"));
2987
2988         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2989         QVERIFY(object != 0);
2990
2991         QCOMPARE(object->property("test").toInt(), 0);
2992         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2993         QCOMPARE(object->property("test").toInt(), 1);
2994         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2995         QCOMPARE(object->property("test").toInt(), 2);
2996         emit object->basicSignal();
2997         QCOMPARE(object->property("test").toInt(), 2);
2998         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2999         QCOMPARE(object->property("test").toInt(), 2);
3000
3001         delete object;
3002     }
3003
3004     {
3005         QQmlComponent component(&engine, testFileUrl("scriptDisconnect.2.qml"));
3006
3007         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3008         QVERIFY(object != 0);
3009
3010         QCOMPARE(object->property("test").toInt(), 0);
3011         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3012         QCOMPARE(object->property("test").toInt(), 1);
3013         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3014         QCOMPARE(object->property("test").toInt(), 2);
3015         emit object->basicSignal();
3016         QCOMPARE(object->property("test").toInt(), 2);
3017         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3018         QCOMPARE(object->property("test").toInt(), 2);
3019
3020         delete object;
3021     }
3022
3023     {
3024         QQmlComponent component(&engine, testFileUrl("scriptDisconnect.3.qml"));
3025
3026         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3027         QVERIFY(object != 0);
3028
3029         QCOMPARE(object->property("test").toInt(), 0);
3030         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3031         QCOMPARE(object->property("test").toInt(), 1);
3032         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3033         QCOMPARE(object->property("test").toInt(), 2);
3034         emit object->basicSignal();
3035         QCOMPARE(object->property("test").toInt(), 2);
3036         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3037         QCOMPARE(object->property("test").toInt(), 3);
3038
3039         delete object;
3040     }
3041     {
3042         QQmlComponent component(&engine, testFileUrl("scriptDisconnect.4.qml"));
3043
3044         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3045         QVERIFY(object != 0);
3046
3047         QCOMPARE(object->property("test").toInt(), 0);
3048         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3049         QCOMPARE(object->property("test").toInt(), 1);
3050         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3051         QCOMPARE(object->property("test").toInt(), 2);
3052         emit object->basicSignal();
3053         QCOMPARE(object->property("test").toInt(), 2);
3054         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
3055         QCOMPARE(object->property("test").toInt(), 3);
3056
3057         delete object;
3058     }
3059 }
3060
3061 class OwnershipObject : public QObject
3062 {
3063     Q_OBJECT
3064 public:
3065     OwnershipObject() { object = new QObject; }
3066
3067     QPointer<QObject> object;
3068
3069 public slots:
3070     QObject *getObject() { return object; }
3071 };
3072
3073 void tst_qqmlecmascript::ownership()
3074 {
3075     OwnershipObject own;
3076     QQmlContext *context = new QQmlContext(engine.rootContext());
3077     context->setContextObject(&own);
3078
3079     {
3080         QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3081
3082         QVERIFY(own.object != 0);
3083
3084         QObject *object = component.create(context);
3085
3086         engine.collectGarbage();
3087
3088         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3089         QCoreApplication::processEvents();
3090
3091         QVERIFY(own.object == 0);
3092
3093         delete object;
3094     }
3095
3096     own.object = new QObject(&own);
3097
3098     {
3099         QQmlComponent component(&engine, testFileUrl("ownership.qml"));
3100
3101         QVERIFY(own.object != 0);
3102
3103         QObject *object = component.create(context);
3104         
3105         engine.collectGarbage();
3106
3107         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3108         QCoreApplication::processEvents();
3109
3110         QVERIFY(own.object != 0);
3111
3112         delete object;
3113     }
3114
3115     delete context;
3116 }
3117
3118 class CppOwnershipReturnValue : public QObject
3119 {
3120     Q_OBJECT
3121 public:
3122     CppOwnershipReturnValue() : value(0) {}
3123     ~CppOwnershipReturnValue() { delete value; }
3124
3125     Q_INVOKABLE QObject *create() {
3126         value = new QObject;
3127         QQmlEngine::setObjectOwnership(value, QQmlEngine::CppOwnership);
3128         return value;
3129     }
3130
3131     Q_INVOKABLE MyQmlObject *createQmlObject() {
3132         MyQmlObject *rv = new MyQmlObject;
3133         value = rv;
3134         return rv;
3135     }
3136
3137     QPointer<QObject> value;
3138 };
3139
3140 // QTBUG-15695.  
3141 // Test setObjectOwnership(CppOwnership) works even when there is no QQmlData
3142 void tst_qqmlecmascript::cppOwnershipReturnValue()
3143 {
3144     CppOwnershipReturnValue source;
3145
3146     {
3147     QQmlEngine engine;
3148     engine.rootContext()->setContextProperty("source", &source);
3149
3150     QVERIFY(source.value == 0);
3151
3152     QQmlComponent component(&engine);
3153     component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl());
3154
3155     QObject *object = component.create();
3156
3157     QVERIFY(object != 0);
3158     QVERIFY(source.value != 0);
3159
3160     delete object;
3161     }
3162
3163     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3164     QCoreApplication::processEvents();
3165
3166     QVERIFY(source.value != 0);
3167 }
3168
3169 // QTBUG-15697
3170 void tst_qqmlecmascript::ownershipCustomReturnValue()
3171 {
3172     CppOwnershipReturnValue source;
3173
3174     {
3175     QQmlEngine engine;
3176     engine.rootContext()->setContextProperty("source", &source);
3177
3178     QVERIFY(source.value == 0);
3179
3180     QQmlComponent component(&engine);
3181     component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl());
3182
3183     QObject *object = component.create();
3184
3185     QVERIFY(object != 0);
3186     QVERIFY(source.value != 0);
3187
3188     delete object;
3189     }
3190
3191     engine.collectGarbage();
3192     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3193     QCoreApplication::processEvents();
3194
3195     QVERIFY(source.value == 0);
3196 }
3197
3198 //the return value from getObject will be JS ownership,
3199 //unless strong Cpp ownership has been set
3200 class OwnershipChangingObject : public QObject
3201 {
3202     Q_OBJECT
3203 public:
3204     OwnershipChangingObject(): object(0) { }
3205
3206     QPointer<QObject> object;
3207
3208 public slots:
3209     QObject *getObject() { return object; }
3210     void setObject(QObject *obj) { object = obj; }
3211 };
3212
3213 void tst_qqmlecmascript::ownershipRootObject()
3214 {
3215     OwnershipChangingObject own;
3216     QQmlContext *context = new QQmlContext(engine.rootContext());
3217     context->setContextObject(&own);
3218
3219     QQmlComponent component(&engine, testFileUrl("ownershipRootObject.qml"));
3220     QQmlGuard<QObject> object = component.create(context);
3221     QVERIFY(object);
3222
3223     engine.collectGarbage();
3224
3225     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3226     QCoreApplication::processEvents();
3227
3228     QVERIFY(own.object != 0);
3229
3230     delete context;
3231     delete object;
3232 }
3233
3234 void tst_qqmlecmascript::ownershipConsistency()
3235 {
3236     OwnershipChangingObject own;
3237     QQmlContext *context = new QQmlContext(engine.rootContext());
3238     context->setContextObject(&own);
3239
3240     QString expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":19: Error: Invalid attempt to destroy() an indestructible object");
3241     QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3242     expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":15: Error: Invalid attempt to destroy() an indestructible object");
3243     QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3244     expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":6: Error: Invalid attempt to destroy() an indestructible object");
3245     QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3246     expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":10: Error: Invalid attempt to destroy() an indestructible object");
3247     QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3248
3249     QQmlComponent component(&engine, testFileUrl("ownershipConsistency.qml"));
3250     QQmlGuard<QObject> object = component.create(context);
3251     QVERIFY(object);
3252
3253     engine.collectGarbage();
3254
3255     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3256     QCoreApplication::processEvents();
3257
3258     QVERIFY(own.object != 0);
3259
3260     delete context;
3261     delete object;
3262 }
3263
3264 void tst_qqmlecmascript::ownershipQmlIncubated()
3265 {
3266     QQmlComponent component(&engine, testFileUrl("ownershipQmlIncubated.qml"));
3267     QObject *object = component.create();
3268     QVERIFY(object);
3269
3270     QTRY_VERIFY(object->property("incubatedItem").value<QObject*>() != 0);
3271
3272     QMetaObject::invokeMethod(object, "deleteIncubatedItem");
3273
3274     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3275     QCoreApplication::processEvents();
3276
3277     QVERIFY(object->property("incubatedItem").value<QObject*>() == 0);
3278
3279     delete object;
3280 }
3281
3282 class QListQObjectMethodsObject : public QObject
3283 {
3284     Q_OBJECT
3285 public:
3286     QListQObjectMethodsObject() {
3287         m_objects.append(new MyQmlObject());
3288         m_objects.append(new MyQmlObject());
3289     }
3290
3291     ~QListQObjectMethodsObject() {
3292         qDeleteAll(m_objects);
3293     }
3294
3295 public slots:
3296     QList<QObject *> getObjects() { return m_objects; }
3297
3298 private:
3299     QList<QObject *> m_objects;
3300 };
3301
3302 // Tests that returning a QList<QObject*> from a method works
3303 void tst_qqmlecmascript::qlistqobjectMethods()
3304 {
3305     QListQObjectMethodsObject obj;
3306     QQmlContext *context = new QQmlContext(engine.rootContext());
3307     context->setContextObject(&obj);
3308
3309     QQmlComponent component(&engine, testFileUrl("qlistqobjectMethods.qml"));
3310
3311     QObject *object = component.create(context);
3312
3313     QCOMPARE(object->property("test").toInt(), 2);
3314     QCOMPARE(object->property("test2").toBool(), true);
3315
3316     delete object;
3317     delete context;
3318 }
3319
3320 // QTBUG-9205
3321 void tst_qqmlecmascript::strictlyEquals()
3322 {
3323     QQmlComponent component(&engine, testFileUrl("strictlyEquals.qml"));
3324
3325     QObject *object = component.create();
3326     QVERIFY(object != 0);
3327
3328     QCOMPARE(object->property("test1").toBool(), true);
3329     QCOMPARE(object->property("test2").toBool(), true);
3330     QCOMPARE(object->property("test3").toBool(), true);
3331     QCOMPARE(object->property("test4").toBool(), true);
3332     QCOMPARE(object->property("test5").toBool(), true);
3333     QCOMPARE(object->property("test6").toBool(), true);
3334     QCOMPARE(object->property("test7").toBool(), true);
3335     QCOMPARE(object->property("test8").toBool(), true);
3336
3337     delete object;
3338 }
3339
3340 void tst_qqmlecmascript::compiled()
3341 {
3342     QQmlComponent component(&engine, testFileUrl("compiled.qml"));
3343
3344     QObject *object = component.create();
3345     QVERIFY(object != 0);
3346
3347     QCOMPARE(object->property("test1").toReal(), qreal(15.7));
3348     QCOMPARE(object->property("test2").toReal(), qreal(-6.7));
3349     QCOMPARE(object->property("test3").toBool(), true);
3350     QCOMPARE(object->property("test4").toBool(), false);
3351     QCOMPARE(object->property("test5").toBool(), false);
3352     QCOMPARE(object->property("test6").toBool(), true);
3353
3354     QCOMPARE(object->property("test7").toInt(), 185);
3355     QCOMPARE(object->property("test8").toInt(), 167);
3356     QCOMPARE(object->property("test9").toBool(), true);
3357     QCOMPARE(object->property("test10").toBool(), false);
3358     QCOMPARE(object->property("test11").toBool(), false);
3359     QCOMPARE(object->property("test12").toBool(), true);
3360
3361     QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld"));
3362     QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World"));
3363     QCOMPARE(object->property("test15").toBool(), false);
3364     QCOMPARE(object->property("test16").toBool(), true);
3365
3366     QCOMPARE(object->property("test17").toInt(), 5);
3367     QCOMPARE(object->property("test18").toReal(), qreal(176));
3368     QCOMPARE(object->property("test19").toInt(), 7);
3369     QCOMPARE(object->property("test20").toReal(), qreal(6.7));
3370     QCOMPARE(object->property("test21").toString(), QLatin1String("6.7"));
3371     QCOMPARE(object->property("test22").toString(), QLatin1String("!"));
3372     QCOMPARE(object->property("test23").toBool(), true);
3373     QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33));
3374     QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA));
3375
3376     delete object;
3377 }
3378
3379 // Test that numbers assigned in bindings as strings work consistently
3380 void tst_qqmlecmascript::numberAssignment()
3381 {
3382     QQmlComponent component(&engine, testFileUrl("numberAssignment.qml"));
3383
3384     QObject *object = component.create();
3385     QVERIFY(object != 0);
3386
3387     QCOMPARE(object->property("test1"), QVariant((qreal)6.7));
3388     QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3389     QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3390     QCOMPARE(object->property("test3"), QVariant((qreal)6));
3391     QCOMPARE(object->property("test4"), QVariant((qreal)6));
3392
3393     QCOMPARE(object->property("test5"), QVariant((int)7));
3394     QCOMPARE(object->property("test6"), QVariant((int)7));
3395     QCOMPARE(object->property("test7"), QVariant((int)6));
3396     QCOMPARE(object->property("test8"), QVariant((int)6));
3397
3398     QCOMPARE(object->property("test9"), QVariant((unsigned int)7));
3399     QCOMPARE(object->property("test10"), QVariant((unsigned int)7));
3400     QCOMPARE(object->property("test11"), QVariant((unsigned int)6));
3401     QCOMPARE(object->property("test12"), QVariant((unsigned int)6));
3402
3403     delete object;
3404 }
3405
3406 void tst_qqmlecmascript::propertySplicing()
3407 {
3408     QQmlComponent component(&engine, testFileUrl("propertySplicing.qml"));
3409
3410     QObject *object = component.create();
3411     QVERIFY(object != 0);
3412
3413     QCOMPARE(object->property("test").toBool(), true);
3414
3415     delete object;
3416 }
3417
3418 // QTBUG-16683
3419 void tst_qqmlecmascript::signalWithUnknownTypes()
3420 {
3421     QQmlComponent component(&engine, testFileUrl("signalWithUnknownTypes.qml"));
3422
3423     MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
3424     QVERIFY(object != 0);
3425
3426     MyQmlObject::MyType type;
3427     type.value = 0x8971123;
3428     emit object->signalWithUnknownType(type);
3429
3430     MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant());
3431
3432     QCOMPARE(result.value, type.value);
3433
3434
3435     delete object;
3436 }
3437
3438 void tst_qqmlecmascript::signalWithJSValueInVariant_data()
3439 {
3440     QTest::addColumn<QString>("expression");
3441     QTest::addColumn<QString>("compare");
3442
3443     QString compareStrict("(function(a, b) { return a === b; })");
3444     QTest::newRow("true") << "true" << compareStrict;
3445     QTest::newRow("undefined") << "undefined" << compareStrict;
3446     QTest::newRow("null") << "null" << compareStrict;
3447     QTest::newRow("123") << "123" << compareStrict;
3448     QTest::newRow("'ciao'") << "'ciao'" << compareStrict;
3449
3450     QString comparePropertiesStrict(
3451         "(function(a, b) {"
3452         "  if (typeof b != 'object')"
3453         "    return a === b;"
3454         "  var props = Object.getOwnPropertyNames(b);"
3455         "  for (var i = 0; i < props.length; ++i) {"
3456         "    var p = props[i];"
3457         "    return arguments.callee(a[p], b[p]);"
3458         "  }"
3459         "})");
3460     QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })"  << comparePropertiesStrict;
3461     QTest::newRow("[10,20,30]") << "[10,20,30]"  << comparePropertiesStrict;
3462 }
3463
3464 void tst_qqmlecmascript::signalWithJSValueInVariant()
3465 {
3466     QFETCH(QString, expression);
3467     QFETCH(QString, compare);
3468
3469     QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3470     QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3471     QVERIFY(object != 0);
3472
3473     QJSValue value = engine.evaluate(expression);
3474     QVERIFY(!value.isError());
3475     object->setProperty("expression", expression);
3476     object->setProperty("compare", compare);
3477     object->setProperty("pass", false);
3478
3479     emit object->signalWithVariant(QVariant::fromValue(value));
3480     QVERIFY(object->property("pass").toBool());
3481 }
3482
3483 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines_data()
3484 {
3485     signalWithJSValueInVariant_data();
3486 }
3487
3488 void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines()
3489 {
3490     QFETCH(QString, expression);
3491     QFETCH(QString, compare);
3492
3493     QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
3494     QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3495     QVERIFY(object != 0);
3496
3497     QJSEngine engine2;
3498     QJSValue value = engine2.evaluate(expression);
3499     QVERIFY(!value.isError());
3500     object->setProperty("expression", expression);
3501     object->setProperty("compare", compare);
3502     object->setProperty("pass", false);
3503
3504     QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine.");
3505     emit object->signalWithVariant(QVariant::fromValue(value));
3506     QVERIFY(!object->property("pass").toBool());
3507 }
3508
3509 void tst_qqmlecmascript::signalWithQJSValue_data()
3510 {
3511     signalWithJSValueInVariant_data();
3512 }
3513
3514 void tst_qqmlecmascript::signalWithQJSValue()
3515 {
3516     QFETCH(QString, expression);
3517     QFETCH(QString, compare);
3518
3519     QQmlComponent component(&engine, testFileUrl("signalWithQJSValue.qml"));
3520     QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
3521     QVERIFY(object != 0);
3522
3523     QJSValue value = engine.evaluate(expression);
3524     QVERIFY(!value.isError());
3525     object->setProperty("expression", expression);
3526     object->setProperty("compare", compare);
3527     object->setProperty("pass", false);
3528
3529     emit object->signalWithQJSValue(value);
3530
3531     QVERIFY(object->property("pass").toBool());
3532     QVERIFY(object->qjsvalue().strictlyEquals(value));
3533 }
3534
3535 void tst_qqmlecmascript::singletonType_data()
3536 {
3537     QTest::addColumn<QUrl>("testfile");
3538     QTest::addColumn<QString>("errorMessage");
3539     QTest::addColumn<QStringList>("warningMessages");
3540     QTest::addColumn<QStringList>("readProperties");
3541     QTest::addColumn<QVariantList>("readExpectedValues");
3542     QTest::addColumn<QStringList>("writeProperties");
3543     QTest::addColumn<QVariantList>("writeValues");
3544     QTest::addColumn<QStringList>("readBackProperties");
3545     QTest::addColumn<QVariantList>("readBackExpectedValues");
3546
3547     QTest::newRow("qobject, register + read + method [no qualifier]")
3548             << testFileUrl("singletontype/qobjectSingletonTypeNoQualifier.qml")
3549             << QString()
3550             << QStringList()
3551             << (QStringList() << "qobjectPropertyTest" << "qobjectMethodTest")
3552             << (QVariantList() << 20 << 1)
3553             << QStringList()
3554             << QVariantList()
3555             << QStringList()
3556             << QVariantList();
3557
3558     QTest::newRow("script, register + read [no qualifier]")
3559             << testFileUrl("singletontype/scriptSingletonTypeNoQualifier.qml")
3560             << QString()
3561             << QStringList()
3562             << (QStringList() << "scriptTest")
3563             << (QVariantList() << 13)
3564             << QStringList()
3565             << QVariantList()
3566             << QStringList()
3567             << QVariantList();
3568
3569     QTest::newRow("qobject, register + read + method")
3570             << testFileUrl("singletontype/qobjectSingletonType.qml")
3571             << QString()
3572             << QStringList()
3573             << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
3574                    << "qobjectMinorVersionTest" << "qobjectMajorVersionTest" << "qobjectParentedTest")
3575             << (QVariantList() << 20 << 20 << 2 << 20 << 20 << 26)
3576             << QStringList()
3577             << QVariantList()
3578             << QStringList()
3579             << QVariantList();
3580
3581     QTest::newRow("script, register + read")
3582             << testFileUrl("singletontype/scriptSingletonType.qml")
3583             << QString()
3584             << QStringList()
3585             << (QStringList() << "scriptTest")
3586             << (QVariantList() << 13)
3587             << QStringList()
3588             << QVariantList()
3589             << QStringList()
3590             << QVariantList();
3591
3592     QTest::newRow("qobject, caching + read")
3593             << testFileUrl("singletontype/qobjectSingletonTypeCaching.qml")
3594             << QString()
3595             << QStringList()
3596             << (QStringList() << "existingUriTest" << "qobjectParentedTest")
3597             << (QVariantList() << 20 << 26) // 26, shouldn't have incremented to 27.
3598             << QStringList()
3599             << QVariantList()
3600             << QStringList()
3601             << QVariantList();
3602
3603     QTest::newRow("script, caching + read")
3604             << testFileUrl("singletontype/scriptSingletonTypeCaching.qml")
3605             << QString()
3606             << QStringList()
3607             << (QStringList() << "scriptTest")
3608             << (QVariantList() << 13) // 13, shouldn't have incremented to 14.
3609             << QStringList()
3610             << QVariantList()
3611             << QStringList()
3612             << QVariantList();
3613
3614     QTest::newRow("qobject, writing + readonly constraints")
3615             << testFileUrl("singletontype/qobjectSingletonTypeWriting.qml")
3616             << QString()
3617             << (QStringList() << QString(testFileUrl("singletontype/qobjectSingletonTypeWriting.qml").toString() + QLatin1String(":15: Error: Cannot assign to read-only property \"qobjectTestProperty\"")))
3618             << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3619             << (QVariantList() << 20 << 50 << 10)
3620             << (QStringList() << "firstProperty" << "secondProperty")
3621             << (QVariantList() << 30 << 30)
3622             << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
3623             << (QVariantList() << 20 << 30 << 30);
3624
3625     QTest::newRow("script, writing + readonly constraints")
3626             << testFileUrl("singletontype/scriptSingletonTypeWriting.qml")
3627             << QString()
3628             << (QStringList() << QString(testFileUrl("singletontype/scriptSingletonTypeWriting.qml").toString() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\"")))
3629             << (QStringList() << "readBack" << "unchanged")
3630             << (QVariantList() << 13 << 42)
3631             << (QStringList() << "firstProperty" << "secondProperty")
3632             << (QVariantList() << 30 << 30)
3633             << (QStringList() << "readBack" << "unchanged")
3634             << (QVariantList() << 30 << 42);
3635
3636     QTest::newRow("qobject singleton Type enum values in JS")
3637             << testFileUrl("singletontype/qobjectSingletonTypeEnums.qml")
3638             << QString()
3639             << QStringList()
3640             << (QStringList() << "enumValue" << "enumMethod")
3641             << (QVariantList() << 42 << 30)
3642             << QStringList()
3643             << QVariantList()
3644             << QStringList()
3645             << QVariantList();
3646
3647     QTest::newRow("qobject, invalid major version fail")
3648             << testFileUrl("singletontype/singletonTypeMajorVersionFail.qml")
3649             << QString("QQmlComponent: Component is not ready")
3650             << QStringList()
3651             << QStringList()
3652             << QVariantList()
3653             << QStringList()
3654             << QVariantList()
3655             << QStringList()
3656             << QVariantList();
3657
3658     QTest::newRow("qobject, invalid minor version fail")
3659             << testFileUrl("singletontype/singletonTypeMinorVersionFail.qml")
3660             << QString("QQmlComponent: Component is not ready")
3661             << QStringList()
3662             << QStringList()
3663             << QVariantList()
3664             << QStringList()
3665             << QVariantList()
3666             << QStringList()
3667             << QVariantList();
3668 }
3669
3670 void tst_qqmlecmascript::singletonType()
3671 {
3672     QFETCH(QUrl, testfile);
3673     QFETCH(QString, errorMessage);
3674     QFETCH(QStringList, warningMessages);
3675     QFETCH(QStringList, readProperties);
3676     QFETCH(QVariantList, readExpectedValues);
3677     QFETCH(QStringList, writeProperties);
3678     QFETCH(QVariantList, writeValues);
3679     QFETCH(QStringList, readBackProperties);
3680     QFETCH(QVariantList, readBackExpectedValues);
3681
3682     QQmlComponent component(&engine, testfile);
3683
3684     if (!errorMessage.isEmpty())
3685         QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3686
3687     if (warningMessages.size())
3688         foreach (const QString &warning, warningMessages)
3689             QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3690
3691     QObject *object = component.create();
3692     if (!errorMessage.isEmpty()) {
3693         QVERIFY(object == 0);
3694     } else {
3695         QVERIFY(object != 0);
3696         for (int i = 0; i < readProperties.size(); ++i)
3697             QCOMPARE(object->property(readProperties.at(i).toLatin1().constData()), readExpectedValues.at(i));
3698         for (int i = 0; i < writeProperties.size(); ++i)
3699             QVERIFY(object->setProperty(writeProperties.at(i).toLatin1().constData(), writeValues.at(i)));
3700         for (int i = 0; i < readBackProperties.size(); ++i)
3701             QCOMPARE(object->property(readBackProperties.at(i).toLatin1().constData()), readBackExpectedValues.at(i));
3702         delete object;
3703     }
3704 }
3705
3706 void tst_qqmlecmascript::singletonTypeImportOrder()
3707 {
3708     QQmlComponent component(&engine, testFileUrl("singletontype/singletonTypeImportOrder.qml"));
3709     QObject *object = component.create();
3710     QVERIFY(object);
3711     QVERIFY(object->property("v") == 1);
3712     delete object;
3713 }
3714
3715 void tst_qqmlecmascript::singletonTypeResolution()
3716 {
3717     QQmlComponent component(&engine, testFileUrl("singletontype/singletonTypeResolution.qml"));
3718     QObject *object = component.create();
3719     QVERIFY(object);
3720     QVERIFY(object->property("success") == true);
3721     delete object;
3722 }
3723
3724 void tst_qqmlecmascript::singletonTypeConflicts1()
3725 {
3726     const char *warning = "Cannot register singleton type TypeName in uri Test.Conflict1 1.5 (a conflicting singleton type already exists)";
3727     QTest::ignoreMessage(QtWarningMsg, warning);
3728
3729     int i0 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict1", 1, 5, "TypeName", 0);
3730     QVERIFY(i0 != -1);
3731
3732     int i1 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict1", 2, 0, "TypeName", 0);
3733     QVERIFY(i1 != -1);
3734
3735     int i2 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict1", 1, 5, "TypeName", 0);
3736     QVERIFY(i2 == -1);
3737
3738     int i3 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict1", 1, 2, "TypeName", 0);
3739     QVERIFY(i3 != -1);
3740
3741     int i4 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict1", 1, 8, "TypeName", 0);
3742     QVERIFY(i4 != -1);
3743 }
3744
3745 void tst_qqmlecmascript::singletonTypeConflicts2()
3746 {
3747     int i0 = qmlRegisterType<MyQmlObject>("Test.Conflict2", 1, 5, "TypeName");
3748     QVERIFY(i0 != -1);
3749
3750     int i2 = qmlRegisterType<MyQmlObject>("Test.Conflict2", 1, 8, "TypeName");
3751     QVERIFY(i2 != -1);
3752
3753     int i3 = qmlRegisterType<MyQmlObject>("Test.Conflict2", 2, 0, "TypeName");
3754     QVERIFY(i3 != -1);
3755
3756     int i4 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict2", 1, 0, "TypeName", 0);
3757     QVERIFY(i4 != -1);
3758
3759     const char *warning2 = "Cannot register singleton type TypeName in uri Test.Conflict2 1.9 (a conflicting type already exists)";
3760     QTest::ignoreMessage(QtWarningMsg, warning2);
3761
3762     int i5 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict2", 1, 9, "TypeName", 0);
3763     QVERIFY(i5 == -1);
3764 }
3765
3766 void tst_qqmlecmascript::singletonTypeConflicts3()
3767 {
3768     int i0 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict3", 1, 0, "TypeName", 0);
3769     QVERIFY(i0 != -1);
3770
3771     int i1 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict3", 1, 5, "TypeName", 0);
3772     QVERIFY(i1 != -1);
3773
3774     int i2 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict3", 1, 8, "TypeName", 0);
3775     QVERIFY(i2 != -1);
3776
3777     int i3 = qmlRegisterSingletonType<testImportOrderApi>("Test.Conflict3", 2, 0, "TypeName", 0);
3778     QVERIFY(i3 != -1);
3779
3780     const char *warning = "Cannot register type TypeName in uri Test.Conflict3 1.0 (a conflicting singleton type already exists)";
3781     QTest::ignoreMessage(QtWarningMsg, warning);
3782
3783     int i4 = qmlRegisterType<MyQmlObject>("Test.Conflict3", 1, 0, "TypeName");
3784     QVERIFY(i4 == -1);
3785
3786     int i5 = qmlRegisterType<MyQmlObject>("Test.Conflict3", 1, 3, "TypeName");
3787     QVERIFY(i5 != -1);
3788 }
3789
3790 void tst_qqmlecmascript::importScripts_data()
3791 {
3792     QTest::addColumn<QUrl>("testfile");
3793     QTest::addColumn<QString>("errorMessage");
3794     QTest::addColumn<QStringList>("warningMessages");
3795     QTest::addColumn<QStringList>("propertyNames");
3796     QTest::addColumn<QVariantList>("propertyValues");
3797
3798     QTest::newRow("basic functionality")
3799             << testFileUrl("jsimport/testImport.qml")
3800             << QString()
3801             << QStringList()
3802             << (QStringList() << QLatin1String("importedScriptStringValue")
3803                               << QLatin1String("importedScriptFunctionValue")
3804                               << QLatin1String("importedModuleAttachedPropertyValue")
3805                               << QLatin1String("importedModuleEnumValue"))
3806             << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3807                                << QVariant(20)
3808                                << QVariant(19)
3809                                << QVariant(2));
3810
3811     QTest::newRow("import scoping")
3812             << testFileUrl("jsimport/testImportScoping.qml")
3813             << QString()
3814             << QStringList()
3815             << (QStringList() << QLatin1String("componentError"))
3816             << (QVariantList() << QVariant(5));
3817
3818     QTest::newRow("parent scope shouldn't be inherited by import with imports")
3819             << testFileUrl("jsimportfail/failOne.qml")
3820             << QString()
3821             << (QStringList() << QString(testFileUrl("jsimportfail/failOne.qml").toString() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
3822             << (QStringList() << QLatin1String("importScriptFunctionValue"))
3823             << (QVariantList() << QVariant(QString()));
3824
3825     QTest::newRow("javascript imports in an import should be private to the import scope")
3826             << testFileUrl("jsimportfail/failTwo.qml")
3827             << QString()
3828             << (QStringList() << QString(testFileUrl("jsimportfail/failTwo.qml").toString() + QLatin1String(":6: ReferenceError: ImportOneJs is not defined")))
3829             << (QStringList() << QLatin1String("importScriptFunctionValue"))
3830             << (QVariantList() << QVariant(QString()));
3831
3832     QTest::newRow("module imports in an import should be private to the import scope")
3833             << testFileUrl("jsimportfail/failThree.qml")
3834             << QString()
3835             << (QStringList() << QString(testFileUrl("jsimportfail/failThree.qml").toString() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
3836             << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
3837             << (QVariantList() << QVariant(false));
3838
3839     QTest::newRow("typenames in an import should be private to the import scope")
3840             << testFileUrl("jsimportfail/failFour.qml")
3841             << QString()
3842             << (QStringList() << QString(testFileUrl("jsimportfail/failFour.qml").toString() + QLatin1String(":6: ReferenceError: JsQtTest is not defined")))
3843             << (QStringList() << QLatin1String("importedModuleEnumValue"))
3844             << (QVariantList() << QVariant(0));
3845
3846     QTest::newRow("import with imports has it's own activation scope")
3847             << testFileUrl("jsimportfail/failFive.qml")
3848             << QString()
3849             << (QStringList() << QString(testFileUrl("jsimportfail/importWithImports.js").toString() + QLatin1String(":8: ReferenceError: Component is not defined")))
3850             << (QStringList() << QLatin1String("componentError"))
3851             << (QVariantList() << QVariant(0));
3852
3853     QTest::newRow("import pragma library script")
3854             << testFileUrl("jsimport/testImportPragmaLibrary.qml")
3855             << QString()
3856             << QStringList()
3857             << (QStringList() << QLatin1String("testValue"))
3858             << (QVariantList() << QVariant(31));
3859
3860     QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3861             << testFileUrl("jsimportfail/testImportPragmaLibrary.qml")
3862             << QString()
3863             << (QStringList() << QString(testFileUrl("jsimportfail/importPragmaLibrary.js").toString() + QLatin1String(":6: ReferenceError: Component is not defined")))
3864             << (QStringList() << QLatin1String("testValue"))
3865             << (QVariantList() << QVariant(0));
3866
3867     QTest::newRow("import pragma library script which has an import")
3868             << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
3869             << QString()
3870             << QStringList()
3871             << (QStringList() << QLatin1String("testValue"))
3872             << (QVariantList() << QVariant(55));
3873
3874     QTest::newRow("import pragma library script which has a pragma library import")
3875             << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3876             << QString()
3877             << QStringList()
3878             << (QStringList() << QLatin1String("testValue"))
3879             << (QVariantList() << QVariant(18));
3880
3881     QTest::newRow("import singleton type into js import")
3882             << testFileUrl("jsimport/testImportSingletonType.qml")
3883             << QString()
3884             << QStringList()
3885             << (QStringList() << QLatin1String("testValue"))
3886             << (QVariantList() << QVariant(20));
3887
3888     QTest::newRow("import module which exports a script")
3889             << testFileUrl("jsimport/testJsImport.qml")
3890             << QString()
3891             << QStringList()
3892             << (QStringList() << QLatin1String("importedScriptStringValue")
3893                               << QLatin1String("renamedScriptStringValue")
3894                               << QLatin1String("reimportedScriptStringValue"))
3895             << (QVariantList() << QVariant(QString("Hello"))
3896                                << QVariant(QString("Hello"))
3897                                << QVariant(QString("Hello")));
3898
3899     QTest::newRow("import module which exports a script which imports a remote module")
3900             << testFileUrl("jsimport/testJsRemoteImport.qml")
3901             << QString()
3902             << QStringList()
3903             << (QStringList() << QLatin1String("importedScriptStringValue")
3904                               << QLatin1String("renamedScriptStringValue")
3905                               << QLatin1String("reimportedScriptStringValue"))
3906             << (QVariantList() << QVariant(QString("Hello"))
3907                                << QVariant(QString("Hello"))
3908                                << QVariant(QString("Hello")));
3909
3910     QTest::newRow("malformed import statement")
3911             << testFileUrl("jsimportfail/malformedImport.qml")
3912             << QString()
3913             << (QStringList() << testFileUrl("jsimportfail/malformedImport.js").toString() + QLatin1String(":1: SyntaxError: Unexpected token ."))
3914             << QStringList()
3915             << QVariantList();
3916
3917     QTest::newRow("malformed file name")
3918             << testFileUrl("jsimportfail/malformedFile.qml")
3919             << QString()
3920             << (QStringList() << testFileUrl("jsimportfail/malformedFile.js").toString() + QLatin1String(":0:1: Imported file must be a script"))
3921             << QStringList()
3922             << QVariantList();
3923
3924     QTest::newRow("missing file qualifier")
3925             << testFileUrl("jsimportfail/missingFileQualifier.qml")
3926             << QString()
3927             << (QStringList() << testFileUrl("jsimportfail/missingFileQualifier.js").toString() + QLatin1String(":0:1: File import requires a qualifier"))
3928             << QStringList()
3929             << QVariantList();
3930
3931     QTest::newRow("malformed file qualifier")
3932             << testFileUrl("jsimportfail/malformedFileQualifier.qml")
3933             << QString()
3934             << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.js").toString() + QLatin1String(":0:1: File import requires a qualifier"))
3935             << QStringList()
3936             << QVariantList();
3937
3938     QTest::newRow("malformed module qualifier 2")
3939             << testFileUrl("jsimportfail/malformedFileQualifier.2.qml")
3940             << QString()
3941             << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.2.js").toString() + QLatin1String(":0:1: Invalid import qualifier"))
3942             << QStringList()
3943             << QVariantList();
3944
3945     QTest::newRow("malformed module uri")
3946             << testFileUrl("jsimportfail/malformedModule.qml")
3947             << QString()
3948             << (QStringList() << testFileUrl("jsimportfail/malformedModule.js").toString() + QLatin1String(":0:1: Invalid module URI"))
3949             << QStringList()
3950             << QVariantList();
3951
3952     QTest::newRow("missing module version")
3953             << testFileUrl("jsimportfail/missingModuleVersion.qml")
3954             << QString()
3955             << (QStringList() << testFileUrl("jsimportfail/missingModuleVersion.js").toString() + QLatin1String(":0:1: Module import requires a version"))
3956             << QStringList()
3957             << QVariantList();
3958
3959     QTest::newRow("malformed module version")
3960             << testFileUrl("jsimportfail/malformedModuleVersion.qml")
3961             << QString()
3962             << (QStringList() << testFileUrl("jsimportfail/malformedModuleVersion.js").toString() + QLatin1String(":0:1: Module import requires a version"))
3963             << QStringList()
3964             << QVariantList();
3965
3966     QTest::newRow("missing module qualifier")
3967             << testFileUrl("jsimportfail/missingModuleQualifier.qml")
3968             << QString()
3969             << (QStringList() << testFileUrl("jsimportfail/missingModuleQualifier.js").toString() + QLatin1String(":0:1: Module import requires a qualifier"))
3970             << QStringList()
3971             << QVariantList();
3972
3973     QTest::newRow("malformed module qualifier")
3974             << testFileUrl("jsimportfail/malformedModuleQualifier.qml")
3975             << QString()
3976             << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.js").toString() + QLatin1String(":0:1: Module import requires a qualifier"))
3977             << QStringList()
3978             << QVariantList();
3979
3980     QTest::newRow("malformed module qualifier 2")
3981             << testFileUrl("jsimportfail/malformedModuleQualifier.2.qml")
3982             << QString()
3983             << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.2.js").toString() + QLatin1String(":0:1: Invalid import qualifier"))
3984             << QStringList()
3985             << QVariantList();
3986 }
3987
3988 void tst_qqmlecmascript::importScripts()
3989 {
3990     QFETCH(QUrl, testfile);
3991     QFETCH(QString, errorMessage);
3992     QFETCH(QStringList, warningMessages);
3993     QFETCH(QStringList, propertyNames);
3994     QFETCH(QVariantList, propertyValues);
3995
3996     TestHTTPServer server(8111);
3997     QVERIFY(server.isValid());
3998     server.serveDirectory(dataDirectory() + "/remote");
3999
4000     QStringList importPathList = engine.importPathList();
4001
4002     QString remotePath(QLatin1String("http://127.0.0.1:8111/"));
4003     engine.addImportPath(remotePath);
4004
4005     QQmlComponent component(&engine, testfile);
4006
4007     if (!errorMessage.isEmpty())
4008         QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
4009
4010     if (warningMessages.size())
4011         foreach (const QString &warning, warningMessages)
4012             QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
4013
4014     QTRY_VERIFY(component.isReady());
4015
4016     QObject *object = component.create();
4017     if (!errorMessage.isEmpty()) {
4018         QVERIFY(object == 0);
4019     } else {
4020         QVERIFY(object != 0);
4021         for (int i = 0; i < propertyNames.size(); ++i)
4022             QCOMPARE(object->property(propertyNames.at(i).toLatin1().constData()), propertyValues.at(i));
4023         delete object;
4024     }
4025
4026     engine.setImportPathList(importPathList);
4027 }
4028
4029 void tst_qqmlecmascript::scarceResources_other()
4030 {
4031     /* These tests require knowledge of state, since we test values after
4032        performing signal or function invocation. */
4033
4034     QPixmap origPixmap(100, 100);
4035     origPixmap.fill(Qt::blue);
4036     QString srp_name, expectedWarning;
4037     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4038     ScarceResourceObject *eo = 0;
4039     QObject *srsc = 0;
4040     QObject *object = 0;
4041
4042     /* property var semantics */
4043
4044     // test that scarce resources are handled properly in signal invocation
4045     QQmlComponent varComponentTen(&engine, testFileUrl("scarceResourceSignal.var.qml"));
4046     object = varComponentTen.create();
4047     srsc = object->findChild<QObject*>("srsc");
4048     QVERIFY(srsc);
4049     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
4050     QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
4051     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4052     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4053     QMetaObject::invokeMethod(srsc, "testSignal");
4054     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
4055     QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
4056     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4057     QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
4058     QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
4059     QVERIFY(srsc->property("scarceResourceCopy").isValid());
4060     QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4061     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4062     QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
4063     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4064     delete object;
4065
4066     // test that scarce resources are handled properly from js functions in qml files
4067     QQmlComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml"));
4068     object = varComponentEleven.create();
4069     QVERIFY(object != 0);
4070     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4071     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4072     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4073     QMetaObject::invokeMethod(object, "retrieveScarceResource");
4074     QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
4075     QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4076     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4077     QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
4078     QMetaObject::invokeMethod(object, "releaseScarceResource");
4079     QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
4080     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4081     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4082     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4083     delete object;
4084
4085     // test that if an exception occurs while invoking js function from cpp, that the resources are released.
4086     QQmlComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
4087     object = varComponentTwelve.create();
4088     QVERIFY(object != 0);
4089     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4090     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4091     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4092     srp_name = object->property("srp_name").toString();
4093     expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
4094     QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
4095     QMetaObject::invokeMethod(object, "retrieveScarceResource");
4096     QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
4097     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4098     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4099     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4100     delete object;
4101
4102     // test that if an Item which has JS ownership but has a scarce resource property is garbage collected,
4103     // that the scarce resource is removed from the engine's list of scarce resources to clean up.
4104     QQmlComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml"));
4105     object = varComponentThirteen.create();
4106     QVERIFY(object != 0);
4107     QVERIFY(!object->property("varProperty").isValid()); // not assigned yet
4108     QMetaObject::invokeMethod(object, "assignVarProperty");
4109     QVERIFY(ep->scarceResources.isEmpty());             // the scarce resource is a VME property.
4110     QMetaObject::invokeMethod(object, "deassignVarProperty");
4111     QVERIFY(ep->scarceResources.isEmpty());             // should still be empty; the resource should have been released on gc.
4112     delete object;
4113
4114     /* property variant semantics */
4115
4116     // test that scarce resources are handled properly in signal invocation
4117     QQmlComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml"));
4118     object = variantComponentTen.create();
4119     QVERIFY(object != 0);
4120     srsc = object->findChild<QObject*>("srsc");
4121     QVERIFY(srsc);
4122     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
4123     QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
4124     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4125     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4126     QMetaObject::invokeMethod(srsc, "testSignal");
4127     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
4128     QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
4129     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4130     QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
4131     QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
4132     QVERIFY(srsc->property("scarceResourceCopy").isValid());
4133     QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4134     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4135     QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
4136     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4137     delete object;
4138
4139     // test that scarce resources are handled properly from js functions in qml files
4140     QQmlComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml"));
4141     object = variantComponentEleven.create();
4142     QVERIFY(object != 0);
4143     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4144     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4145     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4146     QMetaObject::invokeMethod(object, "retrieveScarceResource");
4147     QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
4148     QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4149     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4150     QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
4151     QMetaObject::invokeMethod(object, "releaseScarceResource");
4152     QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
4153     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4154     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4155     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4156     delete object;
4157
4158     // test that if an exception occurs while invoking js function from cpp, that the resources are released.
4159     QQmlComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml"));
4160     object = variantComponentTwelve.create();
4161     QVERIFY(object != 0);
4162     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4163     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4164     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4165     srp_name = object->property("srp_name").toString();
4166     expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
4167     QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
4168     QMetaObject::invokeMethod(object, "retrieveScarceResource");
4169     QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
4170     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4171     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4172     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
4173     delete object;
4174 }
4175
4176 void tst_qqmlecmascript::scarceResources_data()
4177 {
4178     QTest::addColumn<QUrl>("qmlFile");
4179     QTest::addColumn<bool>("readDetachStatus");
4180     QTest::addColumn<bool>("expectedDetachStatus");
4181     QTest::addColumn<QStringList>("propertyNames");
4182     QTest::addColumn<QVariantList>("expectedValidity");
4183     QTest::addColumn<QVariantList>("expectedValues");
4184     QTest::addColumn<QStringList>("expectedErrors");
4185
4186     QPixmap origPixmap(100, 100);
4187     origPixmap.fill(Qt::blue);
4188
4189     /* property var semantics */
4190
4191     // in the following three cases, the instance created from the component
4192     // has a property which is a copy of the scarce resource; hence, the
4193     // resource should NOT be detached prior to deletion of the object instance,
4194     // unless the resource is destroyed explicitly.
4195     QTest::newRow("var: import scarce resource copy directly")
4196         << testFileUrl("scarceResourceCopy.var.qml")
4197         << true
4198         << false // won't be detached, because assigned to property and not explicitly released
4199         << (QStringList() << QLatin1String("scarceResourceCopy"))
4200         << (QList<QVariant>() << true)
4201         << (QList<QVariant>() << origPixmap)
4202         << QStringList();
4203
4204     QTest::newRow("var: import scarce resource copy from JS")
4205         << testFileUrl("scarceResourceCopyFromJs.var.qml")
4206         << true
4207         << false // won't be detached, because assigned to property and not explicitly released
4208         << (QStringList() << QLatin1String("scarceResourceCopy"))
4209         << (QList<QVariant>() << true)
4210         << (QList<QVariant>() << origPixmap)
4211         << QStringList();
4212
4213     QTest::newRow("var: import released scarce resource copy from JS")
4214         << testFileUrl("scarceResourceDestroyedCopy.var.qml")
4215         << true
4216         << true // explicitly released, so it will be detached
4217         << (QStringList() << QLatin1String("scarceResourceCopy"))
4218         << (QList<QVariant>() << false)
4219         << (QList<QVariant>() << QVariant())
4220         << QStringList();
4221
4222     // in the following three cases, no other copy should exist in memory,
4223     // and so it should be detached (unless explicitly preserved).
4224     QTest::newRow("var: import auto-release SR from JS in binding side-effect")
4225         << testFileUrl("scarceResourceTest.var.qml")
4226         << true
4227         << true // auto released, so it will be detached
4228         << (QStringList() << QLatin1String("scarceResourceTest"))
4229         << (QList<QVariant>() << true)
4230         << (QList<QVariant>() << QVariant(100))
4231         << QStringList();
4232     QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4233         << testFileUrl("scarceResourceTestPreserve.var.qml")
4234         << true
4235         << false // won't be detached because we explicitly preserve it
4236         << (QStringList() << QLatin1String("scarceResourceTest"))
4237         << (QList<QVariant>() << true)
4238         << (QList<QVariant>() << QVariant(100))
4239         << QStringList();
4240     QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
4241         << testFileUrl("scarceResourceTestMultiple.var.qml")
4242         << true
4243         << true // will be detached because all resources were released manually or automatically.
4244         << (QStringList() << QLatin1String("scarceResourceTest"))
4245         << (QList<QVariant>() << true)
4246         << (QList<QVariant>() << QVariant(100))
4247         << QStringList();
4248
4249     // In the following three cases, test that scarce resources are handled
4250     // correctly for imports.
4251     QTest::newRow("var: import with no binding")
4252         << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4253         << false // cannot check detach status.
4254         << false
4255         << QStringList()
4256         << QList<QVariant>()
4257         << QList<QVariant>()
4258         << QStringList();
4259     QTest::newRow("var: import with binding without explicit preserve")
4260         << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4261         << false
4262         << false
4263         << (QStringList() << QLatin1String("scarceResourceCopy"))
4264         << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4265         << (QList<QVariant>() << QVariant())
4266         << QStringList();
4267     QTest::newRow("var: import with explicit release after binding evaluation")
4268         << testFileUrl("scarceResourceCopyImport.var.qml")
4269         << false
4270         << false
4271         << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4272         << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated.
4273         << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true))
4274         << QStringList();
4275     QTest::newRow("var: import with different js objects")
4276         << testFileUrl("scarceResourceCopyImportDifferent.var.qml")
4277         << false
4278         << false
4279         << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4280         << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4281         << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false))
4282         << QStringList();
4283     QTest::newRow("var: import with different js objects and explicit release")
4284         << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml")
4285         << false
4286         << false
4287         << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4288         << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4289         << (QList<QVariant>() << QVariant(origPixmap) << QVariant())
4290         << QStringList();
4291     QTest::newRow("var: import with same js objects and explicit release")
4292         << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml")
4293         << false
4294         << false
4295         << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4296         << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4297         << (QList<QVariant>() << QVariant() << QVariant())
4298         << QStringList();
4299     QTest::newRow("var: binding with same js objects and explicit release")
4300         << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml")
4301         << false
4302         << false
4303         << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4304         << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4305         << (QList<QVariant>() << QVariant() << QVariant())
4306         << QStringList();
4307
4308
4309     /* property variant semantics */
4310
4311     // in the following three cases, the instance created from the component
4312     // has a property which is a copy of the scarce resource; hence, the
4313     // resource should NOT be detached prior to deletion of the object instance,
4314     // unless the resource is destroyed explicitly.
4315     QTest::newRow("variant: import scarce resource copy directly")
4316         << testFileUrl("scarceResourceCopy.variant.qml")
4317         << true
4318         << false // won't be detached, because assigned to property and not explicitly released
4319         << (QStringList() << QLatin1String("scarceResourceCopy"))
4320         << (QList<QVariant>() << true)
4321         << (QList<QVariant>() << origPixmap)
4322         << QStringList();
4323
4324     QTest::newRow("variant: import scarce resource copy from JS")
4325         << testFileUrl("scarceResourceCopyFromJs.variant.qml")
4326         << true
4327         << false // won't be detached, because assigned to property and not explicitly released
4328         << (QStringList() << QLatin1String("scarceResourceCopy"))
4329         << (QList<QVariant>() << true)
4330         << (QList<QVariant>() << origPixmap)
4331         << QStringList();
4332
4333     QTest::newRow("variant: import released scarce resource copy from JS")
4334         << testFileUrl("scarceResourceDestroyedCopy.variant.qml")
4335         << true
4336         << true // explicitly released, so it will be detached
4337         << (QStringList() << QLatin1String("scarceResourceCopy"))
4338         << (QList<QVariant>() << false)
4339         << (QList<QVariant>() << QVariant())
4340         << QStringList();
4341
4342     // in the following three cases, no other copy should exist in memory,
4343     // and so it should be detached (unless explicitly preserved).
4344     QTest::newRow("variant: import auto-release SR from JS in binding side-effect")
4345         << testFileUrl("scarceResourceTest.variant.qml")
4346         << true
4347         << true // auto released, so it will be detached
4348         << (QStringList() << QLatin1String("scarceResourceTest"))
4349         << (QList<QVariant>() << true)
4350         << (QList<QVariant>() << QVariant(100))
4351         << QStringList();
4352     QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect")
4353         << testFileUrl("scarceResourceTestPreserve.variant.qml")
4354         << true
4355         << false // won't be detached because we explicitly preserve it
4356         << (QStringList() << QLatin1String("scarceResourceTest"))
4357         << (QList<QVariant>() << true)
4358         << (QList<QVariant>() << QVariant(100))
4359         << QStringList();
4360     QTest::newRow("variant: import multiple scarce resources")
4361         << testFileUrl("scarceResourceTestMultiple.variant.qml")
4362         << true
4363         << true // will be detached because all resources were released manually or automatically.
4364         << (QStringList() << QLatin1String("scarceResourceTest"))
4365         << (QList<QVariant>() << true)
4366         << (QList<QVariant>() << QVariant(100))
4367         << QStringList();
4368
4369     // In the following three cases, test that scarce resources are handled
4370     // correctly for imports.
4371     QTest::newRow("variant: import with no binding")
4372         << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4373         << false // cannot check detach status.
4374         << false
4375         << QStringList()
4376         << QList<QVariant>()
4377         << QList<QVariant>()
4378         << QStringList();
4379     QTest::newRow("variant: import with binding without explicit preserve")
4380         << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4381         << false
4382         << false
4383         << (QStringList() << QLatin1String("scarceResourceCopy"))
4384         << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4385         << (QList<QVariant>() << QVariant())
4386         << QStringList();
4387     QTest::newRow("variant: import with explicit release after binding evaluation")
4388         << testFileUrl("scarceResourceCopyImport.variant.qml")
4389         << false
4390         << false
4391         << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo"))
4392         << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies.
4393         << (QList<QVariant>() << origPixmap << origPixmap << QVariant())
4394         << QStringList();
4395 }
4396
4397 void tst_qqmlecmascript::scarceResources()
4398 {
4399     QFETCH(QUrl, qmlFile);
4400     QFETCH(bool, readDetachStatus);
4401     QFETCH(bool, expectedDetachStatus);
4402     QFETCH(QStringList, propertyNames);
4403     QFETCH(QVariantList, expectedValidity);
4404     QFETCH(QVariantList, expectedValues);
4405     QFETCH(QStringList, expectedErrors);
4406
4407     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4408     ScarceResourceObject *eo = 0;
4409     QObject *object = 0;
4410
4411     QQmlComponent c(&engine, qmlFile);
4412     object = c.create();
4413     QVERIFY(object != 0);
4414     for (int i = 0; i < propertyNames.size(); ++i) {
4415         QString prop = propertyNames.at(i);
4416         bool validity = expectedValidity.at(i).toBool();
4417         QVariant value = expectedValues.at(i);
4418
4419         QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity);
4420         if (value.type() == QVariant::Int) {
4421             QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt());
4422         } else if (value.type() == QVariant::Pixmap) {
4423             QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>());
4424         }
4425     }
4426
4427     if (readDetachStatus) {
4428         eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4429         QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
4430     }
4431
4432     QVERIFY(ep->scarceResources.isEmpty());
4433     delete object;
4434 }
4435
4436 void tst_qqmlecmascript::propertyChangeSlots()
4437 {
4438     // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
4439     QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml"));
4440     QObject *object = component.create();
4441     QVERIFY(object != 0);
4442     delete object;
4443
4444     // ensure that invalid property names fail properly.
4445     QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4446     QQmlComponent e1(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.1.qml"));
4447     QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
4448     QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
4449     object = e1.create();
4450     QVERIFY(object == 0);
4451     delete object;
4452
4453     QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4454     QQmlComponent e2(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.2.qml"));
4455     expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
4456     QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
4457     object = e2.create();
4458     QVERIFY(object == 0);
4459     delete object;
4460
4461     QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4462     QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml"));
4463     expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
4464     QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
4465     object = e3.create();
4466     QVERIFY(object == 0);
4467     delete object;
4468
4469     QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4470     QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml"));
4471     expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
4472     QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
4473     object = e4.create();
4474     QVERIFY(object == 0);
4475     delete object;
4476 }
4477
4478 void tst_qqmlecmascript::propertyVar_data()
4479 {
4480     QTest::addColumn<QUrl>("qmlFile");
4481
4482     // valid
4483     QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyVar.1.qml");
4484     QTest::newRow("non-bindable object changed") << testFileUrl("propertyVar.2.qml");
4485     QTest::newRow("primitive changed") << testFileUrl("propertyVar.3.qml");
4486     QTest::newRow("javascript array modification") << testFileUrl("propertyVar.4.qml");
4487     QTest::newRow("javascript map modification") << testFileUrl("propertyVar.5.qml");
4488     QTest::newRow("javascript array assignment") << testFileUrl("propertyVar.6.qml");
4489     QTest::newRow("javascript map assignment") << testFileUrl("propertyVar.7.qml");
4490     QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml");
4491     QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml");
4492     QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml");
4493     QTest::newRow("javascript function assignment") << testFileUrl("propertyVar.11.qml");
4494     QTest::newRow("javascript special assignment") << testFileUrl("propertyVar.12.qml");
4495     QTest::newRow("declarative binding assignment") << testFileUrl("propertyVar.13.qml");
4496     QTest::newRow("imperative binding assignment") << testFileUrl("propertyVar.14.qml");
4497     QTest::newRow("stored binding assignment") << testFileUrl("propertyVar.15.qml");
4498     QTest::newRow("function expression binding assignment") << testFileUrl("propertyVar.16.qml");
4499 }
4500
4501 void tst_qqmlecmascript::propertyVar()
4502 {
4503     QFETCH(QUrl, qmlFile);
4504
4505     QQmlComponent component(&engine, qmlFile);
4506     QObject *object = component.create();
4507     QVERIFY(object != 0);
4508
4509     QCOMPARE(object->property("test").toBool(), true);
4510
4511     delete object;
4512 }
4513
4514 void tst_qqmlecmascript::propertyQJSValue_data()
4515 {
4516     QTest::addColumn<QUrl>("qmlFile");
4517
4518     // valid
4519     QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyQJSValue.1.qml");
4520     QTest::newRow("non-bindable object changed") << testFileUrl("propertyQJSValue.2.qml");
4521     QTest::newRow("primitive changed") << testFileUrl("propertyQJSValue.3.qml");
4522     QTest::newRow("javascript array modification") << testFileUrl("propertyQJSValue.4.qml");
4523     QTest::newRow("javascript map modification") << testFileUrl("propertyQJSValue.5.qml");
4524     QTest::newRow("javascript array assignment") << testFileUrl("propertyQJSValue.6.qml");
4525     QTest::newRow("javascript map assignment") << testFileUrl("propertyQJSValue.7.qml");
4526     QTest::newRow("literal property assignment") << testFileUrl("propertyQJSValue.8.qml");
4527     QTest::newRow("qobject property assignment") << testFileUrl("propertyQJSValue.9.qml");
4528     QTest::newRow("base class var property assignment") << testFileUrl("propertyQJSValue.10.qml");
4529     QTest::newRow("javascript function assignment") << testFileUrl("propertyQJSValue.11.qml");
4530     QTest::newRow("javascript special assignment") << testFileUrl("propertyQJSValue.12.qml");
4531     QTest::newRow("declarative binding assignment") << testFileUrl("propertyQJSValue.13.qml");
4532     QTest::newRow("imperative binding assignment") << testFileUrl("propertyQJSValue.14.qml");
4533     QTest::newRow("stored binding assignment") << testFileUrl("propertyQJSValue.15.qml");
4534     QTest::newRow("javascript function binding") << testFileUrl("propertyQJSValue.16.qml");
4535
4536     QTest::newRow("reset property") << testFileUrl("propertyQJSValue.reset.qml");
4537     QTest::newRow("reset property in binding") << testFileUrl("propertyQJSValue.bindingreset.qml");
4538 }
4539
4540 void tst_qqmlecmascript::propertyQJSValue()
4541 {
4542     QFETCH(QUrl, qmlFile);
4543
4544     QQmlComponent component(&engine, qmlFile);
4545     QObject *object = component.create();
4546     QVERIFY(object != 0);
4547
4548     QCOMPARE(object->property("test").toBool(), true);
4549
4550     delete object;
4551 }
4552
4553 // Tests that we can write QVariant values to var properties from C++
4554 void tst_qqmlecmascript::propertyVarCpp()
4555 {
4556     QObject *object = 0;
4557
4558     // ensure that writing to and reading from a var property from cpp works as required.
4559     // Literal values stored in var properties can be read and written as QVariants
4560     // of a specific type, whereas object values are read as QVariantMaps.
4561     QQmlComponent component(&engine, testFileUrl("propertyVarCpp.qml"));
4562     object = component.create();
4563     QVERIFY(object != 0);
4564     // assign int to property var that currently has int assigned
4565     QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
4566     QCOMPARE(object->property("varBound"), QVariant(15));
4567     QCOMPARE(object->property("intBound"), QVariant(15));
4568     QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
4569     QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
4570     // assign string to property var that current has bool assigned
4571     QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
4572     QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
4573     QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
4574     QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
4575     // now enforce behaviour when accessing JavaScript objects from cpp.
4576     QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
4577     delete object;
4578 }
4579
4580 static void gc(QQmlEngine &engine)
4581 {
4582     engine.collectGarbage();
4583     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4584     QCoreApplication::processEvents();
4585 }
4586
4587 void tst_qqmlecmascript::propertyVarOwnership()
4588 {
4589     // Referenced JS objects are not collected
4590     {
4591     QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.qml"));
4592     QObject *object = component.create();
4593     QVERIFY(object != 0);
4594     QCOMPARE(object->property("test").toBool(), false);
4595     QMetaObject::invokeMethod(object, "runTest");
4596     QCOMPARE(object->property("test").toBool(), true);
4597     delete object;
4598     }
4599     // Referenced JS objects are not collected
4600     {
4601     QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml"));
4602     QObject *object = component.create();
4603     QVERIFY(object != 0);
4604     QCOMPARE(object->property("test").toBool(), false);
4605     QMetaObject::invokeMethod(object, "runTest");
4606     QCOMPARE(object->property("test").toBool(), true);
4607     delete object;
4608     }
4609     // Qt objects are not collected until they've been dereferenced
4610     {
4611     QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml"));
4612     QObject *object = component.create();
4613     QVERIFY(object != 0);
4614
4615     QCOMPARE(object->property("test2").toBool(), false);
4616     QCOMPARE(object->property("test2").toBool(), false);
4617
4618     QMetaObject::invokeMethod(object, "runTest");
4619     QCOMPARE(object->property("test1").toBool(), true);
4620
4621     QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4622     QVERIFY(!referencedObject.isNull());
4623     gc(engine);
4624     QVERIFY(!referencedObject.isNull());
4625
4626     QMetaObject::invokeMethod(object, "runTest2");
4627     QCOMPARE(object->property("test2").toBool(), true);
4628     gc(engine);
4629     QVERIFY(referencedObject.isNull());
4630
4631     delete object;
4632     }
4633     // Self reference does not prevent Qt object collection
4634     {
4635     QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml"));
4636     QObject *object = component.create();
4637     QVERIFY(object != 0);
4638
4639     QCOMPARE(object->property("test").toBool(), true);
4640
4641     QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4642     QVERIFY(!referencedObject.isNull());
4643     gc(engine);
4644     QVERIFY(!referencedObject.isNull());
4645
4646     QMetaObject::invokeMethod(object, "runTest");
4647     gc(engine);
4648     QVERIFY(referencedObject.isNull());
4649
4650     delete object;
4651     }
4652     // Garbage collection cannot result in attempted dereference of empty handle
4653     {
4654     QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.5.qml"));
4655     QObject *object = component.create();
4656     QVERIFY(object != 0);
4657     QMetaObject::invokeMethod(object, "runTest");
4658     QCOMPARE(object->property("test").toBool(), true);
4659     delete object;
4660     }
4661 }
4662
4663 void tst_qqmlecmascript::propertyVarImplicitOwnership()
4664 {
4665     // The childObject has a reference to a different QObject.  We want to ensure
4666     // that the different item will not be cleaned up until required.  IE, the childObject
4667     // has implicit ownership of the constructed QObject.
4668     QQmlComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml"));
4669     QObject *object = component.create();
4670     QVERIFY(object != 0);
4671     QMetaObject::invokeMethod(object, "assignCircular");
4672     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4673     QCoreApplication::processEvents();
4674     QObject *rootObject = object->property("vp").value<QObject*>();
4675     QVERIFY(rootObject != 0);
4676     QObject *childObject = rootObject->findChild<QObject*>("text");
4677     QVERIFY(childObject != 0);
4678     QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4679     QCOMPARE(childObject->property("textCanary").toInt(), 10);
4680     QMetaObject::invokeMethod(childObject, "constructQObject");    // creates a reference to a constructed QObject.
4681     QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
4682     QVERIFY(!qobjectGuard.isNull());
4683     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4684     QCoreApplication::processEvents();
4685     QVERIFY(!qobjectGuard.isNull());
4686     QMetaObject::invokeMethod(object, "deassignCircular");
4687     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4688     QCoreApplication::processEvents();
4689     QVERIFY(qobjectGuard.isNull());                                // should have been collected now.
4690     delete object;
4691 }
4692
4693 void tst_qqmlecmascript::propertyVarReparent()
4694 {
4695     // ensure that nothing breaks if we re-parent objects
4696     QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4697     QObject *object = component.create();
4698     QVERIFY(object != 0);
4699     QMetaObject::invokeMethod(object, "assignVarProp");
4700     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4701     QCoreApplication::processEvents();
4702     QObject *rect = object->property("vp").value<QObject*>();
4703     QObject *text = rect->findChild<QObject*>("textOne");
4704     QObject *text2 = rect->findChild<QObject*>("textTwo");
4705     QWeakPointer<QObject> rectGuard(rect);
4706     QWeakPointer<QObject> textGuard(text);
4707     QWeakPointer<QObject> text2Guard(text2);
4708     QVERIFY(!rectGuard.isNull());
4709     QVERIFY(!textGuard.isNull());
4710     QVERIFY(!text2Guard.isNull());
4711     QCOMPARE(text->property("textCanary").toInt(), 11);
4712     QCOMPARE(text2->property("textCanary").toInt(), 12);
4713     // now construct an image which we will reparent.
4714     QMetaObject::invokeMethod(text2, "constructQObject");
4715     QObject *image = text2->property("vp").value<QObject*>();
4716     QWeakPointer<QObject> imageGuard(image);
4717     QVERIFY(!imageGuard.isNull());
4718     QCOMPARE(image->property("imageCanary").toInt(), 13);
4719     // now reparent the "Image" object (currently, it has JS ownership)
4720     image->setParent(text);                                        // shouldn't be collected after deassignVp now, since has a parent.
4721     QMetaObject::invokeMethod(text2, "deassignVp");
4722     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4723     QCoreApplication::processEvents();
4724     QCOMPARE(text->property("textCanary").toInt(), 11);
4725     QCOMPARE(text2->property("textCanary").toInt(), 22);
4726     QVERIFY(!imageGuard.isNull());                                 // should still be alive.
4727     QCOMPARE(image->property("imageCanary").toInt(), 13);          // still able to access var properties
4728     QMetaObject::invokeMethod(object, "deassignVarProp");          // now deassign the root-object's vp, causing gc of rect+text+text2
4729     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4730     QCoreApplication::processEvents();
4731     QVERIFY(imageGuard.isNull());                                  // should now have been deleted, due to parent being deleted.
4732     delete object;
4733 }
4734
4735 void tst_qqmlecmascript::propertyVarReparentNullContext()
4736 {
4737     // sometimes reparenting can cause problems
4738     // (eg, if the ctxt is collected, varproperties are no longer available)
4739     // this test ensures that no crash occurs in that situation.
4740     QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4741     QObject *object = component.create();
4742     QVERIFY(object != 0);
4743     QMetaObject::invokeMethod(object, "assignVarProp");
4744     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4745     QCoreApplication::processEvents();
4746     QObject *rect = object->property("vp").value<QObject*>();
4747     QObject *text = rect->findChild<QObject*>("textOne");
4748     QObject *text2 = rect->findChild<QObject*>("textTwo");
4749     QWeakPointer<QObject> rectGuard(rect);
4750     QWeakPointer<QObject> textGuard(text);
4751     QWeakPointer<QObject> text2Guard(text2);
4752     QVERIFY(!rectGuard.isNull());
4753     QVERIFY(!textGuard.isNull());
4754     QVERIFY(!text2Guard.isNull());
4755     QCOMPARE(text->property("textCanary").toInt(), 11);
4756     QCOMPARE(text2->property("textCanary").toInt(), 12);
4757     // now construct an image which we will reparent.
4758     QMetaObject::invokeMethod(text2, "constructQObject");
4759     QObject *image = text2->property("vp").value<QObject*>();
4760     QWeakPointer<QObject> imageGuard(image);
4761     QVERIFY(!imageGuard.isNull());
4762     QCOMPARE(image->property("imageCanary").toInt(), 13);
4763     // now reparent the "Image" object (currently, it has JS ownership)
4764     image->setParent(object);                                      // reparented to base object.  after deassignVarProp, the ctxt will be invalid.
4765     QMetaObject::invokeMethod(object, "deassignVarProp");          // now deassign the root-object's vp, causing gc of rect+text+text2
4766     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4767     QCoreApplication::processEvents();
4768     QVERIFY(!imageGuard.isNull());                                 // should still be alive.
4769     QVERIFY(!image->property("imageCanary").isValid());            // but varProperties won't be available (null context).
4770     delete object;
4771     QVERIFY(imageGuard.isNull());                                  // should now be dead.
4772 }
4773
4774 void tst_qqmlecmascript::propertyVarCircular()
4775 {
4776     // enforce behaviour regarding circular references - ensure qdvmemo deletion.
4777     QQmlComponent component(&engine, testFileUrl("propertyVar.circular.qml"));
4778     QObject *object = component.create();
4779     QVERIFY(object != 0);
4780     QMetaObject::invokeMethod(object, "assignCircular");           // cause assignment and gc
4781     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4782     QCoreApplication::processEvents();
4783     QCOMPARE(object->property("canaryInt"), QVariant(5));
4784     QVariant canaryResourceVariant = object->property("canaryResource");
4785     QVERIFY(canaryResourceVariant.isValid());
4786     QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
4787     canaryResourceVariant = QVariant();                            // invalidate it to remove one copy of the pixmap from memory.
4788     QMetaObject::invokeMethod(object, "deassignCanaryResource");   // remove one copy of the pixmap from memory
4789     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4790     QCoreApplication::processEvents();
4791     QVERIFY(!canaryResourcePixmap.isDetached());                   // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
4792     QMetaObject::invokeMethod(object, "deassignCircular");         // cause deassignment and gc
4793     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4794     QCoreApplication::processEvents();
4795     QCOMPARE(object->property("canaryInt"), QVariant(2));
4796     QCOMPARE(object->property("canaryResource"), QVariant(1));
4797     QVERIFY(canaryResourcePixmap.isDetached());                    // now detached, since orig copy was member of qdvmemo which was deleted.
4798     delete object;
4799 }
4800
4801 void tst_qqmlecmascript::propertyVarCircular2()
4802 {
4803     // track deletion of JS-owned parent item with Cpp-owned child
4804     // where the child has a var property referencing its parent.
4805     QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4806     QObject *object = component.create();
4807     QVERIFY(object != 0);
4808     QMetaObject::invokeMethod(object, "assignCircular");
4809     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4810     QCoreApplication::processEvents();
4811     QObject *rootObject = object->property("vp").value<QObject*>();
4812     QVERIFY(rootObject != 0);
4813     QObject *childObject = rootObject->findChild<QObject*>("text");
4814     QVERIFY(childObject != 0);
4815     QWeakPointer<QObject> rootObjectTracker(rootObject);
4816     QVERIFY(!rootObjectTracker.isNull());
4817     QWeakPointer<QObject> childObjectTracker(childObject);
4818     QVERIFY(!childObjectTracker.isNull());
4819     gc(engine);
4820     QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4821     QCOMPARE(childObject->property("textCanary").toInt(), 10);
4822     QMetaObject::invokeMethod(object, "deassignCircular");
4823     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4824     QCoreApplication::processEvents();
4825     QVERIFY(rootObjectTracker.isNull());                           // should have been collected
4826     QVERIFY(childObjectTracker.isNull());                          // should have been collected
4827     delete object;
4828 }
4829
4830 void tst_qqmlecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
4831 {
4832     *(int*)(parameter) += 1;
4833     qPersistentDispose(object);
4834 }
4835
4836 void tst_qqmlecmascript::propertyVarInheritance()
4837 {
4838     int propertyVarWeakRefCallbackCount = 0;
4839
4840     // enforce behaviour regarding element inheritance - ensure handle disposal.
4841     // The particular component under test here has a chain of references.
4842     QQmlComponent component(&engine, testFileUrl("propertyVar.inherit.qml"));
4843     QObject *object = component.create();
4844     QVERIFY(object != 0);
4845     QMetaObject::invokeMethod(object, "assignCircular");           // cause assignment and gc
4846     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4847     QCoreApplication::processEvents();
4848     // we want to be able to track when the varProperties array of the last metaobject is disposed
4849     QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
4850     QObject *ico5 = object->property("varProperty").value<QObject*>()->property("inheritanceVarProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
4851     QQmlVMEMetaObject *icovmemo = QQmlVMEMetaObject::get(ico5);
4852     QQmlVMEMetaObject *ccovmemo = QQmlVMEMetaObject::get(cco5);
4853     v8::Persistent<v8::Value> icoCanaryHandle;
4854     v8::Persistent<v8::Value> ccoCanaryHandle;
4855     {
4856         v8::HandleScope hs;
4857         // XXX NOTE: this is very implementation dependent.  QDVMEMO->vmeProperty() is the only
4858         // public function which can return us a handle to something in the varProperties array.
4859         icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ")));
4860         ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")));
4861         // we make them weak and invoke the gc, but we should not hit the weak-callback yet
4862         // as the varproperties array of each vmemo still references the resource.
4863         icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4864         ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4865         gc(engine);
4866         QVERIFY(propertyVarWeakRefCallbackCount == 0);
4867     }
4868     // now we deassign the var prop, which should trigger collection of item subtrees.
4869     QMetaObject::invokeMethod(object, "deassignCircular");         // cause deassignment and gc
4870     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4871     QCoreApplication::processEvents();
4872     // ensure that there are only weak handles to the underlying varProperties array remaining.
4873     gc(engine);
4874     QCOMPARE(propertyVarWeakRefCallbackCount, 2);                  // should have been called for both, since all refs should be weak.
4875     delete object;
4876     // since there are no parent vmemo's to keep implicit references alive, and the only handles
4877     // to what remains are weak, all varProperties arrays must have been collected.
4878 }
4879
4880 void tst_qqmlecmascript::propertyVarInheritance2()
4881 {
4882     int propertyVarWeakRefCallbackCount = 0;
4883
4884     // The particular component under test here does NOT have a chain of references; the
4885     // only link between rootObject and childObject is that rootObject is the parent of childObject.
4886     QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4887     QObject *object = component.create();
4888     QVERIFY(object != 0);
4889     QMetaObject::invokeMethod(object, "assignCircular");
4890     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4891     QCoreApplication::processEvents();
4892     QObject *rootObject = object->property("vp").value<QObject*>();
4893     QVERIFY(rootObject != 0);
4894     QObject *childObject = rootObject->findChild<QObject*>("text");
4895     QVERIFY(childObject != 0);
4896     QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4897     QCOMPARE(childObject->property("textCanary").toInt(), 10);
4898     v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
4899     {
4900         v8::HandleScope hs;
4901         propertyVarWeakRefCallbackCount = 0;                           // reset callback count.
4902         childObjectVarArrayValueHandle = qPersistentNew(QQmlVMEMetaObject::get(childObject)->vmeProperty(childObject->metaObject()->indexOfProperty("vp")));
4903         childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4904         gc(engine);
4905         QVERIFY(propertyVarWeakRefCallbackCount == 0);                 // should not have been collected yet.
4906         QCOMPARE(childObject->property("vp").value<QObject*>(), rootObject);
4907         QCOMPARE(childObject->property("textCanary").toInt(), 10);
4908     }
4909     QMetaObject::invokeMethod(object, "deassignCircular");
4910     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4911     QCoreApplication::processEvents();
4912     QVERIFY(propertyVarWeakRefCallbackCount == 1);                 // should have been collected now.
4913     delete object;
4914 }
4915
4916 // Ensure that QObject type conversion works on binding assignment
4917 void tst_qqmlecmascript::elementAssign()
4918 {
4919     QQmlComponent component(&engine, testFileUrl("elementAssign.qml"));
4920
4921     QObject *object = component.create();
4922     QVERIFY(object != 0);
4923
4924     QCOMPARE(object->property("test").toBool(), true);
4925
4926     delete object;
4927 }
4928
4929 // QTBUG-12457
4930 void tst_qqmlecmascript::objectPassThroughSignals()
4931 {
4932     QQmlComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml"));
4933
4934     QObject *object = component.create();
4935     QVERIFY(object != 0);
4936
4937     QCOMPARE(object->property("test").toBool(), true);
4938
4939     delete object;
4940 }
4941
4942 // QTBUG-21626
4943 void tst_qqmlecmascript::objectConversion()
4944 {
4945     QQmlComponent component(&engine, testFileUrl("objectConversion.qml"));
4946
4947     QObject *object = component.create();
4948     QVERIFY(object != 0);
4949     QVariant retn;
4950     QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
4951     QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
4952
4953     delete object;
4954 }
4955
4956
4957 // QTBUG-20242
4958 void tst_qqmlecmascript::booleanConversion()
4959 {
4960     QQmlComponent component(&engine, testFileUrl("booleanConversion.qml"));
4961
4962     QObject *object = component.create();
4963     QVERIFY(object != 0);
4964
4965     QCOMPARE(object->property("test_true1").toBool(), true);
4966     QCOMPARE(object->property("test_true2").toBool(), true);
4967     QCOMPARE(object->property("test_true3").toBool(), true);
4968     QCOMPARE(object->property("test_true4").toBool(), true);
4969     QCOMPARE(object->property("test_true5").toBool(), true);
4970
4971     QCOMPARE(object->property("test_false1").toBool(), false);
4972     QCOMPARE(object->property("test_false2").toBool(), false);
4973     QCOMPARE(object->property("test_false3").toBool(), false);
4974
4975     delete object;
4976 }
4977
4978 void tst_qqmlecmascript::handleReferenceManagement()
4979 {
4980
4981     int dtorCount = 0;
4982     {
4983         // Linear QObject reference
4984         QQmlEngine hrmEngine;
4985         QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml"));
4986         QObject *object = component.create();
4987         QVERIFY(object != 0);
4988         CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4989         cro->setEngine(&hrmEngine);
4990         cro->setDtorCount(&dtorCount);
4991         QMetaObject::invokeMethod(object, "createReference");
4992         gc(engine);
4993         QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
4994         delete object;
4995         hrmEngine.collectGarbage();
4996         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4997         QCoreApplication::processEvents();
4998         QCOMPARE(dtorCount, 3);
4999     }
5000
5001     dtorCount = 0;
5002     {
5003         // Circular QObject reference
5004         QQmlEngine hrmEngine;
5005         QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml"));
5006         QObject *object = component.create();
5007         QVERIFY(object != 0);
5008         CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
5009         cro->setEngine(&hrmEngine);
5010         cro->setDtorCount(&dtorCount);
5011         QMetaObject::invokeMethod(object, "circularReference");
5012         gc(engine);
5013         QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
5014         delete object;
5015         hrmEngine.collectGarbage();
5016         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5017         QCoreApplication::processEvents();
5018         QCOMPARE(dtorCount, 3);
5019     }
5020
5021     dtorCount = 0;
5022     {
5023         // Linear handle reference
5024         QQmlEngine hrmEngine;
5025         QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.1.qml"));
5026         QObject *object = component.create();
5027         QVERIFY(object != 0);
5028         CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
5029         QVERIFY(crh != 0);
5030         crh->setEngine(&hrmEngine);
5031         crh->setDtorCount(&dtorCount);
5032         QMetaObject::invokeMethod(object, "createReference");
5033         CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
5034         CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
5035         QVERIFY(first != 0);
5036         QVERIFY(second != 0);
5037         first->addReference(QQmlData::get(second)->v8object); // create reference
5038         // now we have to reparent second and make second owned by JS.
5039         second->setParent(0);
5040         QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
5041         gc(engine);
5042         QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
5043         delete object;
5044         hrmEngine.collectGarbage();
5045         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5046         QCoreApplication::processEvents();
5047         QCOMPARE(dtorCount, 3);
5048     }
5049
5050     dtorCount = 0;
5051     {
5052         // Circular handle reference
5053         QQmlEngine hrmEngine;
5054         QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.2.qml"));
5055         QObject *object = component.create();
5056         QVERIFY(object != 0);
5057         CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
5058         QVERIFY(crh != 0);
5059         crh->setEngine(&hrmEngine);
5060         crh->setDtorCount(&dtorCount);
5061         QMetaObject::invokeMethod(object, "circularReference");
5062         CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
5063         CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
5064         QVERIFY(first != 0);
5065         QVERIFY(second != 0);
5066         first->addReference(QQmlData::get(second)->v8object); // create circular reference
5067         second->addReference(QQmlData::get(first)->v8object); // note: must be weak.
5068         // now we have to reparent and change ownership, and unset the property references.
5069         first->setParent(0);
5070         second->setParent(0);
5071         QQmlEngine::setObjectOwnership(first, QQmlEngine::JavaScriptOwnership);
5072         QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
5073         object->setProperty("first", QVariant::fromValue<QObject*>(0));
5074         object->setProperty("second", QVariant::fromValue<QObject*>(0));
5075         gc(engine);
5076         QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
5077         delete object;
5078         hrmEngine.collectGarbage();
5079         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5080         QCoreApplication::processEvents();
5081         QCOMPARE(dtorCount, 3);
5082     }
5083
5084     dtorCount = 0;
5085     {
5086         // multiple engine interaction - linear reference
5087         QQmlEngine hrmEngine1;
5088         QQmlEngine hrmEngine2;
5089         QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
5090         QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
5091         QObject *object1 = component1.create();
5092         QObject *object2 = component2.create();
5093         QVERIFY(object1 != 0);
5094         QVERIFY(object2 != 0);
5095         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
5096         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
5097         QVERIFY(crh1 != 0);
5098         QVERIFY(crh2 != 0);
5099         crh1->setEngine(&hrmEngine1);
5100         crh2->setEngine(&hrmEngine2);
5101         crh1->setDtorCount(&dtorCount);
5102         crh2->setDtorCount(&dtorCount);
5103         QMetaObject::invokeMethod(object1, "createReference");
5104         QMetaObject::invokeMethod(object2, "createReference");
5105         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
5106         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
5107         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
5108         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
5109         QVERIFY(first1 != 0);
5110         QVERIFY(second1 != 0);
5111         QVERIFY(first2 != 0);
5112         QVERIFY(second2 != 0);
5113         first1->addReference(QQmlData::get(second2)->v8object); // create reference across engines
5114         // now we have to reparent second2 and make second2 owned by JS.
5115         second2->setParent(0);
5116         QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5117         gc(engine);
5118         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5119         QCoreApplication::processEvents();
5120         QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
5121         delete object1;
5122         delete object2;
5123         hrmEngine1.collectGarbage();
5124         hrmEngine2.collectGarbage();
5125         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5126         QCoreApplication::processEvents();
5127         QCOMPARE(dtorCount, 6);
5128     }
5129
5130     dtorCount = 0;
5131     {
5132         // multiple engine interaction - circular reference
5133         QQmlEngine hrmEngine1;
5134         QQmlEngine hrmEngine2;
5135         QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
5136         QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
5137         QObject *object1 = component1.create();
5138         QObject *object2 = component2.create();
5139         QVERIFY(object1 != 0);
5140         QVERIFY(object2 != 0);
5141         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
5142         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
5143         QVERIFY(crh1 != 0);
5144         QVERIFY(crh2 != 0);
5145         crh1->setEngine(&hrmEngine1);
5146         crh2->setEngine(&hrmEngine2);
5147         crh1->setDtorCount(&dtorCount);
5148         crh2->setDtorCount(&dtorCount);
5149         QMetaObject::invokeMethod(object1, "createReference");
5150         QMetaObject::invokeMethod(object2, "createReference");
5151         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
5152         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
5153         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
5154         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
5155         QVERIFY(first1 != 0);
5156         QVERIFY(second1 != 0);
5157         QVERIFY(first2 != 0);
5158         QVERIFY(second2 != 0);
5159         first1->addReference(QQmlData::get(second1)->v8object);  // create linear reference within engine1
5160         second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
5161         second2->addReference(QQmlData::get(first2)->v8object);  // create linear reference within engine2
5162         first2->addReference(QQmlData::get(first1)->v8object);   // close the loop - circular ref across engines
5163         // now we have to reparent and change ownership to JS, and remove property references.
5164         first1->setParent(0);
5165         second1->setParent(0);
5166         first2->setParent(0);
5167         second2->setParent(0);
5168         QQmlEngine::setObjectOwnership(first1, QQmlEngine::JavaScriptOwnership);
5169         QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
5170         QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
5171         QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5172         object1->setProperty("first", QVariant::fromValue<QObject*>(0));
5173         object1->setProperty("second", QVariant::fromValue<QObject*>(0));
5174         object2->setProperty("first", QVariant::fromValue<QObject*>(0));
5175         object2->setProperty("second", QVariant::fromValue<QObject*>(0));
5176         gc(engine);
5177         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5178         QCoreApplication::processEvents();
5179         QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
5180         delete object1;
5181         delete object2;
5182         hrmEngine1.collectGarbage();
5183         hrmEngine2.collectGarbage();
5184         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5185         QCoreApplication::processEvents();
5186         QCOMPARE(dtorCount, 6);
5187     }
5188
5189     dtorCount = 0;
5190     {
5191         // multiple engine interaction - linear reference with engine deletion
5192         QQmlEngine *hrmEngine1 = new QQmlEngine;
5193         QQmlEngine *hrmEngine2 = new QQmlEngine;
5194         QQmlComponent component1(hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
5195         QQmlComponent component2(hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
5196         QObject *object1 = component1.create();
5197         QObject *object2 = component2.create();
5198         QVERIFY(object1 != 0);
5199         QVERIFY(object2 != 0);
5200         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
5201         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
5202         QVERIFY(crh1 != 0);
5203         QVERIFY(crh2 != 0);
5204         crh1->setEngine(hrmEngine1);
5205         crh2->setEngine(hrmEngine2);
5206         crh1->setDtorCount(&dtorCount);
5207         crh2->setDtorCount(&dtorCount);
5208         QMetaObject::invokeMethod(object1, "createReference");
5209         QMetaObject::invokeMethod(object2, "createReference");
5210         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
5211         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
5212         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
5213         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
5214         QVERIFY(first1 != 0);
5215         QVERIFY(second1 != 0);
5216         QVERIFY(first2 != 0);
5217         QVERIFY(second2 != 0);
5218         first1->addReference(QQmlData::get(second1)->v8object);  // create linear reference within engine1
5219         second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
5220         second2->addReference(QQmlData::get(first2)->v8object);  // create linear reference within engine2
5221         // now we have to reparent and change ownership to JS.
5222         first1->setParent(crh1);
5223         second1->setParent(0);
5224         first2->setParent(0);
5225         second2->setParent(0);
5226         QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
5227         QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
5228         QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
5229         gc(*hrmEngine1);
5230         gc(*hrmEngine2);
5231         QCOMPARE(dtorCount, 0);
5232         delete hrmEngine2; // should trigger deletion of objects with JS ownership tracked by this engine
5233         gc(*hrmEngine1);
5234         QCOMPARE(dtorCount, 2); // first2 and second2 should have been deleted.
5235         delete object1;
5236         delete object2;
5237         gc(*hrmEngine1);
5238         QCOMPARE(dtorCount, 6); // deleting object1 and object2 should trigger deletion of first1 and first2.
5239         delete hrmEngine1;
5240         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
5241         QCoreApplication::processEvents();
5242         QCOMPARE(dtorCount, 6); // all objects should have been cleaned up prior to deleting hrmEngine1.
5243     }
5244
5245     {
5246         // Dynamic variant property reference keeps target alive
5247         QQmlEngine hrmEngine;
5248         QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.qml"));
5249         QObject *object = component.create();
5250         QVERIFY(object != 0);
5251         QMetaObject::invokeMethod(object, "createReference");
5252         gc(engine);
5253         QMetaObject::invokeMethod(object, "ensureReference");
5254         gc(engine);
5255         QMetaObject::invokeMethod(object, "removeReference");
5256         gc(engine);
5257         QMetaObject::invokeMethod(object, "ensureDeletion");
5258         QCOMPARE(object->property("success").toBool(), true);
5259         delete object;
5260     }
5261
5262     {
5263         // Dynamic Item property reference keeps target alive
5264         QQmlEngine hrmEngine;
5265         QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.2.qml"));
5266         QObject *object = component.create();
5267         QVERIFY(object != 0);
5268         QMetaObject::invokeMethod(object, "createReference");
5269         gc(engine);
5270         QMetaObject::invokeMethod(object, "ensureReference");
5271         gc(engine);
5272         QMetaObject::invokeMethod(object, "removeReference");
5273         gc(engine);
5274         QMetaObject::invokeMethod(object, "ensureDeletion");
5275         QCOMPARE(object->property("success").toBool(), true);
5276         delete object;
5277     }
5278
5279     {
5280         // Item property reference to deleted item doesn't crash
5281         QQmlEngine hrmEngine;
5282         QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.3.qml"));
5283         QObject *object = component.create();
5284         QVERIFY(object != 0);
5285         QMetaObject::invokeMethod(object, "createReference");
5286         gc(engine);
5287         QMetaObject::invokeMethod(object, "ensureReference");
5288         gc(engine);
5289         QMetaObject::invokeMethod(object, "manuallyDelete");
5290         gc(engine);
5291         QMetaObject::invokeMethod(object, "ensureDeleted");
5292         QCOMPARE(object->property("success").toBool(), true);
5293         delete object;
5294     }
5295 }
5296
5297 void tst_qqmlecmascript::stringArg()
5298 {
5299     QQmlComponent component(&engine, testFileUrl("stringArg.qml"));
5300     QObject *object = component.create();
5301     QVERIFY(object != 0);
5302     QMetaObject::invokeMethod(object, "success");
5303     QVERIFY(object->property("returnValue").toBool());
5304
5305     QString w1 = testFileUrl("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
5306     QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5307     QMetaObject::invokeMethod(object, "failure");
5308     QVERIFY(object->property("returnValue").toBool());
5309
5310     delete object;
5311 }
5312
5313 void tst_qqmlecmascript::readonlyDeclaration()
5314 {
5315     QQmlComponent component(&engine, testFileUrl("readonlyDeclaration.qml"));
5316
5317     QObject *object = component.create();
5318     QVERIFY(object != 0);
5319
5320     QCOMPARE(object->property("test").toBool(), true);
5321
5322     delete object;
5323 }
5324
5325 Q_DECLARE_METATYPE(QList<int>)
5326 Q_DECLARE_METATYPE(QList<qreal>)
5327 Q_DECLARE_METATYPE(QList<bool>)
5328 Q_DECLARE_METATYPE(QList<QString>)
5329 Q_DECLARE_METATYPE(QList<QUrl>)
5330 void tst_qqmlecmascript::sequenceConversionRead()
5331 {
5332     {
5333         QUrl qmlFile = testFileUrl("sequenceConversion.read.qml");
5334         QQmlComponent component(&engine, qmlFile);
5335         QObject *object = component.create();
5336         QVERIFY(object != 0);
5337         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5338         QVERIFY(seq != 0);
5339
5340         QMetaObject::invokeMethod(object, "readSequences");
5341         QList<int> intList; intList << 1 << 2 << 3 << 4;
5342         QCOMPARE(object->property("intListLength").toInt(), intList.length());
5343         QCOMPARE(object->property("intList").value<QList<int> >(), intList);
5344         QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
5345         QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
5346         QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
5347         QList<bool> boolList; boolList << true << false << true << false;
5348         QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
5349         QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
5350         QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5351         QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
5352         QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
5353         QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
5354         QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
5355         QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
5356         QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5357         QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
5358         QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
5359
5360         QMetaObject::invokeMethod(object, "readSequenceElements");
5361         QCOMPARE(object->property("intVal").toInt(), 2);
5362         QCOMPARE(object->property("qrealVal").toReal(), 2.2);
5363         QCOMPARE(object->property("boolVal").toBool(), false);
5364         QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
5365         QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
5366         QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
5367
5368         QMetaObject::invokeMethod(object, "enumerateSequenceElements");
5369         QCOMPARE(object->property("enumerationMatches").toBool(), true);
5370
5371         intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
5372         QQmlProperty seqProp(seq, "intListProperty");
5373         QCOMPARE(seqProp.read().value<QList<int> >(), intList);
5374         QQmlProperty seqProp2(seq, "intListProperty", &engine);
5375         QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
5376
5377         QMetaObject::invokeMethod(object, "testReferenceDeletion");
5378         QCOMPARE(object->property("referenceDeletion").toBool(), true);
5379
5380         delete object;
5381     }
5382
5383     {
5384         QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml");
5385         QQmlComponent component(&engine, qmlFile);
5386         QObject *object = component.create();
5387         QVERIFY(object != 0);
5388         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5389         QVERIFY(seq != 0);
5390
5391         // we haven't registered QList<QPoint> as a sequence type.
5392         QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5393         QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
5394         QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5395         QTest::ignoreMessage(QtWarningMsg, warningTwo.toLatin1().constData());
5396
5397         QMetaObject::invokeMethod(object, "performTest");
5398
5399         // QList<QPoint> has not been registered as a sequence type.
5400         QCOMPARE(object->property("pointListLength").toInt(), 0);
5401         QVERIFY(!object->property("pointList").isValid());
5402         QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5403         QQmlProperty seqProp(seq, "pointListProperty", &engine);
5404         QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
5405
5406         delete object;
5407     }
5408 }
5409
5410 void tst_qqmlecmascript::sequenceConversionWrite()
5411 {
5412     {
5413         QUrl qmlFile = testFileUrl("sequenceConversion.write.qml");
5414         QQmlComponent component(&engine, qmlFile);
5415         QObject *object = component.create();
5416         QVERIFY(object != 0);
5417         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5418         QVERIFY(seq != 0);
5419
5420         QMetaObject::invokeMethod(object, "writeSequences");
5421         QCOMPARE(object->property("success").toBool(), true);
5422
5423         QMetaObject::invokeMethod(object, "writeSequenceElements");
5424         QCOMPARE(object->property("success").toBool(), true);
5425
5426         QMetaObject::invokeMethod(object, "writeOtherElements");
5427         QCOMPARE(object->property("success").toBool(), true);
5428
5429         QMetaObject::invokeMethod(object, "testReferenceDeletion");
5430         QCOMPARE(object->property("referenceDeletion").toBool(), true);
5431
5432         delete object;
5433     }
5434
5435     {
5436         QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml");
5437         QQmlComponent component(&engine, qmlFile);
5438         QObject *object = component.create();
5439         QVERIFY(object != 0);
5440         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5441         QVERIFY(seq != 0);
5442
5443         // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
5444         QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to an unregistered type");
5445         QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5446
5447         QMetaObject::invokeMethod(object, "performTest");
5448
5449         QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
5450         QCOMPARE(seq->pointListProperty(), pointList);
5451
5452         delete object;
5453     }
5454 }
5455
5456 void tst_qqmlecmascript::sequenceConversionArray()
5457 {
5458     // ensure that in JS the returned sequences act just like normal JS Arrays.
5459     QUrl qmlFile = testFileUrl("sequenceConversion.array.qml");
5460     QQmlComponent component(&engine, qmlFile);
5461     QObject *object = component.create();
5462     QVERIFY(object != 0);
5463     QMetaObject::invokeMethod(object, "indexedAccess");
5464     QVERIFY(object->property("success").toBool());
5465     QMetaObject::invokeMethod(object, "arrayOperations");
5466     QVERIFY(object->property("success").toBool());
5467     QMetaObject::invokeMethod(object, "testEqualitySemantics");
5468     QVERIFY(object->property("success").toBool());
5469     QMetaObject::invokeMethod(object, "testReferenceDeletion");
5470     QCOMPARE(object->property("referenceDeletion").toBool(), true);
5471     delete object;
5472 }
5473
5474
5475 void tst_qqmlecmascript::sequenceConversionIndexes()
5476 {
5477     // ensure that we gracefully fail if unsupported index values are specified.
5478     // Qt container classes only support non-negative, signed integer index values.
5479     QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
5480     QQmlComponent component(&engine, qmlFile);
5481     QObject *object = component.create();
5482     QVERIFY(object != 0);
5483     QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
5484     QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
5485     QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
5486     QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
5487     QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
5488     QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
5489     QMetaObject::invokeMethod(object, "indexedAccess");
5490     QVERIFY(object->property("success").toBool());
5491     delete object;
5492 }
5493
5494 void tst_qqmlecmascript::sequenceConversionThreads()
5495 {
5496     // ensure that sequence conversion operations work correctly in a worker thread
5497     // and that serialisation between the main and worker thread succeeds.
5498     QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml");
5499     QQmlComponent component(&engine, qmlFile);
5500     QObject *object = component.create();
5501     QVERIFY(object != 0);
5502
5503     QMetaObject::invokeMethod(object, "testIntSequence");
5504     QTRY_VERIFY(object->property("finished").toBool());
5505     QVERIFY(object->property("success").toBool());
5506
5507     QMetaObject::invokeMethod(object, "testQrealSequence");
5508     QTRY_VERIFY(object->property("finished").toBool());
5509     QVERIFY(object->property("success").toBool());
5510
5511     QMetaObject::invokeMethod(object, "testBoolSequence");
5512     QTRY_VERIFY(object->property("finished").toBool());
5513     QVERIFY(object->property("success").toBool());
5514
5515     QMetaObject::invokeMethod(object, "testStringSequence");
5516     QTRY_VERIFY(object->property("finished").toBool());
5517     QVERIFY(object->property("success").toBool());
5518
5519     QMetaObject::invokeMethod(object, "testQStringSequence");
5520     QTRY_VERIFY(object->property("finished").toBool());
5521     QVERIFY(object->property("success").toBool());
5522
5523     QMetaObject::invokeMethod(object, "testUrlSequence");
5524     QTRY_VERIFY(object->property("finished").toBool());
5525     QVERIFY(object->property("success").toBool());
5526
5527     QMetaObject::invokeMethod(object, "testVariantSequence");
5528     QTRY_VERIFY(object->property("finished").toBool());
5529     QVERIFY(object->property("success").toBool());
5530
5531     delete object;
5532 }
5533
5534 void tst_qqmlecmascript::sequenceConversionBindings()
5535 {
5536     {
5537         QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml");
5538         QQmlComponent component(&engine, qmlFile);
5539         QObject *object = component.create();
5540         QVERIFY(object != 0);
5541         QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
5542         QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
5543         QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
5544         QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
5545         QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
5546         delete object;
5547     }
5548
5549     {
5550         QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml");
5551         QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
5552         QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5553         QQmlComponent component(&engine, qmlFile);
5554         QObject *object = component.create();
5555         QVERIFY(object != 0);
5556         delete object;
5557     }
5558 }
5559
5560 void tst_qqmlecmascript::sequenceConversionCopy()
5561 {
5562     QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml");
5563     QQmlComponent component(&engine, qmlFile);
5564     QObject *object = component.create();
5565     QVERIFY(object != 0);
5566     QMetaObject::invokeMethod(object, "testCopySequences");
5567     QCOMPARE(object->property("success").toBool(), true);
5568     QMetaObject::invokeMethod(object, "readSequenceCopyElements");
5569     QCOMPARE(object->property("success").toBool(), true);
5570     QMetaObject::invokeMethod(object, "testEqualitySemantics");
5571     QCOMPARE(object->property("success").toBool(), true);
5572     delete object;
5573 }
5574
5575 void tst_qqmlecmascript::assignSequenceTypes()
5576 {
5577     // test binding array to sequence type property
5578     {
5579     QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml"));
5580     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5581     QVERIFY(object != 0);
5582     QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5583     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5584     QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5585     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5586     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5587     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5588     delete object;
5589     }
5590
5591     // test binding literal to sequence type property
5592     {
5593     QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml"));
5594     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5595     QVERIFY(object != 0);
5596     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5597     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5598     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5599     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5600     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5601     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5602     delete object;
5603     }
5604
5605     // test binding single value to sequence type property
5606     {
5607     QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml"));
5608     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5609     QVERIFY(object != 0);
5610     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5611     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5612     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5613     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5614     delete object;
5615     }
5616
5617     // test assigning array to sequence type property in js function
5618     {
5619     QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml"));
5620     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5621     QVERIFY(object != 0);
5622     QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5623     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5624     QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5625     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5626     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5627     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5628     delete object;
5629     }
5630
5631     // test assigning literal to sequence type property in js function
5632     {
5633     QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml"));
5634     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5635     QVERIFY(object != 0);
5636     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5637     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5638     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5639     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5640     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5641     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5642     delete object;
5643     }
5644
5645     // test assigning single value to sequence type property in js function
5646     {
5647     QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml"));
5648     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5649     QVERIFY(object != 0);
5650     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5651     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5652     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5653     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5654     delete object;
5655     }
5656
5657     // test QList<QUrl> literal assignment and binding assignment causes url resolution when required
5658     {
5659     QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml"));
5660     QObject *object = component.create();
5661     QVERIFY(object != 0);
5662     MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
5663     MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
5664     MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
5665     MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
5666     MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco5"));
5667     QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0);
5668     QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5669     QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5670     QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5671     QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5672     QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5673     delete object;
5674     }
5675 }
5676
5677 // Test that assigning a null object works 
5678 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
5679 void tst_qqmlecmascript::nullObjectBinding()
5680 {
5681     QQmlComponent component(&engine, testFileUrl("nullObjectBinding.qml"));
5682
5683     QObject *object = component.create();
5684     QVERIFY(object != 0);
5685
5686     QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
5687
5688     delete object;
5689 }
5690
5691 // Test that bindings don't evaluate once the engine has been destroyed
5692 void tst_qqmlecmascript::deletedEngine()
5693 {
5694     QQmlEngine *engine = new QQmlEngine;
5695     QQmlComponent component(engine, testFileUrl("deletedEngine.qml"));
5696
5697     QObject *object = component.create();
5698     QVERIFY(object != 0);
5699
5700     QCOMPARE(object->property("a").toInt(), 39);
5701     object->setProperty("b", QVariant(9));
5702     QCOMPARE(object->property("a").toInt(), 117);
5703
5704     delete engine;
5705
5706     QCOMPARE(object->property("a").toInt(), 117);
5707     object->setProperty("b", QVariant(10));
5708     QCOMPARE(object->property("a").toInt(), 117);
5709
5710     delete object;
5711 }
5712
5713 // Test the crashing part of QTBUG-9705
5714 void tst_qqmlecmascript::libraryScriptAssert()
5715 {
5716     QQmlComponent component(&engine, testFileUrl("libraryScriptAssert.qml"));
5717
5718     QObject *object = component.create();
5719     QVERIFY(object != 0);
5720
5721     delete object;
5722 }
5723
5724 void tst_qqmlecmascript::variantsAssignedUndefined()
5725 {
5726     QQmlComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml"));
5727
5728     QObject *object = component.create();
5729     QVERIFY(object != 0);
5730
5731     QCOMPARE(object->property("test1").toInt(), 10);
5732     QCOMPARE(object->property("test2").toInt(), 11);
5733
5734     object->setProperty("runTest", true);
5735
5736     QCOMPARE(object->property("test1"), QVariant());
5737     QCOMPARE(object->property("test2"), QVariant());
5738
5739
5740     delete object;
5741 }
5742
5743 void tst_qqmlecmascript::qtbug_9792()
5744 {
5745     QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml"));
5746
5747     QQmlContext *context = new QQmlContext(engine.rootContext());
5748
5749     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
5750     QVERIFY(object != 0);
5751
5752     QTest::ignoreMessage(QtDebugMsg, "Hello world!");
5753     object->basicSignal();
5754
5755     delete context;
5756
5757     transientErrorsMsgCount = 0;
5758     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5759
5760     object->basicSignal();
5761     
5762     qInstallMsgHandler(old);
5763
5764     QCOMPARE(transientErrorsMsgCount, 0);
5765
5766     delete object;
5767 }
5768
5769 // Verifies that QQmlGuard<>s used in the vmemetaobject are cleaned correctly
5770 void tst_qqmlecmascript::qtcreatorbug_1289()
5771 {
5772     QQmlComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml"));
5773
5774     QObject *o = component.create();
5775     QVERIFY(o != 0);
5776
5777     QObject *nested = qvariant_cast<QObject *>(o->property("object"));
5778     QVERIFY(nested != 0);
5779
5780     QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
5781
5782     delete nested;
5783     nested = qvariant_cast<QObject *>(o->property("object"));
5784     QVERIFY(nested == 0);
5785
5786     // If the bug is present, the next line will crash
5787     delete o;
5788 }
5789
5790 // Test that we shut down without stupid warnings
5791 void tst_qqmlecmascript::noSpuriousWarningsAtShutdown()
5792 {
5793     {
5794     QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml"));
5795
5796     QObject *o = component.create();
5797
5798     transientErrorsMsgCount = 0;
5799     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5800
5801     delete o;
5802
5803     qInstallMsgHandler(old);
5804
5805     QCOMPARE(transientErrorsMsgCount, 0);
5806     }
5807
5808
5809     {
5810     QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml"));
5811
5812     QObject *o = component.create();
5813
5814     transientErrorsMsgCount = 0;
5815     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5816
5817     delete o;
5818
5819     qInstallMsgHandler(old);
5820
5821     QCOMPARE(transientErrorsMsgCount, 0);
5822     }
5823 }
5824
5825 void tst_qqmlecmascript::canAssignNullToQObject()
5826 {
5827     {
5828     QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml"));
5829
5830     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5831     QVERIFY(o != 0);
5832
5833     QVERIFY(o->objectProperty() != 0);
5834
5835     o->setProperty("runTest", true);
5836
5837     QVERIFY(o->objectProperty() == 0);
5838
5839     delete o;
5840     }
5841
5842     {
5843     QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml"));
5844
5845     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5846     QVERIFY(o != 0);
5847
5848     QVERIFY(o->objectProperty() == 0);
5849
5850     delete o;
5851     }
5852 }
5853
5854 void tst_qqmlecmascript::functionAssignment_fromBinding()
5855 {
5856     QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml"));
5857
5858     QString url = component.url().toString();
5859     QString w1 = url + ":4:25: Unable to assign a function to a property of any type other than var.";
5860     QString w2 = url + ":5:25: Invalid use of Qt.binding() in a binding declaration.";
5861     QString w3 = url + ":6:21: Invalid use of Qt.binding() in a binding declaration.";
5862     QString w4 = url + ":7:15: Invalid use of Qt.binding() in a binding declaration.";
5863     QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5864     QTest::ignoreMessage(QtWarningMsg, w2.toLatin1().constData());
5865     QTest::ignoreMessage(QtWarningMsg, w3.toLatin1().constData());
5866     QTest::ignoreMessage(QtWarningMsg, w4.toLatin1().constData());
5867
5868     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5869     QVERIFY(o != 0);
5870
5871     QVERIFY(!o->property("a").isValid());
5872
5873     delete o;
5874 }
5875
5876 void tst_qqmlecmascript::functionAssignment_fromJS()
5877 {
5878     QFETCH(QString, triggerProperty);
5879
5880     QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5881     QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5882
5883     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5884     QVERIFY(o != 0);
5885     QVERIFY(!o->property("a").isValid());
5886
5887     o->setProperty("aNumber", QVariant(5));
5888     o->setProperty(triggerProperty.toUtf8().constData(), true);
5889     QCOMPARE(o->property("a"), QVariant(50));
5890
5891     o->setProperty("aNumber", QVariant(10));
5892     QCOMPARE(o->property("a"), QVariant(100));
5893
5894     delete o;
5895 }
5896
5897 void tst_qqmlecmascript::functionAssignment_fromJS_data()
5898 {
5899     QTest::addColumn<QString>("triggerProperty");
5900
5901     QTest::newRow("assign to property") << "assignToProperty";
5902     QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
5903
5904     QTest::newRow("assign to value type") << "assignToValueType";
5905
5906     QTest::newRow("use 'this'") << "assignWithThis";
5907     QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
5908 }
5909
5910 void tst_qqmlecmascript::functionAssignmentfromJS_invalid()
5911 {
5912     QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5913     QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5914
5915     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5916     QVERIFY(o != 0);
5917     QVERIFY(!o->property("a").isValid());
5918
5919     o->setProperty("assignFuncWithoutReturn", true);
5920     QVERIFY(!o->property("a").isValid());
5921
5922     QString url = component.url().toString();
5923     QString warning = url + ":67:17: Unable to assign QString to int";
5924     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5925     o->setProperty("assignWrongType", true);
5926
5927     warning = url + ":71:29: Unable to assign QString to int";
5928     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5929     o->setProperty("assignWrongTypeToValueType", true);
5930
5931     delete o;
5932 }
5933
5934 void tst_qqmlecmascript::functionAssignment_afterBinding()
5935 {
5936     QQmlComponent component(&engine, testFileUrl("functionAssignment.3.qml"));
5937
5938     QString url = component.url().toString();
5939     QString w1 = url + ":16: Error: Cannot assign JavaScript function to int";
5940     QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5941
5942     QObject *o = component.create();
5943     QVERIFY(o != 0);
5944     QCOMPARE(o->property("t1"), QVariant::fromValue<int>(4)); // should have bound
5945     QCOMPARE(o->property("t2"), QVariant::fromValue<int>(2)); // should not have changed
5946
5947     delete o;
5948 }
5949
5950 void tst_qqmlecmascript::eval()
5951 {
5952     QQmlComponent component(&engine, testFileUrl("eval.qml"));
5953
5954     QObject *o = component.create();
5955     QVERIFY(o != 0);
5956
5957     QCOMPARE(o->property("test1").toBool(), true);
5958     QCOMPARE(o->property("test2").toBool(), true);
5959     QCOMPARE(o->property("test3").toBool(), true);
5960     QCOMPARE(o->property("test4").toBool(), true);
5961     QCOMPARE(o->property("test5").toBool(), true);
5962
5963     delete o;
5964 }
5965
5966 void tst_qqmlecmascript::function()
5967 {
5968     QQmlComponent component(&engine, testFileUrl("function.qml"));
5969
5970     QObject *o = component.create();
5971     QVERIFY(o != 0);
5972
5973     QCOMPARE(o->property("test1").toBool(), true);
5974     QCOMPARE(o->property("test2").toBool(), true);
5975     QCOMPARE(o->property("test3").toBool(), true);
5976
5977     delete o;
5978 }
5979
5980 void tst_qqmlecmascript::functionException()
5981 {
5982     // QTBUG-24037 - shouldn't crash.
5983     QString errstr = testFileUrl("v8functionException.qml").toString() + QLatin1String(":13: SyntaxError: Unexpected token ILLEGAL");
5984     QTest::ignoreMessage(QtWarningMsg, qPrintable(errstr));
5985     QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Exception occurred during compilation of function: dynamicSlot()");
5986     QQmlComponent component(&engine, testFileUrl("v8functionException.qml"));
5987     QObject *o = component.create();
5988     QVERIFY(o != 0);
5989     QMetaObject::invokeMethod(o, "dynamicSlot");
5990     delete o;
5991 }
5992
5993 // Test the "Qt.include" method
5994 void tst_qqmlecmascript::include()
5995 {
5996     // Non-library relative include
5997     {
5998     QQmlComponent component(&engine, testFileUrl("include.qml"));
5999     QObject *o = component.create();
6000     QVERIFY(o != 0);
6001
6002     QCOMPARE(o->property("test0").toInt(), 99);
6003     QCOMPARE(o->property("test1").toBool(), true);
6004     QCOMPARE(o->property("test2").toBool(), true);
6005     QCOMPARE(o->property("test2_1").toBool(), true);
6006     QCOMPARE(o->property("test3").toBool(), true);
6007     QCOMPARE(o->property("test3_1").toBool(), true);
6008
6009     delete o;
6010     }
6011
6012     // Library relative include
6013     {
6014     QQmlComponent component(&engine, testFileUrl("include_shared.qml"));
6015     QObject *o = component.create();
6016     QVERIFY(o != 0);
6017
6018     QCOMPARE(o->property("test0").toInt(), 99);
6019     QCOMPARE(o->property("test1").toBool(), true);
6020     QCOMPARE(o->property("test2").toBool(), true);
6021     QCOMPARE(o->property("test2_1").toBool(), true);
6022     QCOMPARE(o->property("test3").toBool(), true);
6023     QCOMPARE(o->property("test3_1").toBool(), true);
6024
6025     delete o;
6026     }
6027
6028     // Callback
6029     {
6030     QQmlComponent component(&engine, testFileUrl("include_callback.qml"));
6031     QObject *o = component.create();
6032     QVERIFY(o != 0);
6033
6034     QCOMPARE(o->property("test1").toBool(), true);
6035     QCOMPARE(o->property("test2").toBool(), true);
6036     QCOMPARE(o->property("test3").toBool(), true);
6037     QCOMPARE(o->property("test4").toBool(), true);
6038     QCOMPARE(o->property("test5").toBool(), true);
6039     QCOMPARE(o->property("test6").toBool(), true);
6040
6041     delete o;
6042     }
6043
6044     // Including file with ".pragma library"
6045     {
6046     QQmlComponent component(&engine, testFileUrl("include_pragma.qml"));
6047     QObject *o = component.create();
6048     QVERIFY(o != 0);
6049     QCOMPARE(o->property("test1").toInt(), 100);
6050
6051     delete o;
6052     }
6053
6054     // Remote - success
6055     {
6056     TestHTTPServer server(8111);
6057     QVERIFY(server.isValid());
6058     server.serveDirectory(dataDirectory());
6059
6060     QQmlComponent component(&engine, testFileUrl("include_remote.qml"));
6061     QObject *o = component.create();
6062     QVERIFY(o != 0);
6063
6064     QTRY_VERIFY(o->property("done").toBool() == true);
6065     QTRY_VERIFY(o->property("done2").toBool() == true);
6066
6067     QCOMPARE(o->property("test1").toBool(), true);
6068     QCOMPARE(o->property("test2").toBool(), true);
6069     QCOMPARE(o->property("test3").toBool(), true);
6070     QCOMPARE(o->property("test4").toBool(), true);
6071     QCOMPARE(o->property("test5").toBool(), true);
6072
6073     QCOMPARE(o->property("test6").toBool(), true);
6074     QCOMPARE(o->property("test7").toBool(), true);
6075     QCOMPARE(o->property("test8").toBool(), true);
6076     QCOMPARE(o->property("test9").toBool(), true);
6077     QCOMPARE(o->property("test10").toBool(), true);
6078
6079     delete o;
6080     }
6081
6082     // Remote - error
6083     {
6084     TestHTTPServer server(8111);
6085     QVERIFY(server.isValid());
6086     server.serveDirectory(dataDirectory());
6087
6088     QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml"));
6089     QObject *o = component.create();
6090     QVERIFY(o != 0);
6091
6092     QTRY_VERIFY(o->property("done").toBool() == true);
6093
6094     QCOMPARE(o->property("test1").toBool(), true);
6095     QCOMPARE(o->property("test2").toBool(), true);
6096     QCOMPARE(o->property("test3").toBool(), true);
6097
6098     delete o;
6099     }
6100 }
6101
6102 void tst_qqmlecmascript::signalHandlers()
6103 {
6104     QQmlComponent component(&engine, testFileUrl("signalHandlers.qml"));
6105     QObject *o = component.create();
6106     QVERIFY(o != 0);
6107
6108     QVERIFY(o->property("count").toInt() == 0);
6109     QMetaObject::invokeMethod(o, "testSignalCall");
6110     QCOMPARE(o->property("count").toInt(), 1);
6111
6112     QMetaObject::invokeMethod(o, "testSignalHandlerCall");
6113     QCOMPARE(o->property("count").toInt(), 1);
6114     QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
6115
6116     QVERIFY(o->property("funcCount").toInt() == 0);
6117     QMetaObject::invokeMethod(o, "testSignalConnection");
6118     QCOMPARE(o->property("funcCount").toInt(), 1);
6119
6120     QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
6121     QCOMPARE(o->property("funcCount").toInt(), 2);
6122
6123     QMetaObject::invokeMethod(o, "testSignalDefined");
6124     QCOMPARE(o->property("definedResult").toBool(), true);
6125
6126     QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
6127     QCOMPARE(o->property("definedHandlerResult").toBool(), true);
6128
6129     delete o;
6130 }
6131
6132 void tst_qqmlecmascript::qtbug_10696()
6133 {
6134     QQmlComponent component(&engine, testFileUrl("qtbug_10696.qml"));
6135     QObject *o = component.create();
6136     QVERIFY(o != 0);
6137     delete o;
6138 }
6139
6140 void tst_qqmlecmascript::qtbug_11606()
6141 {
6142     QQmlComponent component(&engine, testFileUrl("qtbug_11606.qml"));
6143     QObject *o = component.create();
6144     QVERIFY(o != 0);
6145     QCOMPARE(o->property("test").toBool(), true);
6146     delete o;
6147 }
6148
6149 void tst_qqmlecmascript::qtbug_11600()
6150 {
6151     QQmlComponent component(&engine, testFileUrl("qtbug_11600.qml"));
6152     QObject *o = component.create();
6153     QVERIFY(o != 0);
6154     QCOMPARE(o->property("test").toBool(), true);
6155     delete o;
6156 }
6157
6158 void tst_qqmlecmascript::qtbug_21864()
6159 {
6160     QQmlComponent component(&engine, testFileUrl("qtbug_21864.qml"));
6161     QObject *o = component.create();
6162     QVERIFY(o != 0);
6163     QCOMPARE(o->property("test").toBool(), true);
6164     delete o;
6165 }
6166
6167 void tst_qqmlecmascript::rewriteMultiLineStrings()
6168 {
6169     {
6170         // QTBUG-23387
6171         QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml"));
6172         QObject *o = component.create();
6173         QVERIFY(o != 0);
6174         QTRY_COMPARE(o->property("test").toBool(), true);
6175         delete o;
6176     }
6177
6178     {
6179         QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings_crlf.1.qml"));
6180         QObject *o = component.create();
6181         QVERIFY(o != 0);
6182         delete o;
6183     }
6184 }
6185
6186 void tst_qqmlecmascript::qobjectConnectionListExceptionHandling()
6187 {
6188     // QTBUG-23375
6189     QQmlComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml"));
6190     QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined");
6191     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6192     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6193     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6194     QObject *o = component.create();
6195     QVERIFY(o != 0);
6196     QCOMPARE(o->property("test").toBool(), true);
6197     delete o;
6198 }
6199
6200 // Reading and writing non-scriptable properties should fail
6201 void tst_qqmlecmascript::nonscriptable()
6202 {
6203     QQmlComponent component(&engine, testFileUrl("nonscriptable.qml"));
6204     QObject *o = component.create();
6205     QVERIFY(o != 0);
6206     QCOMPARE(o->property("readOk").toBool(), true);
6207     QCOMPARE(o->property("writeOk").toBool(), true);
6208     delete o;
6209 }
6210
6211 // deleteLater() should not be callable from QML
6212 void tst_qqmlecmascript::deleteLater()
6213 {
6214     QQmlComponent component(&engine, testFileUrl("deleteLater.qml"));
6215     QObject *o = component.create();
6216     QVERIFY(o != 0);
6217     QCOMPARE(o->property("test").toBool(), true);
6218     delete o;
6219 }
6220
6221 // objectNameChanged() should be usable from QML
6222 void tst_qqmlecmascript::objectNameChangedSignal()
6223 {
6224     QQmlComponent component(&engine, testFileUrl("objectNameChangedSignal.qml"));
6225     QObject *o = component.create();
6226     QVERIFY(o != 0);
6227     QCOMPARE(o->property("test").toBool(), false);
6228     o->setObjectName("obj");
6229     QCOMPARE(o->property("test").toBool(), true);
6230     delete o;
6231 }
6232
6233 // destroyed() should not be usable from QML
6234 void tst_qqmlecmascript::destroyedSignal()
6235 {
6236     QQmlComponent component(&engine, testFileUrl("destroyedSignal.qml"));
6237     QVERIFY(component.isError());
6238
6239     QString expectedErrorString = component.url().toString() + QLatin1String(":5:5: Cannot assign to non-existent property \"onDestroyed\"");
6240     QCOMPARE(component.errors().at(0).toString(), expectedErrorString);
6241 }
6242
6243 void tst_qqmlecmascript::in()
6244 {
6245     QQmlComponent component(&engine, testFileUrl("in.qml"));
6246     QObject *o = component.create();
6247     QVERIFY(o != 0);
6248     QCOMPARE(o->property("test1").toBool(), true);
6249     QCOMPARE(o->property("test2").toBool(), true);
6250     delete o;
6251 }
6252
6253 void tst_qqmlecmascript::typeOf()
6254 {
6255     QQmlComponent component(&engine, testFileUrl("typeOf.qml"));
6256
6257     QObject *o = component.create();
6258     QVERIFY(o != 0);
6259
6260     QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
6261     QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
6262     QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
6263     QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
6264     QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
6265     QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
6266     QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
6267     QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
6268     QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
6269
6270     delete o;
6271 }
6272
6273 void tst_qqmlecmascript::qtbug_24448()
6274 {
6275     QQmlComponent component(&engine, testFileUrl("qtbug_24448.qml"));
6276     QScopedPointer<QObject> o(component.create());
6277     QVERIFY(o != 0);
6278     QVERIFY(o->property("test").toBool());
6279 }
6280
6281 void tst_qqmlecmascript::sharedAttachedObject()
6282 {
6283     QQmlComponent component(&engine, testFileUrl("sharedAttachedObject.qml"));
6284     QObject *o = component.create();
6285     QVERIFY(o != 0);
6286     QCOMPARE(o->property("test1").toBool(), true);
6287     QCOMPARE(o->property("test2").toBool(), true);
6288     delete o;
6289 }
6290
6291 // QTBUG-13999
6292 void tst_qqmlecmascript::objectName()
6293 {
6294     QQmlComponent component(&engine, testFileUrl("objectName.qml"));
6295     QObject *o = component.create();
6296     QVERIFY(o != 0);
6297
6298     QCOMPARE(o->property("test1").toString(), QString("hello"));
6299     QCOMPARE(o->property("test2").toString(), QString("ell"));
6300
6301     o->setObjectName("world");
6302
6303     QCOMPARE(o->property("test1").toString(), QString("world"));
6304     QCOMPARE(o->property("test2").toString(), QString("orl"));
6305
6306     delete o;
6307 }
6308
6309 void tst_qqmlecmascript::writeRemovesBinding()
6310 {
6311     QQmlComponent component(&engine, testFileUrl("writeRemovesBinding.qml"));
6312     QObject *o = component.create();
6313     QVERIFY(o != 0);
6314
6315     QCOMPARE(o->property("test").toBool(), true);
6316
6317     delete o;
6318 }
6319
6320 // Test bindings assigned to alias properties actually assign to the alias' target
6321 void tst_qqmlecmascript::aliasBindingsAssignCorrectly()
6322 {
6323     QQmlComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml"));
6324     QObject *o = component.create();
6325     QVERIFY(o != 0);
6326
6327     QCOMPARE(o->property("test").toBool(), true);
6328
6329     delete o;
6330 }
6331
6332 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
6333 void tst_qqmlecmascript::aliasBindingsOverrideTarget()
6334 {
6335     { 
6336     QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml"));
6337     QObject *o = component.create();
6338     QVERIFY(o != 0);
6339
6340     QCOMPARE(o->property("test").toBool(), true);
6341
6342     delete o;
6343     }
6344
6345     {
6346     QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml"));
6347     QObject *o = component.create();
6348     QVERIFY(o != 0);
6349
6350     QCOMPARE(o->property("test").toBool(), true);
6351
6352     delete o;
6353     }
6354
6355     {
6356     QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml"));
6357     QObject *o = component.create();
6358     QVERIFY(o != 0);
6359
6360     QCOMPARE(o->property("test").toBool(), true);
6361
6362     delete o;
6363     }
6364 }
6365
6366 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
6367 void tst_qqmlecmascript::aliasWritesOverrideBindings()
6368 {
6369     {
6370     QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml"));
6371     QObject *o = component.create();
6372     QVERIFY(o != 0);
6373
6374     QCOMPARE(o->property("test").toBool(), true);
6375
6376     delete o;
6377     }
6378
6379     {
6380     QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml"));
6381     QObject *o = component.create();
6382     QVERIFY(o != 0);
6383
6384     QCOMPARE(o->property("test").toBool(), true);
6385
6386     delete o;
6387     }
6388
6389     {
6390     QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml"));
6391     QObject *o = component.create();
6392     QVERIFY(o != 0);
6393
6394     QCOMPARE(o->property("test").toBool(), true);
6395
6396     delete o;
6397     }
6398 }
6399
6400 // Allow an alais to a composite element
6401 // QTBUG-20200
6402 void tst_qqmlecmascript::aliasToCompositeElement()
6403 {
6404     QQmlComponent component(&engine, testFileUrl("aliasToCompositeElement.qml"));
6405
6406     QObject *object = component.create();
6407     QVERIFY(object != 0);
6408
6409     delete object;
6410 }
6411
6412 void tst_qqmlecmascript::qtbug_20344()
6413 {
6414     QQmlComponent component(&engine, testFileUrl("qtbug_20344.qml"));
6415
6416     QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
6417     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6418
6419     QObject *object = component.create();
6420     QVERIFY(object != 0);
6421
6422     delete object;
6423 }
6424
6425 void tst_qqmlecmascript::revisionErrors()
6426 {
6427     {
6428         QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml"));
6429         QString url = component.url().toString();
6430
6431         QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6432         QString warning2 = url + ":11: ReferenceError: prop2 is not defined";
6433         QString warning3 = url + ":13: ReferenceError: method2 is not defined";
6434
6435         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6436         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6437         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6438         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6439         QVERIFY(object != 0);
6440         delete object;
6441     }
6442     {
6443         QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml"));
6444         QString url = component.url().toString();
6445
6446         // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
6447         // method2, prop2 from MyRevisionedClass not available
6448         // method4, prop4 from MyRevisionedSubclass not available
6449         QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6450         QString warning2 = url + ":14: ReferenceError: prop2 is not defined";
6451         QString warning3 = url + ":10: ReferenceError: prop4 is not defined";
6452         QString warning4 = url + ":16: ReferenceError: prop4 is not defined";
6453         QString warning5 = url + ":20: ReferenceError: method2 is not defined";
6454
6455         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6456         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6457         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6458         QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
6459         QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
6460         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6461         QVERIFY(object != 0);
6462         delete object;
6463     }
6464     {
6465         QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml"));
6466         QString url = component.url().toString();
6467
6468         // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
6469         // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
6470         QString warning1 = url + ":30: ReferenceError: methodD is not defined";
6471         QString warning2 = url + ":10: ReferenceError: propD is not defined";
6472         QString warning3 = url + ":20: ReferenceError: propD is not defined";
6473         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6474         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6475         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6476         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6477         QVERIFY(object != 0);
6478         delete object;
6479     }
6480 }
6481
6482 void tst_qqmlecmascript::revision()
6483 {
6484     {
6485         QQmlComponent component(&engine, testFileUrl("metaobjectRevision.qml"));
6486         QString url = component.url().toString();
6487
6488         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6489         QVERIFY(object != 0);
6490         delete object;
6491     }
6492     {
6493         QQmlComponent component(&engine, testFileUrl("metaobjectRevision2.qml"));
6494         QString url = component.url().toString();
6495
6496         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6497         QVERIFY(object != 0);
6498         delete object;
6499     }
6500     {
6501         QQmlComponent component(&engine, testFileUrl("metaobjectRevision3.qml"));
6502         QString url = component.url().toString();
6503
6504         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6505         QVERIFY(object != 0);
6506         delete object;
6507     }
6508     // Test that non-root classes can resolve revisioned methods
6509     {
6510         QQmlComponent component(&engine, testFileUrl("metaobjectRevision4.qml"));
6511
6512         QObject *object = component.create();
6513         QVERIFY(object != 0);
6514         QCOMPARE(object->property("test").toReal(), 11.);
6515         delete object;
6516     }
6517 }
6518
6519 void tst_qqmlecmascript::realToInt()
6520 {
6521     QQmlComponent component(&engine, testFileUrl("realToInt.qml"));
6522     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6523     QVERIFY(object != 0);
6524
6525     QMetaObject::invokeMethod(object, "test1");
6526     QCOMPARE(object->value(), int(4));
6527     QMetaObject::invokeMethod(object, "test2");
6528     QCOMPARE(object->value(), int(8));
6529 }
6530
6531 void tst_qqmlecmascript::urlProperty()
6532 {
6533     {
6534         QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml"));
6535         MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6536         QVERIFY(object != 0);
6537         object->setStringProperty("http://qt-project.org");
6538         QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
6539         QCOMPARE(object->intProperty(), 123);
6540         QCOMPARE(object->value(), 1);
6541         QCOMPARE(object->property("result").toBool(), true);
6542     }
6543 }
6544
6545 void tst_qqmlecmascript::urlPropertyWithEncoding()
6546 {
6547     {
6548         QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml"));
6549         MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6550         QVERIFY(object != 0);
6551         object->setStringProperty("http://qt-project.org");
6552         QUrl encoded;
6553         encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6554         QCOMPARE(object->urlProperty(), encoded);
6555         QCOMPARE(object->value(), 0);   // Interpreting URL as string yields canonicalised version
6556         QCOMPARE(object->property("result").toBool(), true);
6557     }
6558 }
6559
6560 void tst_qqmlecmascript::urlListPropertyWithEncoding()
6561 {
6562     {
6563         QQmlComponent component(&engine, testFileUrl("urlListProperty.qml"));
6564         QObject *object = component.create();
6565         QVERIFY(object != 0);
6566         MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
6567         MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
6568         MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
6569         MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
6570         QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0);
6571         QUrl encoded;
6572         encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6573         QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded));
6574         QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded));
6575         QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6576         QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6577         delete object;
6578     }
6579 }
6580
6581 void tst_qqmlecmascript::dynamicString()
6582 {
6583     QQmlComponent component(&engine, testFileUrl("dynamicString.qml"));
6584     QObject *object = component.create();
6585     QVERIFY(object != 0);
6586     QCOMPARE(object->property("stringProperty").toString(),
6587              QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
6588 }
6589
6590 void tst_qqmlecmascript::deleteLaterObjectMethodCall()
6591 {
6592     QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml"));
6593     QObject *object = component.create();
6594     QVERIFY(object != 0);
6595 }
6596
6597 void tst_qqmlecmascript::automaticSemicolon()
6598 {
6599     QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml"));
6600     QObject *object = component.create();
6601     QVERIFY(object != 0);
6602 }
6603
6604 void tst_qqmlecmascript::unaryExpression()
6605 {
6606     QQmlComponent component(&engine, testFileUrl("unaryExpression.qml"));
6607     QObject *object = component.create();
6608     QVERIFY(object != 0);
6609 }
6610
6611 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
6612 void tst_qqmlecmascript::doubleEvaluate()
6613 {
6614     QQmlComponent component(&engine, testFileUrl("doubleEvaluate.qml"));
6615     QObject *object = component.create();
6616     QVERIFY(object != 0);
6617     WriteCounter *wc = qobject_cast<WriteCounter *>(object);
6618     QVERIFY(wc != 0);
6619     QCOMPARE(wc->count(), 1);
6620
6621     wc->setProperty("x", 9);
6622
6623     QCOMPARE(wc->count(), 2);
6624
6625     delete object;
6626 }
6627
6628 static QStringList messages;
6629 static void captureMsgHandler(QtMsgType, const char *msg)
6630 {
6631     messages.append(QLatin1String(msg));
6632 }
6633
6634 void tst_qqmlecmascript::nonNotifyable()
6635 {
6636     QV4Compiler::enableV4(false);
6637     QQmlComponent component(&engine, testFileUrl("nonNotifyable.qml"));
6638     QV4Compiler::enableV4(true);
6639
6640     QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
6641     messages.clear();
6642     QObject *object = component.create();
6643     qInstallMsgHandler(old);
6644
6645     QVERIFY(object != 0);
6646
6647     QString expected1 = QLatin1String("QQmlExpression: Expression ") +
6648                         component.url().toString() +
6649                         QLatin1String(":5 depends on non-NOTIFYable properties:");
6650     QString expected2 = QLatin1String("    ") +
6651                         QLatin1String(object->metaObject()->className()) +
6652                         QLatin1String("::value");
6653
6654     QCOMPARE(messages.length(), 2);
6655     QCOMPARE(messages.at(0), expected1);
6656     QCOMPARE(messages.at(1), expected2);
6657
6658     delete object;
6659 }
6660
6661 void tst_qqmlecmascript::forInLoop()
6662 {
6663     QQmlComponent component(&engine, testFileUrl("forInLoop.qml"));
6664     QObject *object = component.create();
6665     QVERIFY(object != 0);
6666
6667     QMetaObject::invokeMethod(object, "listProperty");
6668
6669     QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
6670     QCOMPARE(r.size(), 3);
6671     QCOMPARE(r[0],QLatin1String("0=obj1"));
6672     QCOMPARE(r[1],QLatin1String("1=obj2"));
6673     QCOMPARE(r[2],QLatin1String("2=obj3"));
6674
6675     //TODO: should test for in loop for other objects (such as QObjects) as well.
6676
6677     delete object;
6678 }
6679
6680 // An object the binding depends on is deleted while the binding is still running
6681 void tst_qqmlecmascript::deleteWhileBindingRunning()
6682 {
6683     QQmlComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml"));
6684     QObject *object = component.create();
6685     QVERIFY(object != 0);
6686     delete object;
6687 }
6688
6689 void tst_qqmlecmascript::qtbug_22679()
6690 {
6691     MyQmlObject object;
6692     object.setStringProperty(QLatin1String("Please work correctly"));
6693     engine.rootContext()->setContextProperty("contextProp", &object);
6694
6695     QQmlComponent component(&engine, testFileUrl("qtbug_22679.qml"));
6696     qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6697     QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6698
6699     QObject *o = component.create();
6700     QVERIFY(o != 0);
6701     QCOMPARE(warningsSpy.count(), 0);
6702     delete o;
6703 }
6704
6705 void tst_qqmlecmascript::qtbug_22843_data()
6706 {
6707     QTest::addColumn<bool>("library");
6708
6709     QTest::newRow("without .pragma library") << false;
6710     QTest::newRow("with .pragma library") << true;
6711 }
6712
6713 void tst_qqmlecmascript::qtbug_22843()
6714 {
6715     QFETCH(bool, library);
6716
6717     QString fileName("qtbug_22843");
6718     if (library)
6719         fileName += QLatin1String(".library");
6720     fileName += QLatin1String(".qml");
6721
6722     QQmlComponent component(&engine, testFileUrl(fileName));
6723     QString url = component.url().toString();
6724     QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )");
6725     QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'");
6726
6727     qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6728     QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6729     for (int x = 0; x < 3; ++x) {
6730         warningsSpy.clear();
6731         // For libraries, only the first import attempt should produce a
6732         // SyntaxError warning; subsequent component creation should not
6733         // attempt to reload the script.
6734         bool expectSyntaxError = !library || (x == 0);
6735         if (expectSyntaxError)
6736             QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
6737         QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
6738         QObject *object = component.create();
6739         QVERIFY(object != 0);
6740         QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
6741         delete object;
6742     }
6743 }
6744
6745
6746 void tst_qqmlecmascript::switchStatement()
6747 {
6748     {
6749         QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml"));
6750         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6751         QVERIFY(object != 0);
6752
6753         // `object->value()' is the number of executed statements
6754
6755         object->setStringProperty("A");
6756         QCOMPARE(object->value(), 5);
6757
6758         object->setStringProperty("S");
6759         QCOMPARE(object->value(), 3);
6760
6761         object->setStringProperty("D");
6762         QCOMPARE(object->value(), 3);
6763
6764         object->setStringProperty("F");
6765         QCOMPARE(object->value(), 4);
6766
6767         object->setStringProperty("something else");
6768         QCOMPARE(object->value(), 1);
6769     }
6770
6771     {
6772         QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml"));
6773         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6774         QVERIFY(object != 0);
6775
6776         // `object->value()' is the number of executed statements
6777
6778         object->setStringProperty("A");
6779         QCOMPARE(object->value(), 5);
6780
6781         object->setStringProperty("S");
6782         QCOMPARE(object->value(), 3);
6783
6784         object->setStringProperty("D");
6785         QCOMPARE(object->value(), 3);
6786
6787         object->setStringProperty("F");
6788         QCOMPARE(object->value(), 3);
6789
6790         object->setStringProperty("something else");
6791         QCOMPARE(object->value(), 4);
6792     }
6793
6794     {
6795         QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml"));
6796         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6797         QVERIFY(object != 0);
6798
6799         // `object->value()' is the number of executed statements
6800
6801         object->setStringProperty("A");
6802         QCOMPARE(object->value(), 5);
6803
6804         object->setStringProperty("S");
6805         QCOMPARE(object->value(), 3);
6806
6807         object->setStringProperty("D");
6808         QCOMPARE(object->value(), 3);
6809
6810         object->setStringProperty("F");
6811         QCOMPARE(object->value(), 3);
6812
6813         object->setStringProperty("something else");
6814         QCOMPARE(object->value(), 6);
6815     }
6816
6817     {
6818         QQmlComponent component(&engine, testFileUrl("switchStatement.4.qml"));
6819
6820         QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
6821         QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6822
6823         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6824         QVERIFY(object != 0);
6825
6826         // `object->value()' is the number of executed statements
6827
6828         object->setStringProperty("A");
6829         QCOMPARE(object->value(), 5);
6830
6831         object->setStringProperty("S");
6832         QCOMPARE(object->value(), 3);
6833
6834         object->setStringProperty("D");
6835         QCOMPARE(object->value(), 3);
6836
6837         object->setStringProperty("F");
6838         QCOMPARE(object->value(), 3);
6839
6840         QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6841
6842         object->setStringProperty("something else");
6843     }
6844
6845     {
6846         QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml"));
6847         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6848         QVERIFY(object != 0);
6849
6850         // `object->value()' is the number of executed statements
6851
6852         object->setStringProperty("A");
6853         QCOMPARE(object->value(), 1);
6854
6855         object->setStringProperty("S");
6856         QCOMPARE(object->value(), 1);
6857
6858         object->setStringProperty("D");
6859         QCOMPARE(object->value(), 1);
6860
6861         object->setStringProperty("F");
6862         QCOMPARE(object->value(), 1);
6863
6864         object->setStringProperty("something else");
6865         QCOMPARE(object->value(), 1);
6866     }
6867
6868     {
6869         QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml"));
6870         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6871         QVERIFY(object != 0);
6872
6873         // `object->value()' is the number of executed statements
6874
6875         object->setStringProperty("A");
6876         QCOMPARE(object->value(), 123);
6877
6878         object->setStringProperty("S");
6879         QCOMPARE(object->value(), 123);
6880
6881         object->setStringProperty("D");
6882         QCOMPARE(object->value(), 321);
6883
6884         object->setStringProperty("F");
6885         QCOMPARE(object->value(), 321);
6886
6887         object->setStringProperty("something else");
6888         QCOMPARE(object->value(), 0);
6889     }
6890 }
6891
6892 void tst_qqmlecmascript::withStatement()
6893 {
6894     {
6895         QQmlComponent component(&engine, testFileUrl("withStatement.1.qml"));
6896         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6897         QVERIFY(object != 0);
6898
6899         QCOMPARE(object->value(), 123);
6900     }
6901 }
6902
6903 void tst_qqmlecmascript::tryStatement()
6904 {
6905     {
6906         QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml"));
6907         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6908         QVERIFY(object != 0);
6909
6910         QCOMPARE(object->value(), 123);
6911     }
6912
6913     {
6914         QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml"));
6915         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6916         QVERIFY(object != 0);
6917
6918         QCOMPARE(object->value(), 321);
6919     }
6920
6921     {
6922         QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml"));
6923         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6924         QVERIFY(object != 0);
6925
6926         QCOMPARE(object->value(), 1);
6927     }
6928
6929     {
6930         QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml"));
6931         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6932         QVERIFY(object != 0);
6933
6934         QCOMPARE(object->value(), 1);
6935     }
6936 }
6937
6938 class CppInvokableWithQObjectDerived : public QObject
6939 {
6940     Q_OBJECT
6941 public:
6942     CppInvokableWithQObjectDerived() {}
6943     ~CppInvokableWithQObjectDerived() {}
6944
6945     Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data)
6946     {
6947         MyQmlObject *obj = new MyQmlObject();
6948         obj->setStringProperty(data);
6949         return obj;
6950     }
6951
6952     Q_INVOKABLE QString getStringProperty(MyQmlObject *obj)
6953     {
6954         return obj->stringProperty();
6955     }
6956 };
6957
6958 void tst_qqmlecmascript::invokableWithQObjectDerived()
6959 {
6960     CppInvokableWithQObjectDerived invokable;
6961
6962     {
6963     QQmlEngine engine;
6964     engine.rootContext()->setContextProperty("invokable", &invokable);
6965
6966     QQmlComponent component(&engine, testFileUrl("qobjectDerivedArgument.qml"));
6967
6968     QObject *object = component.create();
6969
6970     QVERIFY(object != 0);
6971     QVERIFY(object->property("result").value<bool>() == true);
6972
6973     delete object;
6974     }
6975 }
6976
6977 void tst_qqmlecmascript::realTypePrecision()
6978 {
6979     // Properties and signal parameters of type real should have double precision.
6980     QQmlComponent component(&engine, testFileUrl("realTypePrecision.qml"));
6981     QScopedPointer<QObject> object(component.create());
6982     QVERIFY(object != 0);
6983     QCOMPARE(object->property("test").toDouble(), 1234567890.);
6984     QCOMPARE(object->property("test2").toDouble(), 1234567890.);
6985     QCOMPARE(object->property("test3").toDouble(), 1234567890.);
6986     QCOMPARE(object->property("test4").toDouble(), 1234567890.);
6987     QCOMPARE(object->property("test5").toDouble(), 1234567890.);
6988     QCOMPARE(object->property("test6").toDouble(), 1234567890.*2);
6989 }
6990
6991 void tst_qqmlecmascript::registeredFlagMethod()
6992 {
6993     QQmlEngine engine;
6994     QQmlComponent component(&engine, testFileUrl("registeredFlagMethod.qml"));
6995     MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6996     QVERIFY(object != 0);
6997
6998     QCOMPARE(object->buttons(), 0);
6999     emit object->basicSignal();
7000     QCOMPARE(object->buttons(), Qt::RightButton);
7001
7002     delete object;
7003 }
7004
7005 // QTBUG-23138
7006 void tst_qqmlecmascript::replaceBinding()
7007 {
7008     QQmlEngine engine;
7009     QQmlComponent c(&engine, testFileUrl("replaceBinding.qml"));
7010     QObject *obj = c.create();
7011     QVERIFY(obj != 0);
7012
7013     QVERIFY(obj->property("success").toBool());
7014     delete obj;
7015 }
7016
7017 void tst_qqmlecmascript::deleteRootObjectInCreation()
7018 {
7019     {
7020     QQmlEngine engine;
7021     QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.qml"));
7022     QObject *obj = c.create();
7023     QVERIFY(obj != 0);
7024     QVERIFY(obj->property("rootIndestructible").toBool());
7025     QVERIFY(!obj->property("childDestructible").toBool());
7026     QTest::qWait(1);
7027     QVERIFY(obj->property("childDestructible").toBool());
7028     delete obj;
7029     }
7030
7031     {
7032     QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.2.qml"));
7033     QObject *object = c.create();
7034     QVERIFY(object != 0);
7035     QVERIFY(object->property("testConditionsMet").toBool());
7036     delete object;
7037     }
7038 }
7039
7040 void tst_qqmlecmascript::onDestruction()
7041 {
7042     {
7043         // Delete object manually to invoke the associated handlers,
7044         // prior to engine destruction.
7045         QQmlEngine engine;
7046         QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
7047         QObject *obj = c.create();
7048         QVERIFY(obj != 0);
7049         delete obj;
7050     }
7051
7052     {
7053         // In this case, the teardown of the engine causes deletion
7054         // of contexts and child items.  This triggers the
7055         // onDestruction handler of a (previously .destroy()ed)
7056         // component instance.  This shouldn't crash.
7057         QQmlEngine engine;
7058         QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
7059         QObject *obj = c.create();
7060         QVERIFY(obj != 0);
7061     }
7062 }
7063
7064 struct EventProcessor : public QObject
7065 {
7066     Q_OBJECT
7067 public:
7068     Q_INVOKABLE void process()
7069     {
7070         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
7071         QCoreApplication::processEvents();
7072     }
7073 };
7074
7075 void tst_qqmlecmascript::bindingSuppression()
7076 {
7077     QQmlEngine engine;
7078     EventProcessor processor;
7079     engine.rootContext()->setContextProperty("pendingEvents", &processor);
7080
7081     transientErrorsMsgCount = 0;
7082     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
7083
7084     QQmlComponent c(&engine, testFileUrl("bindingSuppression.qml"));
7085     QObject *obj = c.create();
7086     QVERIFY(obj != 0);
7087     delete obj;
7088
7089     qInstallMsgHandler(old);
7090     QCOMPARE(transientErrorsMsgCount, 0);
7091 }
7092
7093 void tst_qqmlecmascript::signalEmitted()
7094 {
7095     {
7096         // calling destroy on the sibling.
7097         QQmlEngine engine;
7098         QQmlComponent c(&engine, testFileUrl("signalEmitted.2.qml"));
7099         QObject *obj = c.create();
7100         QVERIFY(obj != 0);
7101         QTRY_VERIFY(obj->property("success").toBool());
7102         delete obj;
7103     }
7104
7105     {
7106         // allowing gc to clean up the sibling.
7107         QQmlEngine engine;
7108         QQmlComponent c(&engine, testFileUrl("signalEmitted.3.qml"));
7109         QObject *obj = c.create();
7110         QVERIFY(obj != 0);
7111         gc(engine); // should collect c1.
7112         QTRY_VERIFY(obj->property("success").toBool());
7113         delete obj;
7114     }
7115
7116     {
7117         // allowing gc to clean up the sibling after manually destroying target.
7118         QQmlEngine engine;
7119         QQmlComponent c(&engine, testFileUrl("signalEmitted.4.qml"));
7120         QObject *obj = c.create();
7121         QVERIFY(obj != 0);
7122         gc(engine); // should collect c1.
7123         QMetaObject::invokeMethod(obj, "destroyC2");
7124         QTRY_VERIFY(obj->property("success").toBool()); // handles events (incl. delete later).
7125         delete obj;
7126     }
7127 }
7128
7129 // QTBUG-25647
7130 void tst_qqmlecmascript::threadSignal()
7131 {
7132     {
7133     QQmlComponent c(&engine, testFileUrl("threadSignal.qml"));
7134     QObject *object = c.create();
7135     QVERIFY(object != 0);
7136     QTRY_VERIFY(object->property("passed").toBool());
7137     delete object;
7138     }
7139     {
7140     QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml"));
7141     QObject *object = c.create();
7142     QVERIFY(object != 0);
7143     QSignalSpy doneSpy(object, SIGNAL(done(const QString &)));
7144     QMetaObject::invokeMethod(object, "doIt");
7145     QTRY_VERIFY(object->property("passed").toBool());
7146     QCOMPARE(doneSpy.count(), 1);
7147     delete object;
7148     }
7149 }
7150
7151 // ensure that the qqmldata::destroyed() handler doesn't cause problems
7152 void tst_qqmlecmascript::qqmldataDestroyed()
7153 {
7154     // gc cleans up a qobject, later the qqmldata destroyed handler will run.
7155     {
7156         QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.qml"));
7157         QObject *object = c.create();
7158         QVERIFY(object != 0);
7159         // now gc causing the collection of the dynamically constructed object.
7160         engine.collectGarbage();
7161         engine.collectGarbage();
7162         // now process events to allow deletion (calling qqmldata::destroyed())
7163         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
7164         QCoreApplication::processEvents();
7165         // shouldn't crash.
7166         delete object;
7167     }
7168
7169     // in this case, the object has CPP ownership, and the gc will
7170     // be triggered during its beginCreate stage.
7171     {
7172         QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.2.qml"));
7173         QObject *object = c.create();
7174         QVERIFY(object != 0);
7175         QVERIFY(object->property("testConditionsMet").toBool());
7176         // the gc() within the handler will have triggered the weak
7177         // qobject reference callback.  If that incorrectly disposes
7178         // the handle, when the qqmldata::destroyed() handler is
7179         // called due to object deletion we will see a crash.
7180         delete object;
7181         // shouldn't have crashed.
7182     }
7183 }
7184
7185 void tst_qqmlecmascript::secondAlias()
7186 {
7187     QQmlComponent c(&engine, testFileUrl("secondAlias.qml"));
7188     QObject *object = c.create();
7189     QVERIFY(object != 0);
7190     QCOMPARE(object->property("test").toInt(), 200);
7191     delete object;
7192 }
7193
7194 // An alias to a var property works
7195 void tst_qqmlecmascript::varAlias()
7196 {
7197     QQmlComponent c(&engine, testFileUrl("varAlias.qml"));
7198     QObject *object = c.create();
7199     QVERIFY(object != 0);
7200     QCOMPARE(object->property("test").toInt(), 192);
7201     delete object;
7202 }
7203
7204 // Used to trigger an assert in the lazy meta object creation stage
7205 void tst_qqmlecmascript::overrideDataAssert()
7206 {
7207     QQmlComponent c(&engine, testFileUrl("overrideDataAssert.qml"));
7208     QObject *object = c.create();
7209     QVERIFY(object != 0);
7210     object->metaObject();
7211     delete object;
7212 }
7213
7214 void tst_qqmlecmascript::fallbackBindings_data()
7215 {
7216     QTest::addColumn<QString>("source");
7217
7218     QTest::newRow("Property without fallback") << "fallbackBindings.1.qml";
7219     QTest::newRow("Property fallback") << "fallbackBindings.2.qml";
7220     QTest::newRow("SingletonType without fallback") << "fallbackBindings.3.qml";
7221     QTest::newRow("SingletonType fallback") << "fallbackBindings.4.qml";
7222     QTest::newRow("Attached without fallback") << "fallbackBindings.5.qml";
7223     QTest::newRow("Attached fallback") << "fallbackBindings.6.qml";
7224 }
7225
7226 void tst_qqmlecmascript::fallbackBindings()
7227 {
7228     QFETCH(QString, source);
7229
7230     QQmlComponent component(&engine, testFileUrl(source));
7231     QScopedPointer<QObject> object(component.create());
7232     QVERIFY(object != 0);
7233
7234     QCOMPARE(object->property("success").toBool(), true);
7235 }
7236
7237 void tst_qqmlecmascript::sequenceSort_data()
7238 {
7239     QTest::addColumn<QString>("function");
7240     QTest::addColumn<bool>("useComparer");
7241
7242     QTest::newRow("qtbug_25269") << "test_qtbug_25269" << false;
7243
7244     const char *types[] = { "alphabet", "numbers", "reals" };
7245     const char *sort[] = { "insertionSort", "quickSort" };
7246
7247     for (size_t t=0 ; t < sizeof(types)/sizeof(types[0]) ; ++t) {
7248         for (size_t s=0 ; s < sizeof(sort)/sizeof(sort[0]) ; ++s) {
7249             for (int c=0 ; c < 2 ; ++c) {
7250                 QString testName = QLatin1String(types[t]) + QLatin1String("_") + QLatin1String(sort[s]);
7251                 QString fnName = QLatin1String("test_") + testName;
7252                 bool useComparer = c != 0;
7253                 testName += useComparer ? QLatin1String("[custom]") : QLatin1String("[default]");
7254                 QTest::newRow(testName.toAscii().constData()) << fnName << useComparer;
7255             }
7256         }
7257     }
7258 }
7259
7260 void tst_qqmlecmascript::sequenceSort()
7261 {
7262     QFETCH(QString, function);
7263     QFETCH(bool, useComparer);
7264
7265     QQmlComponent component(&engine, testFileUrl("sequenceSort.qml"));
7266
7267     QObject *object = component.create();
7268     if (object == 0)
7269         qDebug() << component.errorString();
7270     QVERIFY(object != 0);
7271
7272     QVariant q;
7273     QMetaObject::invokeMethod(object, function.toAscii().constData(), Q_RETURN_ARG(QVariant, q), Q_ARG(QVariant, useComparer));
7274     QVERIFY(q.toBool() == true);
7275
7276     delete object;
7277 }
7278
7279 QTEST_MAIN(tst_qqmlecmascript)
7280
7281 #include "tst_qqmlecmascript.moc"