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