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