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