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