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