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