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