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