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