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