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