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