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