9f4a8576d7cb19dab21b9987ccd04a827cd8fde6
[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
3611 void tst_qqmlecmascript::moduleApi()
3612 {
3613     QFETCH(QUrl, testfile);
3614     QFETCH(QString, errorMessage);
3615     QFETCH(QStringList, warningMessages);
3616     QFETCH(QStringList, readProperties);
3617     QFETCH(QVariantList, readExpectedValues);
3618     QFETCH(QStringList, writeProperties);
3619     QFETCH(QVariantList, writeValues);
3620     QFETCH(QStringList, readBackProperties);
3621     QFETCH(QVariantList, readBackExpectedValues);
3622
3623     QQmlComponent component(&engine, testfile);
3624
3625     if (!errorMessage.isEmpty())
3626         QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3627
3628     if (warningMessages.size())
3629         foreach (const QString &warning, warningMessages)
3630             QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3631
3632     QObject *object = component.create();
3633     if (!errorMessage.isEmpty()) {
3634         QVERIFY(object == 0);
3635     } else {
3636         QVERIFY(object != 0);
3637         for (int i = 0; i < readProperties.size(); ++i)
3638             QCOMPARE(object->property(readProperties.at(i).toLatin1().constData()), readExpectedValues.at(i));
3639         for (int i = 0; i < writeProperties.size(); ++i)
3640             QVERIFY(object->setProperty(writeProperties.at(i).toLatin1().constData(), writeValues.at(i)));
3641         for (int i = 0; i < readBackProperties.size(); ++i)
3642             QCOMPARE(object->property(readBackProperties.at(i).toLatin1().constData()), readBackExpectedValues.at(i));
3643         delete object;
3644     }
3645 }
3646
3647 void tst_qqmlecmascript::importScripts_data()
3648 {
3649     QTest::addColumn<QUrl>("testfile");
3650     QTest::addColumn<QString>("errorMessage");
3651     QTest::addColumn<QStringList>("warningMessages");
3652     QTest::addColumn<QStringList>("propertyNames");
3653     QTest::addColumn<QVariantList>("propertyValues");
3654
3655     QTest::newRow("basic functionality")
3656             << testFileUrl("jsimport/testImport.qml")
3657             << QString()
3658             << QStringList()
3659             << (QStringList() << QLatin1String("importedScriptStringValue")
3660                               << QLatin1String("importedScriptFunctionValue")
3661                               << QLatin1String("importedModuleAttachedPropertyValue")
3662                               << QLatin1String("importedModuleEnumValue"))
3663             << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3664                                << QVariant(20)
3665                                << QVariant(19)
3666                                << QVariant(2));
3667
3668     QTest::newRow("import scoping")
3669             << testFileUrl("jsimport/testImportScoping.qml")
3670             << QString()
3671             << QStringList()
3672             << (QStringList() << QLatin1String("componentError"))
3673             << (QVariantList() << QVariant(5));
3674
3675     QTest::newRow("parent scope shouldn't be inherited by import with imports")
3676             << testFileUrl("jsimportfail/failOne.qml")
3677             << QString()
3678             << (QStringList() << QString(testFileUrl("jsimportfail/failOne.qml").toString() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
3679             << (QStringList() << QLatin1String("importScriptFunctionValue"))
3680             << (QVariantList() << QVariant(QString()));
3681
3682     QTest::newRow("javascript imports in an import should be private to the import scope")
3683             << testFileUrl("jsimportfail/failTwo.qml")
3684             << QString()
3685             << (QStringList() << QString(testFileUrl("jsimportfail/failTwo.qml").toString() + QLatin1String(":6: ReferenceError: ImportOneJs is not defined")))
3686             << (QStringList() << QLatin1String("importScriptFunctionValue"))
3687             << (QVariantList() << QVariant(QString()));
3688
3689     QTest::newRow("module imports in an import should be private to the import scope")
3690             << testFileUrl("jsimportfail/failThree.qml")
3691             << QString()
3692             << (QStringList() << QString(testFileUrl("jsimportfail/failThree.qml").toString() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
3693             << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
3694             << (QVariantList() << QVariant(false));
3695
3696     QTest::newRow("typenames in an import should be private to the import scope")
3697             << testFileUrl("jsimportfail/failFour.qml")
3698             << QString()
3699             << (QStringList() << QString(testFileUrl("jsimportfail/failFour.qml").toString() + QLatin1String(":6: ReferenceError: JsQtTest is not defined")))
3700             << (QStringList() << QLatin1String("importedModuleEnumValue"))
3701             << (QVariantList() << QVariant(0));
3702
3703     QTest::newRow("import with imports has it's own activation scope")
3704             << testFileUrl("jsimportfail/failFive.qml")
3705             << QString()
3706             << (QStringList() << QString(testFileUrl("jsimportfail/importWithImports.js").toString() + QLatin1String(":8: ReferenceError: Component is not defined")))
3707             << (QStringList() << QLatin1String("componentError"))
3708             << (QVariantList() << QVariant(0));
3709
3710     QTest::newRow("import pragma library script")
3711             << testFileUrl("jsimport/testImportPragmaLibrary.qml")
3712             << QString()
3713             << QStringList()
3714             << (QStringList() << QLatin1String("testValue"))
3715             << (QVariantList() << QVariant(31));
3716
3717     QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3718             << testFileUrl("jsimportfail/testImportPragmaLibrary.qml")
3719             << QString()
3720             << (QStringList() << QString(testFileUrl("jsimportfail/importPragmaLibrary.js").toString() + QLatin1String(":6: ReferenceError: Component is not defined")))
3721             << (QStringList() << QLatin1String("testValue"))
3722             << (QVariantList() << QVariant(0));
3723
3724     QTest::newRow("import pragma library script which has an import")
3725             << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
3726             << QString()
3727             << QStringList()
3728             << (QStringList() << QLatin1String("testValue"))
3729             << (QVariantList() << QVariant(55));
3730
3731     QTest::newRow("import pragma library script which has a pragma library import")
3732             << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3733             << QString()
3734             << QStringList()
3735             << (QStringList() << QLatin1String("testValue"))
3736             << (QVariantList() << QVariant(18));
3737
3738     QTest::newRow("import module api into js import")
3739             << testFileUrl("jsimport/testImportModuleApi.qml")
3740             << QString()
3741             << QStringList()
3742             << (QStringList() << QLatin1String("testValue"))
3743             << (QVariantList() << QVariant(20));
3744
3745     QTest::newRow("import module which exports a script")
3746             << testFileUrl("jsimport/testJsImport.qml")
3747             << QString()
3748             << QStringList()
3749             << (QStringList() << QLatin1String("importedScriptStringValue")
3750                               << QLatin1String("renamedScriptStringValue")
3751                               << QLatin1String("reimportedScriptStringValue"))
3752             << (QVariantList() << QVariant(QString("Hello"))
3753                                << QVariant(QString("Hello"))
3754                                << QVariant(QString("Hello")));
3755 }
3756
3757 void tst_qqmlecmascript::importScripts()
3758 {
3759     QFETCH(QUrl, testfile);
3760     QFETCH(QString, errorMessage);
3761     QFETCH(QStringList, warningMessages);
3762     QFETCH(QStringList, propertyNames);
3763     QFETCH(QVariantList, propertyValues);
3764
3765     QQmlComponent component(&engine, testfile);
3766
3767     if (!errorMessage.isEmpty())
3768         QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
3769
3770     if (warningMessages.size())
3771         foreach (const QString &warning, warningMessages)
3772             QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
3773
3774     QObject *object = component.create();
3775     if (!errorMessage.isEmpty()) {
3776         QVERIFY(object == 0);
3777     } else {
3778         QVERIFY(object != 0);
3779         for (int i = 0; i < propertyNames.size(); ++i)
3780             QCOMPARE(object->property(propertyNames.at(i).toLatin1().constData()), propertyValues.at(i));
3781         delete object;
3782     }
3783 }
3784
3785 void tst_qqmlecmascript::scarceResources_other()
3786 {
3787     /* These tests require knowledge of state, since we test values after
3788        performing signal or function invocation. */
3789
3790     QPixmap origPixmap(100, 100);
3791     origPixmap.fill(Qt::blue);
3792     QString srp_name, expectedWarning;
3793     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
3794     ScarceResourceObject *eo = 0;
3795     QObject *srsc = 0;
3796     QObject *object = 0;
3797
3798     /* property var semantics */
3799
3800     // test that scarce resources are handled properly in signal invocation
3801     QQmlComponent varComponentTen(&engine, testFileUrl("scarceResourceSignal.var.qml"));
3802     object = varComponentTen.create();
3803     srsc = object->findChild<QObject*>("srsc");
3804     QVERIFY(srsc);
3805     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3806     QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3807     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3808     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3809     QMetaObject::invokeMethod(srsc, "testSignal");
3810     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3811     QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3812     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3813     QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3814     QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3815     QVERIFY(srsc->property("scarceResourceCopy").isValid());
3816     QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3817     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3818     QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3819     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3820     delete object;
3821
3822     // test that scarce resources are handled properly from js functions in qml files
3823     QQmlComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml"));
3824     object = varComponentEleven.create();
3825     QVERIFY(object != 0);
3826     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3827     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3828     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3829     QMetaObject::invokeMethod(object, "retrieveScarceResource");
3830     QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3831     QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3832     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3833     QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3834     QMetaObject::invokeMethod(object, "releaseScarceResource");
3835     QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3836     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3837     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3838     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3839     delete object;
3840
3841     // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3842     QQmlComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
3843     object = varComponentTwelve.create();
3844     QVERIFY(object != 0);
3845     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3846     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3847     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3848     srp_name = object->property("srp_name").toString();
3849     expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3850     QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3851     QMetaObject::invokeMethod(object, "retrieveScarceResource");
3852     QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3853     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3854     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3855     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3856     delete object;
3857
3858     // test that if an Item which has JS ownership but has a scarce resource property is garbage collected,
3859     // that the scarce resource is removed from the engine's list of scarce resources to clean up.
3860     QQmlComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml"));
3861     object = varComponentThirteen.create();
3862     QVERIFY(object != 0);
3863     QVERIFY(!object->property("varProperty").isValid()); // not assigned yet
3864     QMetaObject::invokeMethod(object, "assignVarProperty");
3865     QVERIFY(ep->scarceResources.isEmpty());             // the scarce resource is a VME property.
3866     QMetaObject::invokeMethod(object, "deassignVarProperty");
3867     QVERIFY(ep->scarceResources.isEmpty());             // should still be empty; the resource should have been released on gc.
3868     delete object;
3869
3870     /* property variant semantics */
3871
3872     // test that scarce resources are handled properly in signal invocation
3873     QQmlComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml"));
3874     object = variantComponentTen.create();
3875     QVERIFY(object != 0);
3876     srsc = object->findChild<QObject*>("srsc");
3877     QVERIFY(srsc);
3878     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3879     QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3880     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3881     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3882     QMetaObject::invokeMethod(srsc, "testSignal");
3883     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3884     QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3885     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3886     QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3887     QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3888     QVERIFY(srsc->property("scarceResourceCopy").isValid());
3889     QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3890     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3891     QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3892     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3893     delete object;
3894
3895     // test that scarce resources are handled properly from js functions in qml files
3896     QQmlComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml"));
3897     object = variantComponentEleven.create();
3898     QVERIFY(object != 0);
3899     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3900     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3901     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3902     QMetaObject::invokeMethod(object, "retrieveScarceResource");
3903     QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3904     QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3905     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3906     QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3907     QMetaObject::invokeMethod(object, "releaseScarceResource");
3908     QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3909     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3910     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3911     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3912     delete object;
3913
3914     // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3915     QQmlComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml"));
3916     object = variantComponentTwelve.create();
3917     QVERIFY(object != 0);
3918     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3919     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3920     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3921     srp_name = object->property("srp_name").toString();
3922     expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3923     QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3924     QMetaObject::invokeMethod(object, "retrieveScarceResource");
3925     QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3926     eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
3927     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3928     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3929     delete object;
3930 }
3931
3932 void tst_qqmlecmascript::scarceResources_data()
3933 {
3934     QTest::addColumn<QUrl>("qmlFile");
3935     QTest::addColumn<bool>("readDetachStatus");
3936     QTest::addColumn<bool>("expectedDetachStatus");
3937     QTest::addColumn<QStringList>("propertyNames");
3938     QTest::addColumn<QVariantList>("expectedValidity");
3939     QTest::addColumn<QVariantList>("expectedValues");
3940     QTest::addColumn<QStringList>("expectedErrors");
3941
3942     QPixmap origPixmap(100, 100);
3943     origPixmap.fill(Qt::blue);
3944
3945     /* property var semantics */
3946
3947     // in the following three cases, the instance created from the component
3948     // has a property which is a copy of the scarce resource; hence, the
3949     // resource should NOT be detached prior to deletion of the object instance,
3950     // unless the resource is destroyed explicitly.
3951     QTest::newRow("var: import scarce resource copy directly")
3952         << testFileUrl("scarceResourceCopy.var.qml")
3953         << true
3954         << false // won't be detached, because assigned to property and not explicitly released
3955         << (QStringList() << QLatin1String("scarceResourceCopy"))
3956         << (QList<QVariant>() << true)
3957         << (QList<QVariant>() << origPixmap)
3958         << QStringList();
3959
3960     QTest::newRow("var: import scarce resource copy from JS")
3961         << testFileUrl("scarceResourceCopyFromJs.var.qml")
3962         << true
3963         << false // won't be detached, because assigned to property and not explicitly released
3964         << (QStringList() << QLatin1String("scarceResourceCopy"))
3965         << (QList<QVariant>() << true)
3966         << (QList<QVariant>() << origPixmap)
3967         << QStringList();
3968
3969     QTest::newRow("var: import released scarce resource copy from JS")
3970         << testFileUrl("scarceResourceDestroyedCopy.var.qml")
3971         << true
3972         << true // explicitly released, so it will be detached
3973         << (QStringList() << QLatin1String("scarceResourceCopy"))
3974         << (QList<QVariant>() << false)
3975         << (QList<QVariant>() << QVariant())
3976         << QStringList();
3977
3978     // in the following three cases, no other copy should exist in memory,
3979     // and so it should be detached (unless explicitly preserved).
3980     QTest::newRow("var: import auto-release SR from JS in binding side-effect")
3981         << testFileUrl("scarceResourceTest.var.qml")
3982         << true
3983         << true // auto released, so it will be detached
3984         << (QStringList() << QLatin1String("scarceResourceTest"))
3985         << (QList<QVariant>() << true)
3986         << (QList<QVariant>() << QVariant(100))
3987         << QStringList();
3988     QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
3989         << testFileUrl("scarceResourceTestPreserve.var.qml")
3990         << true
3991         << false // won't be detached because we explicitly preserve it
3992         << (QStringList() << QLatin1String("scarceResourceTest"))
3993         << (QList<QVariant>() << true)
3994         << (QList<QVariant>() << QVariant(100))
3995         << QStringList();
3996     QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
3997         << testFileUrl("scarceResourceTestMultiple.var.qml")
3998         << true
3999         << true // will be detached because all resources were released manually or automatically.
4000         << (QStringList() << QLatin1String("scarceResourceTest"))
4001         << (QList<QVariant>() << true)
4002         << (QList<QVariant>() << QVariant(100))
4003         << QStringList();
4004
4005     // In the following three cases, test that scarce resources are handled
4006     // correctly for imports.
4007     QTest::newRow("var: import with no binding")
4008         << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4009         << false // cannot check detach status.
4010         << false
4011         << QStringList()
4012         << QList<QVariant>()
4013         << QList<QVariant>()
4014         << QStringList();
4015     QTest::newRow("var: import with binding without explicit preserve")
4016         << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
4017         << false
4018         << false
4019         << (QStringList() << QLatin1String("scarceResourceCopy"))
4020         << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4021         << (QList<QVariant>() << QVariant())
4022         << QStringList();
4023     QTest::newRow("var: import with explicit release after binding evaluation")
4024         << testFileUrl("scarceResourceCopyImport.var.qml")
4025         << false
4026         << false
4027         << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4028         << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated.
4029         << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true))
4030         << QStringList();
4031     QTest::newRow("var: import with different js objects")
4032         << testFileUrl("scarceResourceCopyImportDifferent.var.qml")
4033         << false
4034         << false
4035         << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4036         << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4037         << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false))
4038         << QStringList();
4039     QTest::newRow("var: import with different js objects and explicit release")
4040         << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml")
4041         << false
4042         << false
4043         << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4044         << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4045         << (QList<QVariant>() << QVariant(origPixmap) << QVariant())
4046         << QStringList();
4047     QTest::newRow("var: import with same js objects and explicit release")
4048         << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml")
4049         << false
4050         << false
4051         << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4052         << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4053         << (QList<QVariant>() << QVariant() << QVariant())
4054         << QStringList();
4055     QTest::newRow("var: binding with same js objects and explicit release")
4056         << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml")
4057         << false
4058         << false
4059         << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4060         << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4061         << (QList<QVariant>() << QVariant() << QVariant())
4062         << QStringList();
4063
4064
4065     /* property variant semantics */
4066
4067     // in the following three cases, the instance created from the component
4068     // has a property which is a copy of the scarce resource; hence, the
4069     // resource should NOT be detached prior to deletion of the object instance,
4070     // unless the resource is destroyed explicitly.
4071     QTest::newRow("variant: import scarce resource copy directly")
4072         << testFileUrl("scarceResourceCopy.variant.qml")
4073         << true
4074         << false // won't be detached, because assigned to property and not explicitly released
4075         << (QStringList() << QLatin1String("scarceResourceCopy"))
4076         << (QList<QVariant>() << true)
4077         << (QList<QVariant>() << origPixmap)
4078         << QStringList();
4079
4080     QTest::newRow("variant: import scarce resource copy from JS")
4081         << testFileUrl("scarceResourceCopyFromJs.variant.qml")
4082         << true
4083         << false // won't be detached, because assigned to property and not explicitly released
4084         << (QStringList() << QLatin1String("scarceResourceCopy"))
4085         << (QList<QVariant>() << true)
4086         << (QList<QVariant>() << origPixmap)
4087         << QStringList();
4088
4089     QTest::newRow("variant: import released scarce resource copy from JS")
4090         << testFileUrl("scarceResourceDestroyedCopy.variant.qml")
4091         << true
4092         << true // explicitly released, so it will be detached
4093         << (QStringList() << QLatin1String("scarceResourceCopy"))
4094         << (QList<QVariant>() << false)
4095         << (QList<QVariant>() << QVariant())
4096         << QStringList();
4097
4098     // in the following three cases, no other copy should exist in memory,
4099     // and so it should be detached (unless explicitly preserved).
4100     QTest::newRow("variant: import auto-release SR from JS in binding side-effect")
4101         << testFileUrl("scarceResourceTest.variant.qml")
4102         << true
4103         << true // auto released, so it will be detached
4104         << (QStringList() << QLatin1String("scarceResourceTest"))
4105         << (QList<QVariant>() << true)
4106         << (QList<QVariant>() << QVariant(100))
4107         << QStringList();
4108     QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect")
4109         << testFileUrl("scarceResourceTestPreserve.variant.qml")
4110         << true
4111         << false // won't be detached because we explicitly preserve it
4112         << (QStringList() << QLatin1String("scarceResourceTest"))
4113         << (QList<QVariant>() << true)
4114         << (QList<QVariant>() << QVariant(100))
4115         << QStringList();
4116     QTest::newRow("variant: import multiple scarce resources")
4117         << testFileUrl("scarceResourceTestMultiple.variant.qml")
4118         << true
4119         << true // will be detached because all resources were released manually or automatically.
4120         << (QStringList() << QLatin1String("scarceResourceTest"))
4121         << (QList<QVariant>() << true)
4122         << (QList<QVariant>() << QVariant(100))
4123         << QStringList();
4124
4125     // In the following three cases, test that scarce resources are handled
4126     // correctly for imports.
4127     QTest::newRow("variant: import with no binding")
4128         << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4129         << false // cannot check detach status.
4130         << false
4131         << QStringList()
4132         << QList<QVariant>()
4133         << QList<QVariant>()
4134         << QStringList();
4135     QTest::newRow("variant: import with binding without explicit preserve")
4136         << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
4137         << false
4138         << false
4139         << (QStringList() << QLatin1String("scarceResourceCopy"))
4140         << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4141         << (QList<QVariant>() << QVariant())
4142         << QStringList();
4143     QTest::newRow("variant: import with explicit release after binding evaluation")
4144         << testFileUrl("scarceResourceCopyImport.variant.qml")
4145         << false
4146         << false
4147         << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo"))
4148         << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies.
4149         << (QList<QVariant>() << origPixmap << origPixmap << QVariant())
4150         << QStringList();
4151 }
4152
4153 void tst_qqmlecmascript::scarceResources()
4154 {
4155     QFETCH(QUrl, qmlFile);
4156     QFETCH(bool, readDetachStatus);
4157     QFETCH(bool, expectedDetachStatus);
4158     QFETCH(QStringList, propertyNames);
4159     QFETCH(QVariantList, expectedValidity);
4160     QFETCH(QVariantList, expectedValues);
4161     QFETCH(QStringList, expectedErrors);
4162
4163     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine);
4164     ScarceResourceObject *eo = 0;
4165     QObject *object = 0;
4166
4167     QQmlComponent c(&engine, qmlFile);
4168     object = c.create();
4169     QVERIFY(object != 0);
4170     for (int i = 0; i < propertyNames.size(); ++i) {
4171         QString prop = propertyNames.at(i);
4172         bool validity = expectedValidity.at(i).toBool();
4173         QVariant value = expectedValues.at(i);
4174
4175         QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity);
4176         if (value.type() == QVariant::Int) {
4177             QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt());
4178         } else if (value.type() == QVariant::Pixmap) {
4179             QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>());
4180         }
4181     }
4182
4183     if (readDetachStatus) {
4184         eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
4185         QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
4186     }
4187
4188     QVERIFY(ep->scarceResources.isEmpty());
4189     delete object;
4190 }
4191
4192 void tst_qqmlecmascript::propertyChangeSlots()
4193 {
4194     // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
4195     QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml"));
4196     QObject *object = component.create();
4197     QVERIFY(object != 0);
4198     delete object;
4199
4200     // ensure that invalid property names fail properly.
4201     QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4202     QQmlComponent e1(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.1.qml"));
4203     QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
4204     QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
4205     object = e1.create();
4206     QVERIFY(object == 0);
4207     delete object;
4208
4209     QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4210     QQmlComponent e2(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.2.qml"));
4211     expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
4212     QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
4213     object = e2.create();
4214     QVERIFY(object == 0);
4215     delete object;
4216
4217     QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4218     QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml"));
4219     expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
4220     QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
4221     object = e3.create();
4222     QVERIFY(object == 0);
4223     delete object;
4224
4225     QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
4226     QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml"));
4227     expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
4228     QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
4229     object = e4.create();
4230     QVERIFY(object == 0);
4231     delete object;
4232 }
4233
4234 void tst_qqmlecmascript::propertyVar_data()
4235 {
4236     QTest::addColumn<QUrl>("qmlFile");
4237
4238     // valid
4239     QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyVar.1.qml");
4240     QTest::newRow("non-bindable object changed") << testFileUrl("propertyVar.2.qml");
4241     QTest::newRow("primitive changed") << testFileUrl("propertyVar.3.qml");
4242     QTest::newRow("javascript array modification") << testFileUrl("propertyVar.4.qml");
4243     QTest::newRow("javascript map modification") << testFileUrl("propertyVar.5.qml");
4244     QTest::newRow("javascript array assignment") << testFileUrl("propertyVar.6.qml");
4245     QTest::newRow("javascript map assignment") << testFileUrl("propertyVar.7.qml");
4246     QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml");
4247     QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml");
4248     QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml");
4249     QTest::newRow("javascript function assignment") << testFileUrl("propertyVar.11.qml");
4250     QTest::newRow("javascript special assignment") << testFileUrl("propertyVar.12.qml");
4251     QTest::newRow("declarative binding assignment") << testFileUrl("propertyVar.13.qml");
4252     QTest::newRow("imperative binding assignment") << testFileUrl("propertyVar.14.qml");
4253     QTest::newRow("stored binding assignment") << testFileUrl("propertyVar.15.qml");
4254     QTest::newRow("function expression binding assignment") << testFileUrl("propertyVar.16.qml");
4255 }
4256
4257 void tst_qqmlecmascript::propertyVar()
4258 {
4259     QFETCH(QUrl, qmlFile);
4260
4261     QQmlComponent component(&engine, qmlFile);
4262     QObject *object = component.create();
4263     QVERIFY(object != 0);
4264
4265     QCOMPARE(object->property("test").toBool(), true);
4266
4267     delete object;
4268 }
4269
4270 void tst_qqmlecmascript::propertyQJSValue_data()
4271 {
4272     QTest::addColumn<QUrl>("qmlFile");
4273
4274     // valid
4275     QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyQJSValue.1.qml");
4276     QTest::newRow("non-bindable object changed") << testFileUrl("propertyQJSValue.2.qml");
4277     QTest::newRow("primitive changed") << testFileUrl("propertyQJSValue.3.qml");
4278     QTest::newRow("javascript array modification") << testFileUrl("propertyQJSValue.4.qml");
4279     QTest::newRow("javascript map modification") << testFileUrl("propertyQJSValue.5.qml");
4280     QTest::newRow("javascript array assignment") << testFileUrl("propertyQJSValue.6.qml");
4281     QTest::newRow("javascript map assignment") << testFileUrl("propertyQJSValue.7.qml");
4282     QTest::newRow("literal property assignment") << testFileUrl("propertyQJSValue.8.qml");
4283     QTest::newRow("qobject property assignment") << testFileUrl("propertyQJSValue.9.qml");
4284     QTest::newRow("base class var property assignment") << testFileUrl("propertyQJSValue.10.qml");
4285     QTest::newRow("javascript function assignment") << testFileUrl("propertyQJSValue.11.qml");
4286     QTest::newRow("javascript special assignment") << testFileUrl("propertyQJSValue.12.qml");
4287     QTest::newRow("declarative binding assignment") << testFileUrl("propertyQJSValue.13.qml");
4288     QTest::newRow("imperative binding assignment") << testFileUrl("propertyQJSValue.14.qml");
4289     QTest::newRow("stored binding assignment") << testFileUrl("propertyQJSValue.15.qml");
4290     QTest::newRow("javascript function binding") << testFileUrl("propertyQJSValue.16.qml");
4291
4292     QTest::newRow("reset property") << testFileUrl("propertyQJSValue.reset.qml");
4293     QTest::newRow("reset property in binding") << testFileUrl("propertyQJSValue.bindingreset.qml");
4294 }
4295
4296 void tst_qqmlecmascript::propertyQJSValue()
4297 {
4298     QFETCH(QUrl, qmlFile);
4299
4300     QQmlComponent component(&engine, qmlFile);
4301     QObject *object = component.create();
4302     QVERIFY(object != 0);
4303
4304     QCOMPARE(object->property("test").toBool(), true);
4305
4306     delete object;
4307 }
4308
4309 // Tests that we can write QVariant values to var properties from C++
4310 void tst_qqmlecmascript::propertyVarCpp()
4311 {
4312     QObject *object = 0;
4313
4314     // ensure that writing to and reading from a var property from cpp works as required.
4315     // Literal values stored in var properties can be read and written as QVariants
4316     // of a specific type, whereas object values are read as QVariantMaps.
4317     QQmlComponent component(&engine, testFileUrl("propertyVarCpp.qml"));
4318     object = component.create();
4319     QVERIFY(object != 0);
4320     // assign int to property var that currently has int assigned
4321     QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
4322     QCOMPARE(object->property("varBound"), QVariant(15));
4323     QCOMPARE(object->property("intBound"), QVariant(15));
4324     QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
4325     QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
4326     // assign string to property var that current has bool assigned
4327     QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
4328     QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
4329     QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
4330     QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
4331     // now enforce behaviour when accessing JavaScript objects from cpp.
4332     QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
4333     delete object;
4334 }
4335
4336 static void gc(QQmlEngine &engine)
4337 {
4338     engine.collectGarbage();
4339     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4340     QCoreApplication::processEvents();
4341 }
4342
4343 void tst_qqmlecmascript::propertyVarOwnership()
4344 {
4345     // Referenced JS objects are not collected
4346     {
4347     QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.qml"));
4348     QObject *object = component.create();
4349     QVERIFY(object != 0);
4350     QCOMPARE(object->property("test").toBool(), false);
4351     QMetaObject::invokeMethod(object, "runTest");
4352     QCOMPARE(object->property("test").toBool(), true);
4353     delete object;
4354     }
4355     // Referenced JS objects are not collected
4356     {
4357     QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml"));
4358     QObject *object = component.create();
4359     QVERIFY(object != 0);
4360     QCOMPARE(object->property("test").toBool(), false);
4361     QMetaObject::invokeMethod(object, "runTest");
4362     QCOMPARE(object->property("test").toBool(), true);
4363     delete object;
4364     }
4365     // Qt objects are not collected until they've been dereferenced
4366     {
4367     QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml"));
4368     QObject *object = component.create();
4369     QVERIFY(object != 0);
4370
4371     QCOMPARE(object->property("test2").toBool(), false);
4372     QCOMPARE(object->property("test2").toBool(), false);
4373
4374     QMetaObject::invokeMethod(object, "runTest");
4375     QCOMPARE(object->property("test1").toBool(), true);
4376
4377     QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4378     QVERIFY(!referencedObject.isNull());
4379     gc(engine);
4380     QVERIFY(!referencedObject.isNull());
4381
4382     QMetaObject::invokeMethod(object, "runTest2");
4383     QCOMPARE(object->property("test2").toBool(), true);
4384     gc(engine);
4385     QVERIFY(referencedObject.isNull());
4386
4387     delete object;
4388     }
4389     // Self reference does not prevent Qt object collection
4390     {
4391     QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml"));
4392     QObject *object = component.create();
4393     QVERIFY(object != 0);
4394
4395     QCOMPARE(object->property("test").toBool(), true);
4396
4397     QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
4398     QVERIFY(!referencedObject.isNull());
4399     gc(engine);
4400     QVERIFY(!referencedObject.isNull());
4401
4402     QMetaObject::invokeMethod(object, "runTest");
4403     gc(engine);
4404     QVERIFY(referencedObject.isNull());
4405
4406     delete object;
4407     }
4408     // Garbage collection cannot result in attempted dereference of empty handle
4409     {
4410     QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.5.qml"));
4411     QObject *object = component.create();
4412     QVERIFY(object != 0);
4413     QMetaObject::invokeMethod(object, "runTest");
4414     QCOMPARE(object->property("test").toBool(), true);
4415     delete object;
4416     }
4417 }
4418
4419 void tst_qqmlecmascript::propertyVarImplicitOwnership()
4420 {
4421     // The childObject has a reference to a different QObject.  We want to ensure
4422     // that the different item will not be cleaned up until required.  IE, the childObject
4423     // has implicit ownership of the constructed QObject.
4424     QQmlComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml"));
4425     QObject *object = component.create();
4426     QVERIFY(object != 0);
4427     QMetaObject::invokeMethod(object, "assignCircular");
4428     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4429     QCoreApplication::processEvents();
4430     QObject *rootObject = object->property("vp").value<QObject*>();
4431     QVERIFY(rootObject != 0);
4432     QObject *childObject = rootObject->findChild<QObject*>("text");
4433     QVERIFY(childObject != 0);
4434     QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4435     QCOMPARE(childObject->property("textCanary").toInt(), 10);
4436     QMetaObject::invokeMethod(childObject, "constructQObject");    // creates a reference to a constructed QObject.
4437     QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
4438     QVERIFY(!qobjectGuard.isNull());
4439     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4440     QCoreApplication::processEvents();
4441     QVERIFY(!qobjectGuard.isNull());
4442     QMetaObject::invokeMethod(object, "deassignCircular");
4443     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4444     QCoreApplication::processEvents();
4445     QVERIFY(qobjectGuard.isNull());                                // should have been collected now.
4446     delete object;
4447 }
4448
4449 void tst_qqmlecmascript::propertyVarReparent()
4450 {
4451     // ensure that nothing breaks if we re-parent objects
4452     QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4453     QObject *object = component.create();
4454     QVERIFY(object != 0);
4455     QMetaObject::invokeMethod(object, "assignVarProp");
4456     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4457     QCoreApplication::processEvents();
4458     QObject *rect = object->property("vp").value<QObject*>();
4459     QObject *text = rect->findChild<QObject*>("textOne");
4460     QObject *text2 = rect->findChild<QObject*>("textTwo");
4461     QWeakPointer<QObject> rectGuard(rect);
4462     QWeakPointer<QObject> textGuard(text);
4463     QWeakPointer<QObject> text2Guard(text2);
4464     QVERIFY(!rectGuard.isNull());
4465     QVERIFY(!textGuard.isNull());
4466     QVERIFY(!text2Guard.isNull());
4467     QCOMPARE(text->property("textCanary").toInt(), 11);
4468     QCOMPARE(text2->property("textCanary").toInt(), 12);
4469     // now construct an image which we will reparent.
4470     QMetaObject::invokeMethod(text2, "constructQObject");
4471     QObject *image = text2->property("vp").value<QObject*>();
4472     QWeakPointer<QObject> imageGuard(image);
4473     QVERIFY(!imageGuard.isNull());
4474     QCOMPARE(image->property("imageCanary").toInt(), 13);
4475     // now reparent the "Image" object (currently, it has JS ownership)
4476     image->setParent(text);                                        // shouldn't be collected after deassignVp now, since has a parent.
4477     QMetaObject::invokeMethod(text2, "deassignVp");
4478     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4479     QCoreApplication::processEvents();
4480     QCOMPARE(text->property("textCanary").toInt(), 11);
4481     QCOMPARE(text2->property("textCanary").toInt(), 22);
4482     QVERIFY(!imageGuard.isNull());                                 // should still be alive.
4483     QCOMPARE(image->property("imageCanary").toInt(), 13);          // still able to access var properties
4484     QMetaObject::invokeMethod(object, "deassignVarProp");          // now deassign the root-object's vp, causing gc of rect+text+text2
4485     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4486     QCoreApplication::processEvents();
4487     QVERIFY(imageGuard.isNull());                                  // should now have been deleted, due to parent being deleted.
4488     delete object;
4489 }
4490
4491 void tst_qqmlecmascript::propertyVarReparentNullContext()
4492 {
4493     // sometimes reparenting can cause problems
4494     // (eg, if the ctxt is collected, varproperties are no longer available)
4495     // this test ensures that no crash occurs in that situation.
4496     QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
4497     QObject *object = component.create();
4498     QVERIFY(object != 0);
4499     QMetaObject::invokeMethod(object, "assignVarProp");
4500     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4501     QCoreApplication::processEvents();
4502     QObject *rect = object->property("vp").value<QObject*>();
4503     QObject *text = rect->findChild<QObject*>("textOne");
4504     QObject *text2 = rect->findChild<QObject*>("textTwo");
4505     QWeakPointer<QObject> rectGuard(rect);
4506     QWeakPointer<QObject> textGuard(text);
4507     QWeakPointer<QObject> text2Guard(text2);
4508     QVERIFY(!rectGuard.isNull());
4509     QVERIFY(!textGuard.isNull());
4510     QVERIFY(!text2Guard.isNull());
4511     QCOMPARE(text->property("textCanary").toInt(), 11);
4512     QCOMPARE(text2->property("textCanary").toInt(), 12);
4513     // now construct an image which we will reparent.
4514     QMetaObject::invokeMethod(text2, "constructQObject");
4515     QObject *image = text2->property("vp").value<QObject*>();
4516     QWeakPointer<QObject> imageGuard(image);
4517     QVERIFY(!imageGuard.isNull());
4518     QCOMPARE(image->property("imageCanary").toInt(), 13);
4519     // now reparent the "Image" object (currently, it has JS ownership)
4520     image->setParent(object);                                      // reparented to base object.  after deassignVarProp, the ctxt will be invalid.
4521     QMetaObject::invokeMethod(object, "deassignVarProp");          // now deassign the root-object's vp, causing gc of rect+text+text2
4522     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4523     QCoreApplication::processEvents();
4524     QVERIFY(!imageGuard.isNull());                                 // should still be alive.
4525     QVERIFY(!image->property("imageCanary").isValid());            // but varProperties won't be available (null context).
4526     delete object;
4527     QVERIFY(imageGuard.isNull());                                  // should now be dead.
4528 }
4529
4530 void tst_qqmlecmascript::propertyVarCircular()
4531 {
4532     // enforce behaviour regarding circular references - ensure qdvmemo deletion.
4533     QQmlComponent component(&engine, testFileUrl("propertyVar.circular.qml"));
4534     QObject *object = component.create();
4535     QVERIFY(object != 0);
4536     QMetaObject::invokeMethod(object, "assignCircular");           // cause assignment and gc
4537     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4538     QCoreApplication::processEvents();
4539     QCOMPARE(object->property("canaryInt"), QVariant(5));
4540     QVariant canaryResourceVariant = object->property("canaryResource");
4541     QVERIFY(canaryResourceVariant.isValid());
4542     QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
4543     canaryResourceVariant = QVariant();                            // invalidate it to remove one copy of the pixmap from memory.
4544     QMetaObject::invokeMethod(object, "deassignCanaryResource");   // remove one copy of the pixmap from memory
4545     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4546     QCoreApplication::processEvents();
4547     QVERIFY(!canaryResourcePixmap.isDetached());                   // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
4548     QMetaObject::invokeMethod(object, "deassignCircular");         // cause deassignment and gc
4549     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4550     QCoreApplication::processEvents();
4551     QCOMPARE(object->property("canaryInt"), QVariant(2));
4552     QCOMPARE(object->property("canaryResource"), QVariant(1));
4553     QVERIFY(canaryResourcePixmap.isDetached());                    // now detached, since orig copy was member of qdvmemo which was deleted.
4554     delete object;
4555 }
4556
4557 void tst_qqmlecmascript::propertyVarCircular2()
4558 {
4559     // track deletion of JS-owned parent item with Cpp-owned child
4560     // where the child has a var property referencing its parent.
4561     QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4562     QObject *object = component.create();
4563     QVERIFY(object != 0);
4564     QMetaObject::invokeMethod(object, "assignCircular");
4565     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4566     QCoreApplication::processEvents();
4567     QObject *rootObject = object->property("vp").value<QObject*>();
4568     QVERIFY(rootObject != 0);
4569     QObject *childObject = rootObject->findChild<QObject*>("text");
4570     QVERIFY(childObject != 0);
4571     QWeakPointer<QObject> rootObjectTracker(rootObject);
4572     QVERIFY(!rootObjectTracker.isNull());
4573     QWeakPointer<QObject> childObjectTracker(childObject);
4574     QVERIFY(!childObjectTracker.isNull());
4575     gc(engine);
4576     QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4577     QCOMPARE(childObject->property("textCanary").toInt(), 10);
4578     QMetaObject::invokeMethod(object, "deassignCircular");
4579     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4580     QCoreApplication::processEvents();
4581     QVERIFY(rootObjectTracker.isNull());                           // should have been collected
4582     QVERIFY(childObjectTracker.isNull());                          // should have been collected
4583     delete object;
4584 }
4585
4586 void tst_qqmlecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
4587 {
4588     *(int*)(parameter) += 1;
4589     qPersistentDispose(object);
4590 }
4591
4592 void tst_qqmlecmascript::propertyVarInheritance()
4593 {
4594     int propertyVarWeakRefCallbackCount = 0;
4595
4596     // enforce behaviour regarding element inheritance - ensure handle disposal.
4597     // The particular component under test here has a chain of references.
4598     QQmlComponent component(&engine, testFileUrl("propertyVar.inherit.qml"));
4599     QObject *object = component.create();
4600     QVERIFY(object != 0);
4601     QMetaObject::invokeMethod(object, "assignCircular");           // cause assignment and gc
4602     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4603     QCoreApplication::processEvents();
4604     // we want to be able to track when the varProperties array of the last metaobject is disposed
4605     QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
4606     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*>();
4607     QQmlVMEMetaObject *icovmemo = QQmlVMEMetaObject::get(ico5);
4608     QQmlVMEMetaObject *ccovmemo = QQmlVMEMetaObject::get(cco5);
4609     v8::Persistent<v8::Value> icoCanaryHandle;
4610     v8::Persistent<v8::Value> ccoCanaryHandle;
4611     {
4612         v8::HandleScope hs;
4613         // XXX NOTE: this is very implementation dependent.  QDVMEMO->vmeProperty() is the only
4614         // public function which can return us a handle to something in the varProperties array.
4615         icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ")));
4616         ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")));
4617         // we make them weak and invoke the gc, but we should not hit the weak-callback yet
4618         // as the varproperties array of each vmemo still references the resource.
4619         icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4620         ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4621         gc(engine);
4622         QVERIFY(propertyVarWeakRefCallbackCount == 0);
4623     }
4624     // now we deassign the var prop, which should trigger collection of item subtrees.
4625     QMetaObject::invokeMethod(object, "deassignCircular");         // cause deassignment and gc
4626     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4627     QCoreApplication::processEvents();
4628     // ensure that there are only weak handles to the underlying varProperties array remaining.
4629     gc(engine);
4630     QCOMPARE(propertyVarWeakRefCallbackCount, 2);                  // should have been called for both, since all refs should be weak.
4631     delete object;
4632     // since there are no parent vmemo's to keep implicit references alive, and the only handles
4633     // to what remains are weak, all varProperties arrays must have been collected.
4634 }
4635
4636 void tst_qqmlecmascript::propertyVarInheritance2()
4637 {
4638     int propertyVarWeakRefCallbackCount = 0;
4639
4640     // The particular component under test here does NOT have a chain of references; the
4641     // only link between rootObject and childObject is that rootObject is the parent of childObject.
4642     QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4643     QObject *object = component.create();
4644     QVERIFY(object != 0);
4645     QMetaObject::invokeMethod(object, "assignCircular");
4646     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4647     QCoreApplication::processEvents();
4648     QObject *rootObject = object->property("vp").value<QObject*>();
4649     QVERIFY(rootObject != 0);
4650     QObject *childObject = rootObject->findChild<QObject*>("text");
4651     QVERIFY(childObject != 0);
4652     QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4653     QCOMPARE(childObject->property("textCanary").toInt(), 10);
4654     v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
4655     {
4656         v8::HandleScope hs;
4657         propertyVarWeakRefCallbackCount = 0;                           // reset callback count.
4658         childObjectVarArrayValueHandle = qPersistentNew(QQmlVMEMetaObject::get(childObject)->vmeProperty(childObject->metaObject()->indexOfProperty("vp")));
4659         childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4660         gc(engine);
4661         QVERIFY(propertyVarWeakRefCallbackCount == 0);                 // should not have been collected yet.
4662         QCOMPARE(childObject->property("vp").value<QObject*>(), rootObject);
4663         QCOMPARE(childObject->property("textCanary").toInt(), 10);
4664     }
4665     QMetaObject::invokeMethod(object, "deassignCircular");
4666     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4667     QCoreApplication::processEvents();
4668     QVERIFY(propertyVarWeakRefCallbackCount == 1);                 // should have been collected now.
4669     delete object;
4670 }
4671
4672 // Ensure that QObject type conversion works on binding assignment
4673 void tst_qqmlecmascript::elementAssign()
4674 {
4675     QQmlComponent component(&engine, testFileUrl("elementAssign.qml"));
4676
4677     QObject *object = component.create();
4678     QVERIFY(object != 0);
4679
4680     QCOMPARE(object->property("test").toBool(), true);
4681
4682     delete object;
4683 }
4684
4685 // QTBUG-12457
4686 void tst_qqmlecmascript::objectPassThroughSignals()
4687 {
4688     QQmlComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml"));
4689
4690     QObject *object = component.create();
4691     QVERIFY(object != 0);
4692
4693     QCOMPARE(object->property("test").toBool(), true);
4694
4695     delete object;
4696 }
4697
4698 // QTBUG-21626
4699 void tst_qqmlecmascript::objectConversion()
4700 {
4701     QQmlComponent component(&engine, testFileUrl("objectConversion.qml"));
4702
4703     QObject *object = component.create();
4704     QVERIFY(object != 0);
4705     QVariant retn;
4706     QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
4707     QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
4708
4709     delete object;
4710 }
4711
4712
4713 // QTBUG-20242
4714 void tst_qqmlecmascript::booleanConversion()
4715 {
4716     QQmlComponent component(&engine, testFileUrl("booleanConversion.qml"));
4717
4718     QObject *object = component.create();
4719     QVERIFY(object != 0);
4720
4721     QCOMPARE(object->property("test_true1").toBool(), true);
4722     QCOMPARE(object->property("test_true2").toBool(), true);
4723     QCOMPARE(object->property("test_true3").toBool(), true);
4724     QCOMPARE(object->property("test_true4").toBool(), true);
4725     QCOMPARE(object->property("test_true5").toBool(), true);
4726
4727     QCOMPARE(object->property("test_false1").toBool(), false);
4728     QCOMPARE(object->property("test_false2").toBool(), false);
4729     QCOMPARE(object->property("test_false3").toBool(), false);
4730
4731     delete object;
4732 }
4733
4734 void tst_qqmlecmascript::handleReferenceManagement()
4735 {
4736
4737     int dtorCount = 0;
4738     {
4739         // Linear QObject reference
4740         QQmlEngine hrmEngine;
4741         QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml"));
4742         QObject *object = component.create();
4743         QVERIFY(object != 0);
4744         CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4745         cro->setEngine(&hrmEngine);
4746         cro->setDtorCount(&dtorCount);
4747         QMetaObject::invokeMethod(object, "createReference");
4748         gc(engine);
4749         QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
4750         delete object;
4751         hrmEngine.collectGarbage();
4752         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4753         QCoreApplication::processEvents();
4754         QCOMPARE(dtorCount, 3);
4755     }
4756
4757     dtorCount = 0;
4758     {
4759         // Circular QObject reference
4760         QQmlEngine hrmEngine;
4761         QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml"));
4762         QObject *object = component.create();
4763         QVERIFY(object != 0);
4764         CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4765         cro->setEngine(&hrmEngine);
4766         cro->setDtorCount(&dtorCount);
4767         QMetaObject::invokeMethod(object, "circularReference");
4768         gc(engine);
4769         QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
4770         delete object;
4771         hrmEngine.collectGarbage();
4772         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4773         QCoreApplication::processEvents();
4774         QCOMPARE(dtorCount, 3);
4775     }
4776
4777     dtorCount = 0;
4778     {
4779         // Linear handle reference
4780         QQmlEngine hrmEngine;
4781         QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.1.qml"));
4782         QObject *object = component.create();
4783         QVERIFY(object != 0);
4784         CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4785         QVERIFY(crh != 0);
4786         crh->setEngine(&hrmEngine);
4787         crh->setDtorCount(&dtorCount);
4788         QMetaObject::invokeMethod(object, "createReference");
4789         CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4790         CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4791         QVERIFY(first != 0);
4792         QVERIFY(second != 0);
4793         first->addReference(QQmlData::get(second)->v8object); // create reference
4794         // now we have to reparent second and make second owned by JS.
4795         second->setParent(0);
4796         QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
4797         gc(engine);
4798         QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
4799         delete object;
4800         hrmEngine.collectGarbage();
4801         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4802         QCoreApplication::processEvents();
4803         QCOMPARE(dtorCount, 3);
4804     }
4805
4806     dtorCount = 0;
4807     {
4808         // Circular handle reference
4809         QQmlEngine hrmEngine;
4810         QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.2.qml"));
4811         QObject *object = component.create();
4812         QVERIFY(object != 0);
4813         CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4814         QVERIFY(crh != 0);
4815         crh->setEngine(&hrmEngine);
4816         crh->setDtorCount(&dtorCount);
4817         QMetaObject::invokeMethod(object, "circularReference");
4818         CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4819         CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4820         QVERIFY(first != 0);
4821         QVERIFY(second != 0);
4822         first->addReference(QQmlData::get(second)->v8object); // create circular reference
4823         second->addReference(QQmlData::get(first)->v8object); // note: must be weak.
4824         // now we have to reparent and change ownership, and unset the property references.
4825         first->setParent(0);
4826         second->setParent(0);
4827         QQmlEngine::setObjectOwnership(first, QQmlEngine::JavaScriptOwnership);
4828         QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership);
4829         object->setProperty("first", QVariant::fromValue<QObject*>(0));
4830         object->setProperty("second", QVariant::fromValue<QObject*>(0));
4831         gc(engine);
4832         QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
4833         delete object;
4834         hrmEngine.collectGarbage();
4835         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4836         QCoreApplication::processEvents();
4837         QCOMPARE(dtorCount, 3);
4838     }
4839
4840     dtorCount = 0;
4841     {
4842         // multiple engine interaction - linear reference
4843         QQmlEngine hrmEngine1;
4844         QQmlEngine hrmEngine2;
4845         QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4846         QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4847         QObject *object1 = component1.create();
4848         QObject *object2 = component2.create();
4849         QVERIFY(object1 != 0);
4850         QVERIFY(object2 != 0);
4851         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4852         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4853         QVERIFY(crh1 != 0);
4854         QVERIFY(crh2 != 0);
4855         crh1->setEngine(&hrmEngine1);
4856         crh2->setEngine(&hrmEngine2);
4857         crh1->setDtorCount(&dtorCount);
4858         crh2->setDtorCount(&dtorCount);
4859         QMetaObject::invokeMethod(object1, "createReference");
4860         QMetaObject::invokeMethod(object2, "createReference");
4861         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4862         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4863         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4864         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4865         QVERIFY(first1 != 0);
4866         QVERIFY(second1 != 0);
4867         QVERIFY(first2 != 0);
4868         QVERIFY(second2 != 0);
4869         first1->addReference(QQmlData::get(second2)->v8object); // create reference across engines
4870         // now we have to reparent second2 and make second2 owned by JS.
4871         second2->setParent(0);
4872         QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4873         gc(engine);
4874         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4875         QCoreApplication::processEvents();
4876         QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
4877         delete object1;
4878         delete object2;
4879         hrmEngine1.collectGarbage();
4880         hrmEngine2.collectGarbage();
4881         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4882         QCoreApplication::processEvents();
4883         QCOMPARE(dtorCount, 6);
4884     }
4885
4886     dtorCount = 0;
4887     {
4888         // multiple engine interaction - circular reference
4889         QQmlEngine hrmEngine1;
4890         QQmlEngine hrmEngine2;
4891         QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4892         QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4893         QObject *object1 = component1.create();
4894         QObject *object2 = component2.create();
4895         QVERIFY(object1 != 0);
4896         QVERIFY(object2 != 0);
4897         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4898         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4899         QVERIFY(crh1 != 0);
4900         QVERIFY(crh2 != 0);
4901         crh1->setEngine(&hrmEngine1);
4902         crh2->setEngine(&hrmEngine2);
4903         crh1->setDtorCount(&dtorCount);
4904         crh2->setDtorCount(&dtorCount);
4905         QMetaObject::invokeMethod(object1, "createReference");
4906         QMetaObject::invokeMethod(object2, "createReference");
4907         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4908         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4909         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4910         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4911         QVERIFY(first1 != 0);
4912         QVERIFY(second1 != 0);
4913         QVERIFY(first2 != 0);
4914         QVERIFY(second2 != 0);
4915         first1->addReference(QQmlData::get(second1)->v8object);  // create linear reference within engine1
4916         second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
4917         second2->addReference(QQmlData::get(first2)->v8object);  // create linear reference within engine2
4918         first2->addReference(QQmlData::get(first1)->v8object);   // close the loop - circular ref across engines
4919         // now we have to reparent and change ownership to JS, and remove property references.
4920         first1->setParent(0);
4921         second1->setParent(0);
4922         first2->setParent(0);
4923         second2->setParent(0);
4924         QQmlEngine::setObjectOwnership(first1, QQmlEngine::JavaScriptOwnership);
4925         QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
4926         QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
4927         QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4928         object1->setProperty("first", QVariant::fromValue<QObject*>(0));
4929         object1->setProperty("second", QVariant::fromValue<QObject*>(0));
4930         object2->setProperty("first", QVariant::fromValue<QObject*>(0));
4931         object2->setProperty("second", QVariant::fromValue<QObject*>(0));
4932         gc(engine);
4933         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4934         QCoreApplication::processEvents();
4935         QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
4936         delete object1;
4937         delete object2;
4938         hrmEngine1.collectGarbage();
4939         hrmEngine2.collectGarbage();
4940         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4941         QCoreApplication::processEvents();
4942         QCOMPARE(dtorCount, 6);
4943     }
4944
4945     dtorCount = 0;
4946     {
4947         // multiple engine interaction - linear reference with engine deletion
4948         QQmlEngine *hrmEngine1 = new QQmlEngine;
4949         QQmlEngine *hrmEngine2 = new QQmlEngine;
4950         QQmlComponent component1(hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4951         QQmlComponent component2(hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4952         QObject *object1 = component1.create();
4953         QObject *object2 = component2.create();
4954         QVERIFY(object1 != 0);
4955         QVERIFY(object2 != 0);
4956         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4957         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4958         QVERIFY(crh1 != 0);
4959         QVERIFY(crh2 != 0);
4960         crh1->setEngine(hrmEngine1);
4961         crh2->setEngine(hrmEngine2);
4962         crh1->setDtorCount(&dtorCount);
4963         crh2->setDtorCount(&dtorCount);
4964         QMetaObject::invokeMethod(object1, "createReference");
4965         QMetaObject::invokeMethod(object2, "createReference");
4966         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4967         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4968         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4969         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4970         QVERIFY(first1 != 0);
4971         QVERIFY(second1 != 0);
4972         QVERIFY(first2 != 0);
4973         QVERIFY(second2 != 0);
4974         first1->addReference(QQmlData::get(second1)->v8object);  // create linear reference within engine1
4975         second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines
4976         second2->addReference(QQmlData::get(first2)->v8object);  // create linear reference within engine2
4977         // now we have to reparent and change ownership to JS.
4978         first1->setParent(crh1);
4979         second1->setParent(0);
4980         first2->setParent(0);
4981         second2->setParent(0);
4982         QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership);
4983         QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership);
4984         QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership);
4985         gc(*hrmEngine1);
4986         gc(*hrmEngine2);
4987         QCOMPARE(dtorCount, 0);
4988         delete hrmEngine2; // should trigger deletion of objects with JS ownership tracked by this engine
4989         gc(*hrmEngine1);
4990         QCOMPARE(dtorCount, 2); // first2 and second2 should have been deleted.
4991         delete object1;
4992         delete object2;
4993         gc(*hrmEngine1);
4994         QCOMPARE(dtorCount, 6); // deleting object1 and object2 should trigger deletion of first1 and first2.
4995         delete hrmEngine1;
4996         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4997         QCoreApplication::processEvents();
4998         QCOMPARE(dtorCount, 6); // all objects should have been cleaned up prior to deleting hrmEngine1.
4999     }
5000
5001     {
5002         // Dynamic variant property reference keeps target alive
5003         QQmlEngine hrmEngine;
5004         QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.qml"));
5005         QObject *object = component.create();
5006         QVERIFY(object != 0);
5007         QMetaObject::invokeMethod(object, "createReference");
5008         gc(engine);
5009         QMetaObject::invokeMethod(object, "ensureReference");
5010         gc(engine);
5011         QMetaObject::invokeMethod(object, "removeReference");
5012         gc(engine);
5013         QMetaObject::invokeMethod(object, "ensureDeletion");
5014         QCOMPARE(object->property("success").toBool(), true);
5015         delete object;
5016     }
5017
5018     {
5019         // Dynamic Item property reference keeps target alive
5020         QQmlEngine hrmEngine;
5021         QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.2.qml"));
5022         QObject *object = component.create();
5023         QVERIFY(object != 0);
5024         QMetaObject::invokeMethod(object, "createReference");
5025         gc(engine);
5026         QMetaObject::invokeMethod(object, "ensureReference");
5027         gc(engine);
5028         QMetaObject::invokeMethod(object, "removeReference");
5029         gc(engine);
5030         QMetaObject::invokeMethod(object, "ensureDeletion");
5031         QCOMPARE(object->property("success").toBool(), true);
5032         delete object;
5033     }
5034
5035     {
5036         // Item property reference to deleted item doesn't crash
5037         QQmlEngine hrmEngine;
5038         QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.3.qml"));
5039         QObject *object = component.create();
5040         QVERIFY(object != 0);
5041         QMetaObject::invokeMethod(object, "createReference");
5042         gc(engine);
5043         QMetaObject::invokeMethod(object, "ensureReference");
5044         gc(engine);
5045         QMetaObject::invokeMethod(object, "manuallyDelete");
5046         gc(engine);
5047         QMetaObject::invokeMethod(object, "ensureDeleted");
5048         QCOMPARE(object->property("success").toBool(), true);
5049         delete object;
5050     }
5051 }
5052
5053 void tst_qqmlecmascript::stringArg()
5054 {
5055     QQmlComponent component(&engine, testFileUrl("stringArg.qml"));
5056     QObject *object = component.create();
5057     QVERIFY(object != 0);
5058     QMetaObject::invokeMethod(object, "success");
5059     QVERIFY(object->property("returnValue").toBool());
5060
5061     QString w1 = testFileUrl("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
5062     QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5063     QMetaObject::invokeMethod(object, "failure");
5064     QVERIFY(object->property("returnValue").toBool());
5065
5066     delete object;
5067 }
5068
5069 void tst_qqmlecmascript::readonlyDeclaration()
5070 {
5071     QQmlComponent component(&engine, testFileUrl("readonlyDeclaration.qml"));
5072
5073     QObject *object = component.create();
5074     QVERIFY(object != 0);
5075
5076     QCOMPARE(object->property("test").toBool(), true);
5077
5078     delete object;
5079 }
5080
5081 Q_DECLARE_METATYPE(QList<int>)
5082 Q_DECLARE_METATYPE(QList<qreal>)
5083 Q_DECLARE_METATYPE(QList<bool>)
5084 Q_DECLARE_METATYPE(QList<QString>)
5085 Q_DECLARE_METATYPE(QList<QUrl>)
5086 void tst_qqmlecmascript::sequenceConversionRead()
5087 {
5088     {
5089         QUrl qmlFile = testFileUrl("sequenceConversion.read.qml");
5090         QQmlComponent component(&engine, qmlFile);
5091         QObject *object = component.create();
5092         QVERIFY(object != 0);
5093         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5094         QVERIFY(seq != 0);
5095
5096         QMetaObject::invokeMethod(object, "readSequences");
5097         QList<int> intList; intList << 1 << 2 << 3 << 4;
5098         QCOMPARE(object->property("intListLength").toInt(), intList.length());
5099         QCOMPARE(object->property("intList").value<QList<int> >(), intList);
5100         QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
5101         QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
5102         QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
5103         QList<bool> boolList; boolList << true << false << true << false;
5104         QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
5105         QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
5106         QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5107         QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
5108         QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
5109         QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
5110         QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
5111         QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
5112         QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5113         QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
5114         QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
5115
5116         QMetaObject::invokeMethod(object, "readSequenceElements");
5117         QCOMPARE(object->property("intVal").toInt(), 2);
5118         QCOMPARE(object->property("qrealVal").toReal(), 2.2);
5119         QCOMPARE(object->property("boolVal").toBool(), false);
5120         QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
5121         QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
5122         QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
5123
5124         QMetaObject::invokeMethod(object, "enumerateSequenceElements");
5125         QCOMPARE(object->property("enumerationMatches").toBool(), true);
5126
5127         intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
5128         QQmlProperty seqProp(seq, "intListProperty");
5129         QCOMPARE(seqProp.read().value<QList<int> >(), intList);
5130         QQmlProperty seqProp2(seq, "intListProperty", &engine);
5131         QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
5132
5133         QMetaObject::invokeMethod(object, "testReferenceDeletion");
5134         QCOMPARE(object->property("referenceDeletion").toBool(), true);
5135
5136         delete object;
5137     }
5138
5139     {
5140         QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml");
5141         QQmlComponent component(&engine, qmlFile);
5142         QObject *object = component.create();
5143         QVERIFY(object != 0);
5144         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5145         QVERIFY(seq != 0);
5146
5147         // we haven't registered QList<QPoint> as a sequence type.
5148         QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5149         QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
5150         QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5151         QTest::ignoreMessage(QtWarningMsg, warningTwo.toLatin1().constData());
5152
5153         QMetaObject::invokeMethod(object, "performTest");
5154
5155         // QList<QPoint> has not been registered as a sequence type.
5156         QCOMPARE(object->property("pointListLength").toInt(), 0);
5157         QVERIFY(!object->property("pointList").isValid());
5158         QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
5159         QQmlProperty seqProp(seq, "pointListProperty", &engine);
5160         QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
5161
5162         delete object;
5163     }
5164 }
5165
5166 void tst_qqmlecmascript::sequenceConversionWrite()
5167 {
5168     {
5169         QUrl qmlFile = testFileUrl("sequenceConversion.write.qml");
5170         QQmlComponent component(&engine, qmlFile);
5171         QObject *object = component.create();
5172         QVERIFY(object != 0);
5173         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5174         QVERIFY(seq != 0);
5175
5176         QMetaObject::invokeMethod(object, "writeSequences");
5177         QCOMPARE(object->property("success").toBool(), true);
5178
5179         QMetaObject::invokeMethod(object, "writeSequenceElements");
5180         QCOMPARE(object->property("success").toBool(), true);
5181
5182         QMetaObject::invokeMethod(object, "writeOtherElements");
5183         QCOMPARE(object->property("success").toBool(), true);
5184
5185         QMetaObject::invokeMethod(object, "testReferenceDeletion");
5186         QCOMPARE(object->property("referenceDeletion").toBool(), true);
5187
5188         delete object;
5189     }
5190
5191     {
5192         QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml");
5193         QQmlComponent component(&engine, qmlFile);
5194         QObject *object = component.create();
5195         QVERIFY(object != 0);
5196         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
5197         QVERIFY(seq != 0);
5198
5199         // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
5200         QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to an unregistered type");
5201         QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
5202
5203         QMetaObject::invokeMethod(object, "performTest");
5204
5205         QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
5206         QCOMPARE(seq->pointListProperty(), pointList);
5207
5208         delete object;
5209     }
5210 }
5211
5212 void tst_qqmlecmascript::sequenceConversionArray()
5213 {
5214     // ensure that in JS the returned sequences act just like normal JS Arrays.
5215     QUrl qmlFile = testFileUrl("sequenceConversion.array.qml");
5216     QQmlComponent component(&engine, qmlFile);
5217     QObject *object = component.create();
5218     QVERIFY(object != 0);
5219     QMetaObject::invokeMethod(object, "indexedAccess");
5220     QVERIFY(object->property("success").toBool());
5221     QMetaObject::invokeMethod(object, "arrayOperations");
5222     QVERIFY(object->property("success").toBool());
5223     QMetaObject::invokeMethod(object, "testEqualitySemantics");
5224     QVERIFY(object->property("success").toBool());
5225     QMetaObject::invokeMethod(object, "testReferenceDeletion");
5226     QCOMPARE(object->property("referenceDeletion").toBool(), true);
5227     delete object;
5228 }
5229
5230
5231 void tst_qqmlecmascript::sequenceConversionIndexes()
5232 {
5233     // ensure that we gracefully fail if unsupported index values are specified.
5234     // Qt container classes only support non-negative, signed integer index values.
5235     QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
5236     QQmlComponent component(&engine, qmlFile);
5237     QObject *object = component.create();
5238     QVERIFY(object != 0);
5239     QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
5240     QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
5241     QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
5242     QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
5243     QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
5244     QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
5245     QMetaObject::invokeMethod(object, "indexedAccess");
5246     QVERIFY(object->property("success").toBool());
5247     delete object;
5248 }
5249
5250 void tst_qqmlecmascript::sequenceConversionThreads()
5251 {
5252     // ensure that sequence conversion operations work correctly in a worker thread
5253     // and that serialisation between the main and worker thread succeeds.
5254     QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml");
5255     QQmlComponent component(&engine, qmlFile);
5256     QObject *object = component.create();
5257     QVERIFY(object != 0);
5258
5259     QMetaObject::invokeMethod(object, "testIntSequence");
5260     QTRY_VERIFY(object->property("finished").toBool());
5261     QVERIFY(object->property("success").toBool());
5262
5263     QMetaObject::invokeMethod(object, "testQrealSequence");
5264     QTRY_VERIFY(object->property("finished").toBool());
5265     QVERIFY(object->property("success").toBool());
5266
5267     QMetaObject::invokeMethod(object, "testBoolSequence");
5268     QTRY_VERIFY(object->property("finished").toBool());
5269     QVERIFY(object->property("success").toBool());
5270
5271     QMetaObject::invokeMethod(object, "testStringSequence");
5272     QTRY_VERIFY(object->property("finished").toBool());
5273     QVERIFY(object->property("success").toBool());
5274
5275     QMetaObject::invokeMethod(object, "testQStringSequence");
5276     QTRY_VERIFY(object->property("finished").toBool());
5277     QVERIFY(object->property("success").toBool());
5278
5279     QMetaObject::invokeMethod(object, "testUrlSequence");
5280     QTRY_VERIFY(object->property("finished").toBool());
5281     QVERIFY(object->property("success").toBool());
5282
5283     QMetaObject::invokeMethod(object, "testVariantSequence");
5284     QTRY_VERIFY(object->property("finished").toBool());
5285     QVERIFY(object->property("success").toBool());
5286
5287     delete object;
5288 }
5289
5290 void tst_qqmlecmascript::sequenceConversionBindings()
5291 {
5292     {
5293         QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml");
5294         QQmlComponent component(&engine, qmlFile);
5295         QObject *object = component.create();
5296         QVERIFY(object != 0);
5297         QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
5298         QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
5299         QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
5300         QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
5301         QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
5302         delete object;
5303     }
5304
5305     {
5306         QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml");
5307         QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
5308         QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5309         QQmlComponent component(&engine, qmlFile);
5310         QObject *object = component.create();
5311         QVERIFY(object != 0);
5312         delete object;
5313     }
5314 }
5315
5316 void tst_qqmlecmascript::sequenceConversionCopy()
5317 {
5318     QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml");
5319     QQmlComponent component(&engine, qmlFile);
5320     QObject *object = component.create();
5321     QVERIFY(object != 0);
5322     QMetaObject::invokeMethod(object, "testCopySequences");
5323     QCOMPARE(object->property("success").toBool(), true);
5324     QMetaObject::invokeMethod(object, "readSequenceCopyElements");
5325     QCOMPARE(object->property("success").toBool(), true);
5326     QMetaObject::invokeMethod(object, "testEqualitySemantics");
5327     QCOMPARE(object->property("success").toBool(), true);
5328     delete object;
5329 }
5330
5331 void tst_qqmlecmascript::assignSequenceTypes()
5332 {
5333     // test binding array to sequence type property
5334     {
5335     QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml"));
5336     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5337     QVERIFY(object != 0);
5338     QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5339     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5340     QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5341     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5342     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5343     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5344     delete object;
5345     }
5346
5347     // test binding literal to sequence type property
5348     {
5349     QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml"));
5350     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5351     QVERIFY(object != 0);
5352     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5353     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5354     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5355     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5356     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5357     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5358     delete object;
5359     }
5360
5361     // test binding single value to sequence type property
5362     {
5363     QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml"));
5364     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5365     QVERIFY(object != 0);
5366     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5367     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5368     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5369     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5370     delete object;
5371     }
5372
5373     // test assigning array to sequence type property in js function
5374     {
5375     QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml"));
5376     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5377     QVERIFY(object != 0);
5378     QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5379     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
5380     QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5381     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5382     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5383     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5384     delete object;
5385     }
5386
5387     // test assigning literal to sequence type property in js function
5388     {
5389     QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml"));
5390     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5391     QVERIFY(object != 0);
5392     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5393     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5394     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5395     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
5396     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
5397     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
5398     delete object;
5399     }
5400
5401     // test assigning single value to sequence type property in js function
5402     {
5403     QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml"));
5404     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
5405     QVERIFY(object != 0);
5406     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5407     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5408     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
5409     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5410     delete object;
5411     }
5412
5413     // test QList<QUrl> literal assignment and binding assignment causes url resolution when required
5414     {
5415     QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml"));
5416     QObject *object = component.create();
5417     QVERIFY(object != 0);
5418     MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
5419     MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
5420     MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
5421     MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
5422     MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco5"));
5423     QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0);
5424     QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5425     QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
5426     QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5427     QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5428     QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
5429     delete object;
5430     }
5431 }
5432
5433 // Test that assigning a null object works 
5434 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
5435 void tst_qqmlecmascript::nullObjectBinding()
5436 {
5437     QQmlComponent component(&engine, testFileUrl("nullObjectBinding.qml"));
5438
5439     QObject *object = component.create();
5440     QVERIFY(object != 0);
5441
5442     QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
5443
5444     delete object;
5445 }
5446
5447 // Test that bindings don't evaluate once the engine has been destroyed
5448 void tst_qqmlecmascript::deletedEngine()
5449 {
5450     QQmlEngine *engine = new QQmlEngine;
5451     QQmlComponent component(engine, testFileUrl("deletedEngine.qml"));
5452
5453     QObject *object = component.create();
5454     QVERIFY(object != 0);
5455
5456     QCOMPARE(object->property("a").toInt(), 39);
5457     object->setProperty("b", QVariant(9));
5458     QCOMPARE(object->property("a").toInt(), 117);
5459
5460     delete engine;
5461
5462     QCOMPARE(object->property("a").toInt(), 117);
5463     object->setProperty("b", QVariant(10));
5464     QCOMPARE(object->property("a").toInt(), 117);
5465
5466     delete object;
5467 }
5468
5469 // Test the crashing part of QTBUG-9705
5470 void tst_qqmlecmascript::libraryScriptAssert()
5471 {
5472     QQmlComponent component(&engine, testFileUrl("libraryScriptAssert.qml"));
5473
5474     QObject *object = component.create();
5475     QVERIFY(object != 0);
5476
5477     delete object;
5478 }
5479
5480 void tst_qqmlecmascript::variantsAssignedUndefined()
5481 {
5482     QQmlComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml"));
5483
5484     QObject *object = component.create();
5485     QVERIFY(object != 0);
5486
5487     QCOMPARE(object->property("test1").toInt(), 10);
5488     QCOMPARE(object->property("test2").toInt(), 11);
5489
5490     object->setProperty("runTest", true);
5491
5492     QCOMPARE(object->property("test1"), QVariant());
5493     QCOMPARE(object->property("test2"), QVariant());
5494
5495
5496     delete object;
5497 }
5498
5499 void tst_qqmlecmascript::qtbug_9792()
5500 {
5501     QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml"));
5502
5503     QQmlContext *context = new QQmlContext(engine.rootContext());
5504
5505     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
5506     QVERIFY(object != 0);
5507
5508     QTest::ignoreMessage(QtDebugMsg, "Hello world!");
5509     object->basicSignal();
5510
5511     delete context;
5512
5513     transientErrorsMsgCount = 0;
5514     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5515
5516     object->basicSignal();
5517     
5518     qInstallMsgHandler(old);
5519
5520     QCOMPARE(transientErrorsMsgCount, 0);
5521
5522     delete object;
5523 }
5524
5525 // Verifies that QQmlGuard<>s used in the vmemetaobject are cleaned correctly
5526 void tst_qqmlecmascript::qtcreatorbug_1289()
5527 {
5528     QQmlComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml"));
5529
5530     QObject *o = component.create();
5531     QVERIFY(o != 0);
5532
5533     QObject *nested = qvariant_cast<QObject *>(o->property("object"));
5534     QVERIFY(nested != 0);
5535
5536     QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
5537
5538     delete nested;
5539     nested = qvariant_cast<QObject *>(o->property("object"));
5540     QVERIFY(nested == 0);
5541
5542     // If the bug is present, the next line will crash
5543     delete o;
5544 }
5545
5546 // Test that we shut down without stupid warnings
5547 void tst_qqmlecmascript::noSpuriousWarningsAtShutdown()
5548 {
5549     {
5550     QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml"));
5551
5552     QObject *o = component.create();
5553
5554     transientErrorsMsgCount = 0;
5555     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5556
5557     delete o;
5558
5559     qInstallMsgHandler(old);
5560
5561     QCOMPARE(transientErrorsMsgCount, 0);
5562     }
5563
5564
5565     {
5566     QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml"));
5567
5568     QObject *o = component.create();
5569
5570     transientErrorsMsgCount = 0;
5571     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
5572
5573     delete o;
5574
5575     qInstallMsgHandler(old);
5576
5577     QCOMPARE(transientErrorsMsgCount, 0);
5578     }
5579 }
5580
5581 void tst_qqmlecmascript::canAssignNullToQObject()
5582 {
5583     {
5584     QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml"));
5585
5586     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5587     QVERIFY(o != 0);
5588
5589     QVERIFY(o->objectProperty() != 0);
5590
5591     o->setProperty("runTest", true);
5592
5593     QVERIFY(o->objectProperty() == 0);
5594
5595     delete o;
5596     }
5597
5598     {
5599     QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml"));
5600
5601     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5602     QVERIFY(o != 0);
5603
5604     QVERIFY(o->objectProperty() == 0);
5605
5606     delete o;
5607     }
5608 }
5609
5610 void tst_qqmlecmascript::functionAssignment_fromBinding()
5611 {
5612     QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml"));
5613
5614     QString url = component.url().toString();
5615     QString w1 = url + ":4:25: Unable to assign a function to a property of any type other than var.";
5616     QString w2 = url + ":5:25: Invalid use of Qt.binding() in a binding declaration.";
5617     QString w3 = url + ":6:21: Invalid use of Qt.binding() in a binding declaration.";
5618     QString w4 = url + ":7:15: Invalid use of Qt.binding() in a binding declaration.";
5619     QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5620     QTest::ignoreMessage(QtWarningMsg, w2.toLatin1().constData());
5621     QTest::ignoreMessage(QtWarningMsg, w3.toLatin1().constData());
5622     QTest::ignoreMessage(QtWarningMsg, w4.toLatin1().constData());
5623
5624     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5625     QVERIFY(o != 0);
5626
5627     QVERIFY(!o->property("a").isValid());
5628
5629     delete o;
5630 }
5631
5632 void tst_qqmlecmascript::functionAssignment_fromJS()
5633 {
5634     QFETCH(QString, triggerProperty);
5635
5636     QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5637     QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5638
5639     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5640     QVERIFY(o != 0);
5641     QVERIFY(!o->property("a").isValid());
5642
5643     o->setProperty("aNumber", QVariant(5));
5644     o->setProperty(triggerProperty.toUtf8().constData(), true);
5645     QCOMPARE(o->property("a"), QVariant(50));
5646
5647     o->setProperty("aNumber", QVariant(10));
5648     QCOMPARE(o->property("a"), QVariant(100));
5649
5650     delete o;
5651 }
5652
5653 void tst_qqmlecmascript::functionAssignment_fromJS_data()
5654 {
5655     QTest::addColumn<QString>("triggerProperty");
5656
5657     QTest::newRow("assign to property") << "assignToProperty";
5658     QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
5659
5660     QTest::newRow("assign to value type") << "assignToValueType";
5661
5662     QTest::newRow("use 'this'") << "assignWithThis";
5663     QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
5664 }
5665
5666 void tst_qqmlecmascript::functionAssignmentfromJS_invalid()
5667 {
5668     QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5669     QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5670
5671     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5672     QVERIFY(o != 0);
5673     QVERIFY(!o->property("a").isValid());
5674
5675     o->setProperty("assignFuncWithoutReturn", true);
5676     QVERIFY(!o->property("a").isValid());
5677
5678     QString url = component.url().toString();
5679     QString warning = url + ":67:17: Unable to assign QString to int";
5680     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5681     o->setProperty("assignWrongType", true);
5682
5683     warning = url + ":71:29: Unable to assign QString to int";
5684     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5685     o->setProperty("assignWrongTypeToValueType", true);
5686
5687     delete o;
5688 }
5689
5690 void tst_qqmlecmascript::functionAssignment_afterBinding()
5691 {
5692     QQmlComponent component(&engine, testFileUrl("functionAssignment.3.qml"));
5693
5694     QString url = component.url().toString();
5695     QString w1 = url + ":16: Error: Cannot assign JavaScript function to int";
5696     QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
5697
5698     QObject *o = component.create();
5699     QVERIFY(o != 0);
5700     QCOMPARE(o->property("t1"), QVariant::fromValue<int>(4)); // should have bound
5701     QCOMPARE(o->property("t2"), QVariant::fromValue<int>(2)); // should not have changed
5702
5703     delete o;
5704 }
5705
5706 void tst_qqmlecmascript::eval()
5707 {
5708     QQmlComponent component(&engine, testFileUrl("eval.qml"));
5709
5710     QObject *o = component.create();
5711     QVERIFY(o != 0);
5712
5713     QCOMPARE(o->property("test1").toBool(), true);
5714     QCOMPARE(o->property("test2").toBool(), true);
5715     QCOMPARE(o->property("test3").toBool(), true);
5716     QCOMPARE(o->property("test4").toBool(), true);
5717     QCOMPARE(o->property("test5").toBool(), true);
5718
5719     delete o;
5720 }
5721
5722 void tst_qqmlecmascript::function()
5723 {
5724     QQmlComponent component(&engine, testFileUrl("function.qml"));
5725
5726     QObject *o = component.create();
5727     QVERIFY(o != 0);
5728
5729     QCOMPARE(o->property("test1").toBool(), true);
5730     QCOMPARE(o->property("test2").toBool(), true);
5731     QCOMPARE(o->property("test3").toBool(), true);
5732
5733     delete o;
5734 }
5735
5736 void tst_qqmlecmascript::functionException()
5737 {
5738     // QTBUG-24037 - shouldn't crash.
5739     QString errstr = testFileUrl("v8functionException.qml").toString() + QLatin1String(":13: SyntaxError: Unexpected token ILLEGAL");
5740     QTest::ignoreMessage(QtWarningMsg, qPrintable(errstr));
5741     QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Exception occurred during compilation of function: dynamicSlot()");
5742     QQmlComponent component(&engine, testFileUrl("v8functionException.qml"));
5743     QObject *o = component.create();
5744     QVERIFY(o != 0);
5745     QMetaObject::invokeMethod(o, "dynamicSlot");
5746     delete o;
5747 }
5748
5749 // Test the "Qt.include" method
5750 void tst_qqmlecmascript::include()
5751 {
5752     // Non-library relative include
5753     {
5754     QQmlComponent component(&engine, testFileUrl("include.qml"));
5755     QObject *o = component.create();
5756     QVERIFY(o != 0);
5757
5758     QCOMPARE(o->property("test0").toInt(), 99);
5759     QCOMPARE(o->property("test1").toBool(), true);
5760     QCOMPARE(o->property("test2").toBool(), true);
5761     QCOMPARE(o->property("test2_1").toBool(), true);
5762     QCOMPARE(o->property("test3").toBool(), true);
5763     QCOMPARE(o->property("test3_1").toBool(), true);
5764
5765     delete o;
5766     }
5767
5768     // Library relative include
5769     {
5770     QQmlComponent component(&engine, testFileUrl("include_shared.qml"));
5771     QObject *o = component.create();
5772     QVERIFY(o != 0);
5773
5774     QCOMPARE(o->property("test0").toInt(), 99);
5775     QCOMPARE(o->property("test1").toBool(), true);
5776     QCOMPARE(o->property("test2").toBool(), true);
5777     QCOMPARE(o->property("test2_1").toBool(), true);
5778     QCOMPARE(o->property("test3").toBool(), true);
5779     QCOMPARE(o->property("test3_1").toBool(), true);
5780
5781     delete o;
5782     }
5783
5784     // Callback
5785     {
5786     QQmlComponent component(&engine, testFileUrl("include_callback.qml"));
5787     QObject *o = component.create();
5788     QVERIFY(o != 0);
5789
5790     QCOMPARE(o->property("test1").toBool(), true);
5791     QCOMPARE(o->property("test2").toBool(), true);
5792     QCOMPARE(o->property("test3").toBool(), true);
5793     QCOMPARE(o->property("test4").toBool(), true);
5794     QCOMPARE(o->property("test5").toBool(), true);
5795     QCOMPARE(o->property("test6").toBool(), true);
5796
5797     delete o;
5798     }
5799
5800     // Including file with ".pragma library"
5801     {
5802     QQmlComponent component(&engine, testFileUrl("include_pragma.qml"));
5803     QObject *o = component.create();
5804     QVERIFY(o != 0);
5805     QCOMPARE(o->property("test1").toInt(), 100);
5806
5807     delete o;
5808     }
5809
5810     // Remote - success
5811     {
5812     TestHTTPServer server(8111);
5813     QVERIFY(server.isValid());
5814     server.serveDirectory(dataDirectory());
5815
5816     QQmlComponent component(&engine, testFileUrl("include_remote.qml"));
5817     QObject *o = component.create();
5818     QVERIFY(o != 0);
5819
5820     QTRY_VERIFY(o->property("done").toBool() == true);
5821     QTRY_VERIFY(o->property("done2").toBool() == true);
5822
5823     QCOMPARE(o->property("test1").toBool(), true);
5824     QCOMPARE(o->property("test2").toBool(), true);
5825     QCOMPARE(o->property("test3").toBool(), true);
5826     QCOMPARE(o->property("test4").toBool(), true);
5827     QCOMPARE(o->property("test5").toBool(), true);
5828
5829     QCOMPARE(o->property("test6").toBool(), true);
5830     QCOMPARE(o->property("test7").toBool(), true);
5831     QCOMPARE(o->property("test8").toBool(), true);
5832     QCOMPARE(o->property("test9").toBool(), true);
5833     QCOMPARE(o->property("test10").toBool(), true);
5834
5835     delete o;
5836     }
5837
5838     // Remote - error
5839     {
5840     TestHTTPServer server(8111);
5841     QVERIFY(server.isValid());
5842     server.serveDirectory(dataDirectory());
5843
5844     QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml"));
5845     QObject *o = component.create();
5846     QVERIFY(o != 0);
5847
5848     QTRY_VERIFY(o->property("done").toBool() == true);
5849
5850     QCOMPARE(o->property("test1").toBool(), true);
5851     QCOMPARE(o->property("test2").toBool(), true);
5852     QCOMPARE(o->property("test3").toBool(), true);
5853
5854     delete o;
5855     }
5856 }
5857
5858 void tst_qqmlecmascript::signalHandlers()
5859 {
5860     QQmlComponent component(&engine, testFileUrl("signalHandlers.qml"));
5861     QObject *o = component.create();
5862     QVERIFY(o != 0);
5863
5864     QVERIFY(o->property("count").toInt() == 0);
5865     QMetaObject::invokeMethod(o, "testSignalCall");
5866     QCOMPARE(o->property("count").toInt(), 1);
5867
5868     QMetaObject::invokeMethod(o, "testSignalHandlerCall");
5869     QCOMPARE(o->property("count").toInt(), 1);
5870     QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
5871
5872     QVERIFY(o->property("funcCount").toInt() == 0);
5873     QMetaObject::invokeMethod(o, "testSignalConnection");
5874     QCOMPARE(o->property("funcCount").toInt(), 1);
5875
5876     QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
5877     QCOMPARE(o->property("funcCount").toInt(), 2);
5878
5879     QMetaObject::invokeMethod(o, "testSignalDefined");
5880     QCOMPARE(o->property("definedResult").toBool(), true);
5881
5882     QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
5883     QCOMPARE(o->property("definedHandlerResult").toBool(), true);
5884
5885     delete o;
5886 }
5887
5888 void tst_qqmlecmascript::qtbug_10696()
5889 {
5890     QQmlComponent component(&engine, testFileUrl("qtbug_10696.qml"));
5891     QObject *o = component.create();
5892     QVERIFY(o != 0);
5893     delete o;
5894 }
5895
5896 void tst_qqmlecmascript::qtbug_11606()
5897 {
5898     QQmlComponent component(&engine, testFileUrl("qtbug_11606.qml"));
5899     QObject *o = component.create();
5900     QVERIFY(o != 0);
5901     QCOMPARE(o->property("test").toBool(), true);
5902     delete o;
5903 }
5904
5905 void tst_qqmlecmascript::qtbug_11600()
5906 {
5907     QQmlComponent component(&engine, testFileUrl("qtbug_11600.qml"));
5908     QObject *o = component.create();
5909     QVERIFY(o != 0);
5910     QCOMPARE(o->property("test").toBool(), true);
5911     delete o;
5912 }
5913
5914 void tst_qqmlecmascript::qtbug_21864()
5915 {
5916     QQmlComponent component(&engine, testFileUrl("qtbug_21864.qml"));
5917     QObject *o = component.create();
5918     QVERIFY(o != 0);
5919     QCOMPARE(o->property("test").toBool(), true);
5920     delete o;
5921 }
5922
5923 void tst_qqmlecmascript::rewriteMultiLineStrings()
5924 {
5925     {
5926         // QTBUG-23387
5927         QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml"));
5928         QObject *o = component.create();
5929         QVERIFY(o != 0);
5930         QTRY_COMPARE(o->property("test").toBool(), true);
5931         delete o;
5932     }
5933
5934     {
5935         QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings_crlf.1.qml"));
5936         QObject *o = component.create();
5937         QVERIFY(o != 0);
5938         delete o;
5939     }
5940 }
5941
5942 void tst_qqmlecmascript::qobjectConnectionListExceptionHandling()
5943 {
5944     // QTBUG-23375
5945     QQmlComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml"));
5946     QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined");
5947     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5948     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5949     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5950     QObject *o = component.create();
5951     QVERIFY(o != 0);
5952     QCOMPARE(o->property("test").toBool(), true);
5953     delete o;
5954 }
5955
5956 // Reading and writing non-scriptable properties should fail
5957 void tst_qqmlecmascript::nonscriptable()
5958 {
5959     QQmlComponent component(&engine, testFileUrl("nonscriptable.qml"));
5960     QObject *o = component.create();
5961     QVERIFY(o != 0);
5962     QCOMPARE(o->property("readOk").toBool(), true);
5963     QCOMPARE(o->property("writeOk").toBool(), true);
5964     delete o;
5965 }
5966
5967 // deleteLater() should not be callable from QML
5968 void tst_qqmlecmascript::deleteLater()
5969 {
5970     QQmlComponent component(&engine, testFileUrl("deleteLater.qml"));
5971     QObject *o = component.create();
5972     QVERIFY(o != 0);
5973     QCOMPARE(o->property("test").toBool(), true);
5974     delete o;
5975 }
5976
5977 // objectNameChanged() should be usable from QML
5978 void tst_qqmlecmascript::objectNameChangedSignal()
5979 {
5980     QQmlComponent component(&engine, testFileUrl("objectNameChangedSignal.qml"));
5981     QObject *o = component.create();
5982     QVERIFY(o != 0);
5983     QCOMPARE(o->property("test").toBool(), false);
5984     o->setObjectName("obj");
5985     QCOMPARE(o->property("test").toBool(), true);
5986     delete o;
5987 }
5988
5989 // destroyed() should not be usable from QML
5990 void tst_qqmlecmascript::destroyedSignal()
5991 {
5992     QQmlComponent component(&engine, testFileUrl("destroyedSignal.qml"));
5993     QVERIFY(component.isError());
5994
5995     QString expectedErrorString = component.url().toString() + QLatin1String(":5:5: Cannot assign to non-existent property \"onDestroyed\"");
5996     QCOMPARE(component.errors().at(0).toString(), expectedErrorString);
5997 }
5998
5999 void tst_qqmlecmascript::in()
6000 {
6001     QQmlComponent component(&engine, testFileUrl("in.qml"));
6002     QObject *o = component.create();
6003     QVERIFY(o != 0);
6004     QCOMPARE(o->property("test1").toBool(), true);
6005     QCOMPARE(o->property("test2").toBool(), true);
6006     delete o;
6007 }
6008
6009 void tst_qqmlecmascript::typeOf()
6010 {
6011     QQmlComponent component(&engine, testFileUrl("typeOf.qml"));
6012
6013     QObject *o = component.create();
6014     QVERIFY(o != 0);
6015
6016     QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
6017     QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
6018     QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
6019     QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
6020     QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
6021     QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
6022     QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
6023     QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
6024     QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
6025
6026     delete o;
6027 }
6028
6029 void tst_qqmlecmascript::qtbug_24448()
6030 {
6031     QQmlComponent component(&engine, testFileUrl("qtbug_24448.qml"));
6032     QScopedPointer<QObject> o(component.create());
6033     QVERIFY(o != 0);
6034     QVERIFY(o->property("test").toBool());
6035 }
6036
6037 void tst_qqmlecmascript::sharedAttachedObject()
6038 {
6039     QQmlComponent component(&engine, testFileUrl("sharedAttachedObject.qml"));
6040     QObject *o = component.create();
6041     QVERIFY(o != 0);
6042     QCOMPARE(o->property("test1").toBool(), true);
6043     QCOMPARE(o->property("test2").toBool(), true);
6044     delete o;
6045 }
6046
6047 // QTBUG-13999
6048 void tst_qqmlecmascript::objectName()
6049 {
6050     QQmlComponent component(&engine, testFileUrl("objectName.qml"));
6051     QObject *o = component.create();
6052     QVERIFY(o != 0);
6053
6054     QCOMPARE(o->property("test1").toString(), QString("hello"));
6055     QCOMPARE(o->property("test2").toString(), QString("ell"));
6056
6057     o->setObjectName("world");
6058
6059     QCOMPARE(o->property("test1").toString(), QString("world"));
6060     QCOMPARE(o->property("test2").toString(), QString("orl"));
6061
6062     delete o;
6063 }
6064
6065 void tst_qqmlecmascript::writeRemovesBinding()
6066 {
6067     QQmlComponent component(&engine, testFileUrl("writeRemovesBinding.qml"));
6068     QObject *o = component.create();
6069     QVERIFY(o != 0);
6070
6071     QCOMPARE(o->property("test").toBool(), true);
6072
6073     delete o;
6074 }
6075
6076 // Test bindings assigned to alias properties actually assign to the alias' target
6077 void tst_qqmlecmascript::aliasBindingsAssignCorrectly()
6078 {
6079     QQmlComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml"));
6080     QObject *o = component.create();
6081     QVERIFY(o != 0);
6082
6083     QCOMPARE(o->property("test").toBool(), true);
6084
6085     delete o;
6086 }
6087
6088 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
6089 void tst_qqmlecmascript::aliasBindingsOverrideTarget()
6090 {
6091     { 
6092     QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml"));
6093     QObject *o = component.create();
6094     QVERIFY(o != 0);
6095
6096     QCOMPARE(o->property("test").toBool(), true);
6097
6098     delete o;
6099     }
6100
6101     {
6102     QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml"));
6103     QObject *o = component.create();
6104     QVERIFY(o != 0);
6105
6106     QCOMPARE(o->property("test").toBool(), true);
6107
6108     delete o;
6109     }
6110
6111     {
6112     QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml"));
6113     QObject *o = component.create();
6114     QVERIFY(o != 0);
6115
6116     QCOMPARE(o->property("test").toBool(), true);
6117
6118     delete o;
6119     }
6120 }
6121
6122 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
6123 void tst_qqmlecmascript::aliasWritesOverrideBindings()
6124 {
6125     {
6126     QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml"));
6127     QObject *o = component.create();
6128     QVERIFY(o != 0);
6129
6130     QCOMPARE(o->property("test").toBool(), true);
6131
6132     delete o;
6133     }
6134
6135     {
6136     QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml"));
6137     QObject *o = component.create();
6138     QVERIFY(o != 0);
6139
6140     QCOMPARE(o->property("test").toBool(), true);
6141
6142     delete o;
6143     }
6144
6145     {
6146     QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml"));
6147     QObject *o = component.create();
6148     QVERIFY(o != 0);
6149
6150     QCOMPARE(o->property("test").toBool(), true);
6151
6152     delete o;
6153     }
6154 }
6155
6156 // Allow an alais to a composite element
6157 // QTBUG-20200
6158 void tst_qqmlecmascript::aliasToCompositeElement()
6159 {
6160     QQmlComponent component(&engine, testFileUrl("aliasToCompositeElement.qml"));
6161
6162     QObject *object = component.create();
6163     QVERIFY(object != 0);
6164
6165     delete object;
6166 }
6167
6168 void tst_qqmlecmascript::qtbug_20344()
6169 {
6170     QQmlComponent component(&engine, testFileUrl("qtbug_20344.qml"));
6171
6172     QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
6173     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6174
6175     QObject *object = component.create();
6176     QVERIFY(object != 0);
6177
6178     delete object;
6179 }
6180
6181 void tst_qqmlecmascript::revisionErrors()
6182 {
6183     {
6184         QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml"));
6185         QString url = component.url().toString();
6186
6187         QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6188         QString warning2 = url + ":11: ReferenceError: prop2 is not defined";
6189         QString warning3 = url + ":13: ReferenceError: method2 is not defined";
6190
6191         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6192         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6193         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6194         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6195         QVERIFY(object != 0);
6196         delete object;
6197     }
6198     {
6199         QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml"));
6200         QString url = component.url().toString();
6201
6202         // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
6203         // method2, prop2 from MyRevisionedClass not available
6204         // method4, prop4 from MyRevisionedSubclass not available
6205         QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
6206         QString warning2 = url + ":14: ReferenceError: prop2 is not defined";
6207         QString warning3 = url + ":10: ReferenceError: prop4 is not defined";
6208         QString warning4 = url + ":16: ReferenceError: prop4 is not defined";
6209         QString warning5 = url + ":20: ReferenceError: method2 is not defined";
6210
6211         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6212         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6213         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6214         QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
6215         QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
6216         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6217         QVERIFY(object != 0);
6218         delete object;
6219     }
6220     {
6221         QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml"));
6222         QString url = component.url().toString();
6223
6224         // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
6225         // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
6226         QString warning1 = url + ":30: ReferenceError: methodD is not defined";
6227         QString warning2 = url + ":10: ReferenceError: propD is not defined";
6228         QString warning3 = url + ":20: ReferenceError: propD is not defined";
6229         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
6230         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
6231         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
6232         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6233         QVERIFY(object != 0);
6234         delete object;
6235     }
6236 }
6237
6238 void tst_qqmlecmascript::revision()
6239 {
6240     {
6241         QQmlComponent component(&engine, testFileUrl("metaobjectRevision.qml"));
6242         QString url = component.url().toString();
6243
6244         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6245         QVERIFY(object != 0);
6246         delete object;
6247     }
6248     {
6249         QQmlComponent component(&engine, testFileUrl("metaobjectRevision2.qml"));
6250         QString url = component.url().toString();
6251
6252         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6253         QVERIFY(object != 0);
6254         delete object;
6255     }
6256     {
6257         QQmlComponent component(&engine, testFileUrl("metaobjectRevision3.qml"));
6258         QString url = component.url().toString();
6259
6260         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
6261         QVERIFY(object != 0);
6262         delete object;
6263     }
6264     // Test that non-root classes can resolve revisioned methods
6265     {
6266         QQmlComponent component(&engine, testFileUrl("metaobjectRevision4.qml"));
6267
6268         QObject *object = component.create();
6269         QVERIFY(object != 0);
6270         QCOMPARE(object->property("test").toReal(), 11.);
6271         delete object;
6272     }
6273 }
6274
6275 void tst_qqmlecmascript::realToInt()
6276 {
6277     QQmlComponent component(&engine, testFileUrl("realToInt.qml"));
6278     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6279     QVERIFY(object != 0);
6280
6281     QMetaObject::invokeMethod(object, "test1");
6282     QCOMPARE(object->value(), int(4));
6283     QMetaObject::invokeMethod(object, "test2");
6284     QCOMPARE(object->value(), int(8));
6285 }
6286
6287 void tst_qqmlecmascript::urlProperty()
6288 {
6289     {
6290         QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml"));
6291         MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6292         QVERIFY(object != 0);
6293         object->setStringProperty("http://qt-project.org");
6294         QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
6295         QCOMPARE(object->intProperty(), 123);
6296         QCOMPARE(object->value(), 1);
6297         QCOMPARE(object->property("result").toBool(), true);
6298     }
6299 }
6300
6301 void tst_qqmlecmascript::urlPropertyWithEncoding()
6302 {
6303     {
6304         QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml"));
6305         MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
6306         QVERIFY(object != 0);
6307         object->setStringProperty("http://qt-project.org");
6308         QUrl encoded;
6309         encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6310         QCOMPARE(object->urlProperty(), encoded);
6311         QCOMPARE(object->value(), 0);   // Interpreting URL as string yields canonicalised version
6312         QCOMPARE(object->property("result").toBool(), true);
6313     }
6314 }
6315
6316 void tst_qqmlecmascript::urlListPropertyWithEncoding()
6317 {
6318     {
6319         QQmlComponent component(&engine, testFileUrl("urlListProperty.qml"));
6320         QObject *object = component.create();
6321         QVERIFY(object != 0);
6322         MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
6323         MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
6324         MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
6325         MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
6326         QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0);
6327         QUrl encoded;
6328         encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
6329         QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded));
6330         QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded));
6331         QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6332         QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << encoded << encoded));
6333         delete object;
6334     }
6335 }
6336
6337 void tst_qqmlecmascript::dynamicString()
6338 {
6339     QQmlComponent component(&engine, testFileUrl("dynamicString.qml"));
6340     QObject *object = component.create();
6341     QVERIFY(object != 0);
6342     QCOMPARE(object->property("stringProperty").toString(),
6343              QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
6344 }
6345
6346 void tst_qqmlecmascript::deleteLaterObjectMethodCall()
6347 {
6348     QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml"));
6349     QObject *object = component.create();
6350     QVERIFY(object != 0);
6351 }
6352
6353 void tst_qqmlecmascript::automaticSemicolon()
6354 {
6355     QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml"));
6356     QObject *object = component.create();
6357     QVERIFY(object != 0);
6358 }
6359
6360 void tst_qqmlecmascript::unaryExpression()
6361 {
6362     QQmlComponent component(&engine, testFileUrl("unaryExpression.qml"));
6363     QObject *object = component.create();
6364     QVERIFY(object != 0);
6365 }
6366
6367 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
6368 void tst_qqmlecmascript::doubleEvaluate()
6369 {
6370     QQmlComponent component(&engine, testFileUrl("doubleEvaluate.qml"));
6371     QObject *object = component.create();
6372     QVERIFY(object != 0);
6373     WriteCounter *wc = qobject_cast<WriteCounter *>(object);
6374     QVERIFY(wc != 0);
6375     QCOMPARE(wc->count(), 1);
6376
6377     wc->setProperty("x", 9);
6378
6379     QCOMPARE(wc->count(), 2);
6380
6381     delete object;
6382 }
6383
6384 static QStringList messages;
6385 static void captureMsgHandler(QtMsgType, const char *msg)
6386 {
6387     messages.append(QLatin1String(msg));
6388 }
6389
6390 void tst_qqmlecmascript::nonNotifyable()
6391 {
6392     QV4Compiler::enableV4(false);
6393     QQmlComponent component(&engine, testFileUrl("nonNotifyable.qml"));
6394     QV4Compiler::enableV4(true);
6395
6396     QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
6397     messages.clear();
6398     QObject *object = component.create();
6399     qInstallMsgHandler(old);
6400
6401     QVERIFY(object != 0);
6402
6403     QString expected1 = QLatin1String("QQmlExpression: Expression ") +
6404                         component.url().toString() +
6405                         QLatin1String(":5 depends on non-NOTIFYable properties:");
6406     QString expected2 = QLatin1String("    ") +
6407                         QLatin1String(object->metaObject()->className()) +
6408                         QLatin1String("::value");
6409
6410     QCOMPARE(messages.length(), 2);
6411     QCOMPARE(messages.at(0), expected1);
6412     QCOMPARE(messages.at(1), expected2);
6413
6414     delete object;
6415 }
6416
6417 void tst_qqmlecmascript::forInLoop()
6418 {
6419     QQmlComponent component(&engine, testFileUrl("forInLoop.qml"));
6420     QObject *object = component.create();
6421     QVERIFY(object != 0);
6422
6423     QMetaObject::invokeMethod(object, "listProperty");
6424
6425     QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
6426     QCOMPARE(r.size(), 3);
6427     QCOMPARE(r[0],QLatin1String("0=obj1"));
6428     QCOMPARE(r[1],QLatin1String("1=obj2"));
6429     QCOMPARE(r[2],QLatin1String("2=obj3"));
6430
6431     //TODO: should test for in loop for other objects (such as QObjects) as well.
6432
6433     delete object;
6434 }
6435
6436 // An object the binding depends on is deleted while the binding is still running
6437 void tst_qqmlecmascript::deleteWhileBindingRunning()
6438 {
6439     QQmlComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml"));
6440     QObject *object = component.create();
6441     QVERIFY(object != 0);
6442     delete object;
6443 }
6444
6445 void tst_qqmlecmascript::qtbug_22679()
6446 {
6447     MyQmlObject object;
6448     object.setStringProperty(QLatin1String("Please work correctly"));
6449     engine.rootContext()->setContextProperty("contextProp", &object);
6450
6451     QQmlComponent component(&engine, testFileUrl("qtbug_22679.qml"));
6452     qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6453     QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6454
6455     QObject *o = component.create();
6456     QVERIFY(o != 0);
6457     QCOMPARE(warningsSpy.count(), 0);
6458     delete o;
6459 }
6460
6461 void tst_qqmlecmascript::qtbug_22843_data()
6462 {
6463     QTest::addColumn<bool>("library");
6464
6465     QTest::newRow("without .pragma library") << false;
6466     QTest::newRow("with .pragma library") << true;
6467 }
6468
6469 void tst_qqmlecmascript::qtbug_22843()
6470 {
6471     QFETCH(bool, library);
6472
6473     QString fileName("qtbug_22843");
6474     if (library)
6475         fileName += QLatin1String(".library");
6476     fileName += QLatin1String(".qml");
6477
6478     QQmlComponent component(&engine, testFileUrl(fileName));
6479     QString url = component.url().toString();
6480     QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )");
6481     QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'");
6482
6483     qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
6484     QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
6485     for (int x = 0; x < 3; ++x) {
6486         warningsSpy.clear();
6487         // For libraries, only the first import attempt should produce a
6488         // SyntaxError warning; subsequent component creation should not
6489         // attempt to reload the script.
6490         bool expectSyntaxError = !library || (x == 0);
6491         if (expectSyntaxError)
6492             QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
6493         QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
6494         QObject *object = component.create();
6495         QVERIFY(object != 0);
6496         QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
6497         delete object;
6498     }
6499 }
6500
6501
6502 void tst_qqmlecmascript::switchStatement()
6503 {
6504     {
6505         QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml"));
6506         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6507         QVERIFY(object != 0);
6508
6509         // `object->value()' is the number of executed statements
6510
6511         object->setStringProperty("A");
6512         QCOMPARE(object->value(), 5);
6513
6514         object->setStringProperty("S");
6515         QCOMPARE(object->value(), 3);
6516
6517         object->setStringProperty("D");
6518         QCOMPARE(object->value(), 3);
6519
6520         object->setStringProperty("F");
6521         QCOMPARE(object->value(), 4);
6522
6523         object->setStringProperty("something else");
6524         QCOMPARE(object->value(), 1);
6525     }
6526
6527     {
6528         QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml"));
6529         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6530         QVERIFY(object != 0);
6531
6532         // `object->value()' is the number of executed statements
6533
6534         object->setStringProperty("A");
6535         QCOMPARE(object->value(), 5);
6536
6537         object->setStringProperty("S");
6538         QCOMPARE(object->value(), 3);
6539
6540         object->setStringProperty("D");
6541         QCOMPARE(object->value(), 3);
6542
6543         object->setStringProperty("F");
6544         QCOMPARE(object->value(), 3);
6545
6546         object->setStringProperty("something else");
6547         QCOMPARE(object->value(), 4);
6548     }
6549
6550     {
6551         QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml"));
6552         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6553         QVERIFY(object != 0);
6554
6555         // `object->value()' is the number of executed statements
6556
6557         object->setStringProperty("A");
6558         QCOMPARE(object->value(), 5);
6559
6560         object->setStringProperty("S");
6561         QCOMPARE(object->value(), 3);
6562
6563         object->setStringProperty("D");
6564         QCOMPARE(object->value(), 3);
6565
6566         object->setStringProperty("F");
6567         QCOMPARE(object->value(), 3);
6568
6569         object->setStringProperty("something else");
6570         QCOMPARE(object->value(), 6);
6571     }
6572
6573     {
6574         QQmlComponent component(&engine, testFileUrl("switchStatement.4.qml"));
6575
6576         QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
6577         QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6578
6579         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6580         QVERIFY(object != 0);
6581
6582         // `object->value()' is the number of executed statements
6583
6584         object->setStringProperty("A");
6585         QCOMPARE(object->value(), 5);
6586
6587         object->setStringProperty("S");
6588         QCOMPARE(object->value(), 3);
6589
6590         object->setStringProperty("D");
6591         QCOMPARE(object->value(), 3);
6592
6593         object->setStringProperty("F");
6594         QCOMPARE(object->value(), 3);
6595
6596         QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
6597
6598         object->setStringProperty("something else");
6599     }
6600
6601     {
6602         QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml"));
6603         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6604         QVERIFY(object != 0);
6605
6606         // `object->value()' is the number of executed statements
6607
6608         object->setStringProperty("A");
6609         QCOMPARE(object->value(), 1);
6610
6611         object->setStringProperty("S");
6612         QCOMPARE(object->value(), 1);
6613
6614         object->setStringProperty("D");
6615         QCOMPARE(object->value(), 1);
6616
6617         object->setStringProperty("F");
6618         QCOMPARE(object->value(), 1);
6619
6620         object->setStringProperty("something else");
6621         QCOMPARE(object->value(), 1);
6622     }
6623
6624     {
6625         QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml"));
6626         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6627         QVERIFY(object != 0);
6628
6629         // `object->value()' is the number of executed statements
6630
6631         object->setStringProperty("A");
6632         QCOMPARE(object->value(), 123);
6633
6634         object->setStringProperty("S");
6635         QCOMPARE(object->value(), 123);
6636
6637         object->setStringProperty("D");
6638         QCOMPARE(object->value(), 321);
6639
6640         object->setStringProperty("F");
6641         QCOMPARE(object->value(), 321);
6642
6643         object->setStringProperty("something else");
6644         QCOMPARE(object->value(), 0);
6645     }
6646 }
6647
6648 void tst_qqmlecmascript::withStatement()
6649 {
6650     {
6651         QQmlComponent component(&engine, testFileUrl("withStatement.1.qml"));
6652         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6653         QVERIFY(object != 0);
6654
6655         QCOMPARE(object->value(), 123);
6656     }
6657 }
6658
6659 void tst_qqmlecmascript::tryStatement()
6660 {
6661     {
6662         QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml"));
6663         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6664         QVERIFY(object != 0);
6665
6666         QCOMPARE(object->value(), 123);
6667     }
6668
6669     {
6670         QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml"));
6671         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6672         QVERIFY(object != 0);
6673
6674         QCOMPARE(object->value(), 321);
6675     }
6676
6677     {
6678         QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml"));
6679         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6680         QVERIFY(object != 0);
6681
6682         QCOMPARE(object->value(), 1);
6683     }
6684
6685     {
6686         QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml"));
6687         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6688         QVERIFY(object != 0);
6689
6690         QCOMPARE(object->value(), 1);
6691     }
6692 }
6693
6694 class CppInvokableWithQObjectDerived : public QObject
6695 {
6696     Q_OBJECT
6697 public:
6698     CppInvokableWithQObjectDerived() {}
6699     ~CppInvokableWithQObjectDerived() {}
6700
6701     Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data)
6702     {
6703         MyQmlObject *obj = new MyQmlObject();
6704         obj->setStringProperty(data);
6705         return obj;
6706     }
6707
6708     Q_INVOKABLE QString getStringProperty(MyQmlObject *obj)
6709     {
6710         return obj->stringProperty();
6711     }
6712 };
6713
6714 void tst_qqmlecmascript::invokableWithQObjectDerived()
6715 {
6716     CppInvokableWithQObjectDerived invokable;
6717
6718     {
6719     QQmlEngine engine;
6720     engine.rootContext()->setContextProperty("invokable", &invokable);
6721
6722     QQmlComponent component(&engine, testFileUrl("qobjectDerivedArgument.qml"));
6723
6724     QObject *object = component.create();
6725
6726     QVERIFY(object != 0);
6727     QVERIFY(object->property("result").value<bool>() == true);
6728
6729     delete object;
6730     }
6731 }
6732
6733 void tst_qqmlecmascript::realTypePrecision()
6734 {
6735     // Properties and signal parameters of type real should have double precision.
6736     QQmlComponent component(&engine, testFileUrl("realTypePrecision.qml"));
6737     QScopedPointer<QObject> object(component.create());
6738     QVERIFY(object != 0);
6739     QCOMPARE(object->property("test").toDouble(), 1234567890.);
6740     QCOMPARE(object->property("test2").toDouble(), 1234567890.);
6741     QCOMPARE(object->property("test3").toDouble(), 1234567890.);
6742     QCOMPARE(object->property("test4").toDouble(), 1234567890.);
6743     QCOMPARE(object->property("test5").toDouble(), 1234567890.);
6744     QCOMPARE(object->property("test6").toDouble(), 1234567890.*2);
6745 }
6746
6747 void tst_qqmlecmascript::registeredFlagMethod()
6748 {
6749     QQmlEngine engine;
6750     QQmlComponent component(&engine, testFileUrl("registeredFlagMethod.qml"));
6751     MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6752     QVERIFY(object != 0);
6753
6754     QCOMPARE(object->buttons(), 0);
6755     emit object->basicSignal();
6756     QCOMPARE(object->buttons(), Qt::RightButton);
6757
6758     delete object;
6759 }
6760
6761 // QTBUG-23138
6762 void tst_qqmlecmascript::replaceBinding()
6763 {
6764     QQmlEngine engine;
6765     QQmlComponent c(&engine, testFileUrl("replaceBinding.qml"));
6766     QObject *obj = c.create();
6767     QVERIFY(obj != 0);
6768
6769     QVERIFY(obj->property("success").toBool());
6770     delete obj;
6771 }
6772
6773 void tst_qqmlecmascript::deleteRootObjectInCreation()
6774 {
6775     {
6776     QQmlEngine engine;
6777     QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.qml"));
6778     QObject *obj = c.create();
6779     QVERIFY(obj != 0);
6780     QVERIFY(obj->property("rootIndestructible").toBool());
6781     QVERIFY(!obj->property("childDestructible").toBool());
6782     QTest::qWait(1);
6783     QVERIFY(obj->property("childDestructible").toBool());
6784     delete obj;
6785     }
6786
6787     {
6788     QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.2.qml"));
6789     QObject *object = c.create();
6790     QVERIFY(object != 0);
6791     QVERIFY(object->property("testConditionsMet").toBool());
6792     delete object;
6793     }
6794 }
6795
6796 void tst_qqmlecmascript::onDestruction()
6797 {
6798     {
6799         // Delete object manually to invoke the associated handlers,
6800         // prior to engine destruction.
6801         QQmlEngine engine;
6802         QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
6803         QObject *obj = c.create();
6804         QVERIFY(obj != 0);
6805         delete obj;
6806     }
6807
6808     {
6809         // In this case, the teardown of the engine causes deletion
6810         // of contexts and child items.  This triggers the
6811         // onDestruction handler of a (previously .destroy()ed)
6812         // component instance.  This shouldn't crash.
6813         QQmlEngine engine;
6814         QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
6815         QObject *obj = c.create();
6816         QVERIFY(obj != 0);
6817     }
6818 }
6819
6820 struct EventProcessor : public QObject
6821 {
6822     Q_OBJECT
6823 public:
6824     Q_INVOKABLE void process()
6825     {
6826         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
6827         QCoreApplication::processEvents();
6828     }
6829 };
6830
6831 void tst_qqmlecmascript::bindingSuppression()
6832 {
6833     QQmlEngine engine;
6834     EventProcessor processor;
6835     engine.rootContext()->setContextProperty("pendingEvents", &processor);
6836
6837     transientErrorsMsgCount = 0;
6838     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
6839
6840     QQmlComponent c(&engine, testFileUrl("bindingSuppression.qml"));
6841     QObject *obj = c.create();
6842     QVERIFY(obj != 0);
6843     delete obj;
6844
6845     qInstallMsgHandler(old);
6846     QCOMPARE(transientErrorsMsgCount, 0);
6847 }
6848
6849 void tst_qqmlecmascript::signalEmitted()
6850 {
6851     {
6852         // calling destroy on the sibling.
6853         QQmlEngine engine;
6854         QQmlComponent c(&engine, testFileUrl("signalEmitted.2.qml"));
6855         QObject *obj = c.create();
6856         QVERIFY(obj != 0);
6857         QTRY_VERIFY(obj->property("success").toBool());
6858         delete obj;
6859     }
6860
6861     {
6862         // allowing gc to clean up the sibling.
6863         QQmlEngine engine;
6864         QQmlComponent c(&engine, testFileUrl("signalEmitted.3.qml"));
6865         QObject *obj = c.create();
6866         QVERIFY(obj != 0);
6867         gc(engine); // should collect c1.
6868         QTRY_VERIFY(obj->property("success").toBool());
6869         delete obj;
6870     }
6871
6872     {
6873         // allowing gc to clean up the sibling after manually destroying target.
6874         QQmlEngine engine;
6875         QQmlComponent c(&engine, testFileUrl("signalEmitted.4.qml"));
6876         QObject *obj = c.create();
6877         QVERIFY(obj != 0);
6878         gc(engine); // should collect c1.
6879         QMetaObject::invokeMethod(obj, "destroyC2");
6880         QTRY_VERIFY(obj->property("success").toBool()); // handles events (incl. delete later).
6881         delete obj;
6882     }
6883 }
6884
6885 // QTBUG-25647
6886 void tst_qqmlecmascript::threadSignal()
6887 {
6888     {
6889     QQmlComponent c(&engine, testFileUrl("threadSignal.qml"));
6890     QObject *object = c.create();
6891     QVERIFY(object != 0);
6892     QTRY_VERIFY(object->property("passed").toBool());
6893     delete object;
6894     }
6895     {
6896     QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml"));
6897     QObject *object = c.create();
6898     QVERIFY(object != 0);
6899     QSignalSpy doneSpy(object, SIGNAL(done(const QString &)));
6900     QMetaObject::invokeMethod(object, "doIt");
6901     QTRY_VERIFY(object->property("passed").toBool());
6902     QCOMPARE(doneSpy.count(), 1);
6903     delete object;
6904     }
6905 }
6906
6907 // ensure that the qqmldata::destroyed() handler doesn't cause problems
6908 void tst_qqmlecmascript::qqmldataDestroyed()
6909 {
6910     // gc cleans up a qobject, later the qqmldata destroyed handler will run.
6911     {
6912         QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.qml"));
6913         QObject *object = c.create();
6914         QVERIFY(object != 0);
6915         // now gc causing the collection of the dynamically constructed object.
6916         engine.collectGarbage();
6917         engine.collectGarbage();
6918         // now process events to allow deletion (calling qqmldata::destroyed())
6919         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
6920         QCoreApplication::processEvents();
6921         // shouldn't crash.
6922         delete object;
6923     }
6924
6925     // in this case, the object has CPP ownership, and the gc will
6926     // be triggered during its beginCreate stage.
6927     {
6928         QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.2.qml"));
6929         QObject *object = c.create();
6930         QVERIFY(object != 0);
6931         QVERIFY(object->property("testConditionsMet").toBool());
6932         // the gc() within the handler will have triggered the weak
6933         // qobject reference callback.  If that incorrectly disposes
6934         // the handle, when the qqmldata::destroyed() handler is
6935         // called due to object deletion we will see a crash.
6936         delete object;
6937         // shouldn't have crashed.
6938     }
6939 }
6940
6941 void tst_qqmlecmascript::secondAlias()
6942 {
6943     QQmlComponent c(&engine, testFileUrl("secondAlias.qml"));
6944     QObject *object = c.create();
6945     QVERIFY(object != 0);
6946     QCOMPARE(object->property("test").toInt(), 200);
6947     delete object;
6948 }
6949
6950 // An alias to a var property works
6951 void tst_qqmlecmascript::varAlias()
6952 {
6953     QQmlComponent c(&engine, testFileUrl("varAlias.qml"));
6954     QObject *object = c.create();
6955     QVERIFY(object != 0);
6956     QCOMPARE(object->property("test").toInt(), 192);
6957     delete object;
6958 }
6959
6960 // Used to trigger an assert in the lazy meta object creation stage
6961 void tst_qqmlecmascript::overrideDataAssert()
6962 {
6963     QQmlComponent c(&engine, testFileUrl("overrideDataAssert.qml"));
6964     QObject *object = c.create();
6965     QVERIFY(object != 0);
6966     object->metaObject();
6967     delete object;
6968 }
6969
6970 QTEST_MAIN(tst_qqmlecmascript)
6971
6972 #include "tst_qqmlecmascript.moc"