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