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