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