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