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