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