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