Remove unused codepaths from QV8SequenceWrapper
[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     QTest::ignoreMessage(QtDebugMsg, "hello world");
1594     QObject *object = qobject_cast<QObject *>(component.create());
1595     delete object;
1596 }
1597
1598 // QTBUG-5759
1599 void tst_qdeclarativeecmascript::jsObject()
1600 {
1601     QDeclarativeComponent component(&engine, TEST_FILE("jsObject.qml"));
1602     QObject *object = component.create();
1603     QVERIFY(object != 0);
1604
1605     QCOMPARE(object->property("test").toInt(), 92);
1606
1607     delete object;
1608 }
1609
1610 void tst_qdeclarativeecmascript::undefinedResetsProperty()
1611 {
1612     {
1613     QDeclarativeComponent component(&engine, TEST_FILE("undefinedResetsProperty.qml"));
1614     QObject *object = component.create();
1615     QVERIFY(object != 0);
1616
1617     QCOMPARE(object->property("resettableProperty").toInt(), 92);
1618
1619     object->setProperty("setUndefined", true);
1620
1621     QCOMPARE(object->property("resettableProperty").toInt(), 13);
1622
1623     object->setProperty("setUndefined", false);
1624
1625     QCOMPARE(object->property("resettableProperty").toInt(), 92);
1626
1627     delete object;
1628     }
1629     {
1630     QDeclarativeComponent component(&engine, TEST_FILE("undefinedResetsProperty.2.qml"));
1631     QObject *object = component.create();
1632     QVERIFY(object != 0);
1633
1634     QCOMPARE(object->property("resettableProperty").toInt(), 19);
1635
1636     QMetaObject::invokeMethod(object, "doReset");
1637
1638     QCOMPARE(object->property("resettableProperty").toInt(), 13);
1639
1640     delete object;
1641     }
1642 }
1643
1644 // QTBUG-6781
1645 void tst_qdeclarativeecmascript::bug1()
1646 {
1647     QDeclarativeComponent component(&engine, TEST_FILE("bug.1.qml"));
1648     QObject *object = component.create();
1649     QVERIFY(object != 0);
1650
1651     QCOMPARE(object->property("test").toInt(), 14);
1652
1653     object->setProperty("a", 11);
1654
1655     QCOMPARE(object->property("test").toInt(), 3);
1656
1657     object->setProperty("b", true);
1658
1659     QCOMPARE(object->property("test").toInt(), 9);
1660
1661     delete object;
1662 }
1663
1664 void tst_qdeclarativeecmascript::bug2()
1665 {
1666     QDeclarativeComponent component(&engine);
1667     component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl());
1668
1669     QObject *object = component.create();
1670     QVERIFY(object != 0);
1671
1672     delete object;
1673 }
1674
1675 // Don't crash in createObject when the component has errors.
1676 void tst_qdeclarativeecmascript::dynamicCreationCrash()
1677 {
1678     QDeclarativeComponent component(&engine, TEST_FILE("dynamicCreation.qml"));
1679     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1680     QVERIFY(object != 0);
1681
1682     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
1683     QMetaObject::invokeMethod(object, "dontCrash");
1684     QObject *created = object->objectProperty();
1685     QVERIFY(created == 0);
1686
1687     delete object;
1688 }
1689
1690 // ownership transferred to JS, ensure that GC runs the dtor
1691 void tst_qdeclarativeecmascript::dynamicCreationOwnership()
1692 {
1693     int dtorCount = 0;
1694     int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
1695
1696     // allow the engine to go out of scope too.
1697     {
1698         QDeclarativeEngine dcoEngine;
1699         QDeclarativeComponent component(&dcoEngine, TEST_FILE("dynamicCreationOwnership.qml"));
1700         QObject *object = component.create();
1701         QVERIFY(object != 0);
1702         MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo");
1703         QVERIFY(mdcdo != 0);
1704         mdcdo->setDtorCount(&dtorCount);
1705
1706         for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
1707             QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
1708             if (i % 90 == 0) {
1709                 // we do this once manually, but it should be done automatically
1710                 // when the engine goes out of scope (since it should gc in dtor)
1711                 QMetaObject::invokeMethod(object, "performGc");
1712             }
1713             if (i % 10 == 0) {
1714                 QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
1715             }
1716         }
1717
1718         delete object;
1719     }
1720     QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
1721     QCOMPARE(dtorCount, expectedDtorCount);
1722 }
1723
1724 //QTBUG-9367
1725 void tst_qdeclarativeecmascript::regExpBug()
1726 {
1727     QDeclarativeComponent component(&engine, TEST_FILE("regExp.qml"));
1728     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1729     QVERIFY(object != 0);
1730     QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]"));
1731     delete object;
1732 }
1733
1734 static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source)
1735 {
1736     QString functionSource = QLatin1String("(function(object) { return ") + 
1737                              QLatin1String(source) + QLatin1String(" })");
1738     v8::TryCatch tc;
1739     v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
1740     if (tc.HasCaught())
1741         return false;
1742     v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
1743     if (function.IsEmpty())
1744         return false;
1745     v8::Handle<v8::Value> args[] = { o };
1746     function->Call(engine->global(), 1, args);
1747     return tc.HasCaught();
1748 }
1749
1750 static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o, 
1751                                   const char *source, v8::Handle<v8::Value> result)
1752 {
1753     QString functionSource = QLatin1String("(function(object) { return ") + 
1754                              QLatin1String(source) + QLatin1String(" })");
1755     v8::TryCatch tc;
1756     v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
1757     if (tc.HasCaught())
1758         return false;
1759     v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
1760     if (function.IsEmpty())
1761         return false;
1762     v8::Handle<v8::Value> args[] = { o };
1763
1764     v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
1765
1766     if (tc.HasCaught())
1767         return false;
1768
1769     return value->StrictEquals(result);
1770 }
1771
1772 static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o, 
1773                                              const char *source)
1774 {
1775     QString functionSource = QLatin1String("(function(object) { return ") + 
1776                              QLatin1String(source) + QLatin1String(" })");
1777     v8::TryCatch tc;
1778     v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
1779     if (tc.HasCaught())
1780         return v8::Handle<v8::Value>();
1781     v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
1782     if (function.IsEmpty())
1783         return v8::Handle<v8::Value>();
1784     v8::Handle<v8::Value> args[] = { o };
1785
1786     v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
1787
1788     if (tc.HasCaught())
1789         return v8::Handle<v8::Value>();
1790     return value;
1791 }
1792
1793 #define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
1794 #define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result)
1795 #define EVALUATE(source) evaluate(engine, object, source)
1796
1797 void tst_qdeclarativeecmascript::callQtInvokables()
1798 {
1799     MyInvokableObject o;
1800
1801     QDeclarativeEngine qmlengine;
1802     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&qmlengine);
1803     
1804     QV8Engine *engine = ep->v8engine();
1805
1806     v8::HandleScope handle_scope;
1807     v8::Context::Scope scope(engine->context());
1808
1809     v8::Local<v8::Object> object = engine->newQObject(&o)->ToObject();
1810
1811     // Non-existent methods
1812     o.reset();
1813     QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
1814     QCOMPARE(o.error(), false);
1815     QCOMPARE(o.invoked(), -1);
1816     QCOMPARE(o.actuals().count(), 0);
1817
1818     o.reset();
1819     QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)"));
1820     QCOMPARE(o.error(), false);
1821     QCOMPARE(o.invoked(), -1);
1822     QCOMPARE(o.actuals().count(), 0);
1823
1824     // Insufficient arguments
1825     o.reset();
1826     QVERIFY(EVALUATE_ERROR("object.method_int()"));
1827     QCOMPARE(o.error(), false);
1828     QCOMPARE(o.invoked(), -1);
1829     QCOMPARE(o.actuals().count(), 0);
1830
1831     o.reset();
1832     QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
1833     QCOMPARE(o.error(), false);
1834     QCOMPARE(o.invoked(), -1);
1835     QCOMPARE(o.actuals().count(), 0);
1836
1837     // Excessive arguments
1838     o.reset();
1839     QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", v8::Undefined()));
1840     QCOMPARE(o.error(), false);
1841     QCOMPARE(o.invoked(), 8);
1842     QCOMPARE(o.actuals().count(), 1);
1843     QCOMPARE(o.actuals().at(0), QVariant(10));
1844
1845     o.reset();
1846     QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", v8::Undefined()));
1847     QCOMPARE(o.error(), false);
1848     QCOMPARE(o.invoked(), 9);
1849     QCOMPARE(o.actuals().count(), 2);
1850     QCOMPARE(o.actuals().at(0), QVariant(10));
1851     QCOMPARE(o.actuals().at(1), QVariant(11));
1852
1853     // Test return types
1854     o.reset();
1855     QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", v8::Undefined()));
1856     QCOMPARE(o.error(), false);
1857     QCOMPARE(o.invoked(), 0);
1858     QCOMPARE(o.actuals().count(), 0);
1859
1860     o.reset();
1861     QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", v8::Integer::New(6)));
1862     QCOMPARE(o.error(), false);
1863     QCOMPARE(o.invoked(), 1);
1864     QCOMPARE(o.actuals().count(), 0);
1865
1866     o.reset();
1867     QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", v8::Number::New(19.75)));
1868     QCOMPARE(o.error(), false);
1869     QCOMPARE(o.invoked(), 2);
1870     QCOMPARE(o.actuals().count(), 0);
1871
1872     o.reset();
1873     {
1874     v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QPointF()");
1875     QVERIFY(!ret.IsEmpty());
1876     QCOMPARE(engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5)));
1877     QCOMPARE(o.error(), false);
1878     QCOMPARE(o.invoked(), 3);
1879     QCOMPARE(o.actuals().count(), 0);
1880     }
1881
1882     o.reset();
1883     {
1884     v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QObject()");
1885     QCOMPARE(engine->toQObject(ret), (QObject *)&o);
1886     QCOMPARE(o.error(), false);
1887     QCOMPARE(o.invoked(), 4);
1888     QCOMPARE(o.actuals().count(), 0);
1889     }
1890
1891     o.reset();
1892     QVERIFY(EVALUATE_VALUE("object.method_NoArgs_unknown()", v8::Undefined()));
1893     QCOMPARE(o.error(), false);
1894     QCOMPARE(o.invoked(), 5);
1895     QCOMPARE(o.actuals().count(), 0);
1896
1897     o.reset();
1898     {
1899     v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QScriptValue()");
1900     QVERIFY(ret->IsString());
1901     QCOMPARE(engine->toString(ret), QString("Hello world"));
1902     QCOMPARE(o.error(), false);
1903     QCOMPARE(o.invoked(), 6);
1904     QCOMPARE(o.actuals().count(), 0);
1905     }
1906
1907     o.reset();
1908     QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks")));
1909     QCOMPARE(o.error(), false);
1910     QCOMPARE(o.invoked(), 7);
1911     QCOMPARE(o.actuals().count(), 0);
1912
1913     // Test arg types
1914     o.reset();
1915     QVERIFY(EVALUATE_VALUE("object.method_int(94)", v8::Undefined()));
1916     QCOMPARE(o.error(), false);
1917     QCOMPARE(o.invoked(), 8);
1918     QCOMPARE(o.actuals().count(), 1);
1919     QCOMPARE(o.actuals().at(0), QVariant(94));
1920
1921     o.reset();
1922     QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", v8::Undefined()));
1923     QCOMPARE(o.error(), false);
1924     QCOMPARE(o.invoked(), 8);
1925     QCOMPARE(o.actuals().count(), 1);
1926     QCOMPARE(o.actuals().at(0), QVariant(94));
1927
1928     o.reset();
1929     QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", v8::Undefined()));
1930     QCOMPARE(o.error(), false);
1931     QCOMPARE(o.invoked(), 8);
1932     QCOMPARE(o.actuals().count(), 1);
1933     QCOMPARE(o.actuals().at(0), QVariant(0));
1934
1935     o.reset();
1936     QVERIFY(EVALUATE_VALUE("object.method_int(null)", v8::Undefined()));
1937     QCOMPARE(o.error(), false);
1938     QCOMPARE(o.invoked(), 8);
1939     QCOMPARE(o.actuals().count(), 1);
1940     QCOMPARE(o.actuals().at(0), QVariant(0));
1941
1942     o.reset();
1943     QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", v8::Undefined()));
1944     QCOMPARE(o.error(), false);
1945     QCOMPARE(o.invoked(), 8);
1946     QCOMPARE(o.actuals().count(), 1);
1947     QCOMPARE(o.actuals().at(0), QVariant(0));
1948
1949     o.reset();
1950     QVERIFY(EVALUATE_VALUE("object.method_int(object)", v8::Undefined()));
1951     QCOMPARE(o.error(), false);
1952     QCOMPARE(o.invoked(), 8);
1953     QCOMPARE(o.actuals().count(), 1);
1954     QCOMPARE(o.actuals().at(0), QVariant(0));
1955
1956     o.reset();
1957     QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", v8::Undefined()));
1958     QCOMPARE(o.error(), false);
1959     QCOMPARE(o.invoked(), 9);
1960     QCOMPARE(o.actuals().count(), 2);
1961     QCOMPARE(o.actuals().at(0), QVariant(122));
1962     QCOMPARE(o.actuals().at(1), QVariant(9));
1963
1964     o.reset();
1965     QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", v8::Undefined()));
1966     QCOMPARE(o.error(), false);
1967     QCOMPARE(o.invoked(), 10);
1968     QCOMPARE(o.actuals().count(), 1);
1969     QCOMPARE(o.actuals().at(0), QVariant(94.3));
1970
1971     o.reset();
1972     QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", v8::Undefined()));
1973     QCOMPARE(o.error(), false);
1974     QCOMPARE(o.invoked(), 10);
1975     QCOMPARE(o.actuals().count(), 1);
1976     QCOMPARE(o.actuals().at(0), QVariant(94.3));
1977
1978     o.reset();
1979     QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", v8::Undefined()));
1980     QCOMPARE(o.error(), false);
1981     QCOMPARE(o.invoked(), 10);
1982     QCOMPARE(o.actuals().count(), 1);
1983     QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
1984
1985     o.reset();
1986     QVERIFY(EVALUATE_VALUE("object.method_real(null)", v8::Undefined()));
1987     QCOMPARE(o.error(), false);
1988     QCOMPARE(o.invoked(), 10);
1989     QCOMPARE(o.actuals().count(), 1);
1990     QCOMPARE(o.actuals().at(0), QVariant(0));
1991
1992     o.reset();
1993     QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", v8::Undefined()));
1994     QCOMPARE(o.error(), false);
1995     QCOMPARE(o.invoked(), 10);
1996     QCOMPARE(o.actuals().count(), 1);
1997     QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
1998
1999     o.reset();
2000     QVERIFY(EVALUATE_VALUE("object.method_real(object)", v8::Undefined()));
2001     QCOMPARE(o.error(), false);
2002     QCOMPARE(o.invoked(), 10);
2003     QCOMPARE(o.actuals().count(), 1);
2004     QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2005
2006     o.reset();
2007     QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", v8::Undefined()));
2008     QCOMPARE(o.error(), false);
2009     QCOMPARE(o.invoked(), 11);
2010     QCOMPARE(o.actuals().count(), 1);
2011     QCOMPARE(o.actuals().at(0), QVariant("Hello world"));
2012
2013     o.reset();
2014     QVERIFY(EVALUATE_VALUE("object.method_QString(19)", v8::Undefined()));
2015     QCOMPARE(o.error(), false);
2016     QCOMPARE(o.invoked(), 11);
2017     QCOMPARE(o.actuals().count(), 1);
2018     QCOMPARE(o.actuals().at(0), QVariant("19"));
2019
2020     o.reset();
2021     {
2022     QString expected = "MyInvokableObject(0x" + QString::number((quintptr)&o, 16) + ")";
2023     QVERIFY(EVALUATE_VALUE("object.method_QString(object)", v8::Undefined()));
2024     QCOMPARE(o.error(), false);
2025     QCOMPARE(o.invoked(), 11);
2026     QCOMPARE(o.actuals().count(), 1);
2027     QCOMPARE(o.actuals().at(0), QVariant(expected));
2028     }
2029
2030     o.reset();
2031     QVERIFY(EVALUATE_VALUE("object.method_QString(null)", v8::Undefined()));
2032     QCOMPARE(o.error(), false);
2033     QCOMPARE(o.invoked(), 11);
2034     QCOMPARE(o.actuals().count(), 1);
2035     QCOMPARE(o.actuals().at(0), QVariant(QString()));
2036
2037     o.reset();
2038     QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", v8::Undefined()));
2039     QCOMPARE(o.error(), false);
2040     QCOMPARE(o.invoked(), 11);
2041     QCOMPARE(o.actuals().count(), 1);
2042     QCOMPARE(o.actuals().at(0), QVariant(QString()));
2043
2044     o.reset();
2045     QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", v8::Undefined()));
2046     QCOMPARE(o.error(), false);
2047     QCOMPARE(o.invoked(), 12);
2048     QCOMPARE(o.actuals().count(), 1);
2049     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2050
2051     o.reset();
2052     QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", v8::Undefined()));
2053     QCOMPARE(o.error(), false);
2054     QCOMPARE(o.invoked(), 12);
2055     QCOMPARE(o.actuals().count(), 1);
2056     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2057
2058     o.reset();
2059     QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", v8::Undefined()));
2060     QCOMPARE(o.error(), false);
2061     QCOMPARE(o.invoked(), 12);
2062     QCOMPARE(o.actuals().count(), 1);
2063     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2064
2065     o.reset();
2066     QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", v8::Undefined()));
2067     QCOMPARE(o.error(), false);
2068     QCOMPARE(o.invoked(), 12);
2069     QCOMPARE(o.actuals().count(), 1);
2070     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2071
2072     o.reset();
2073     QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", v8::Undefined()));
2074     QCOMPARE(o.error(), false);
2075     QCOMPARE(o.invoked(), 12);
2076     QCOMPARE(o.actuals().count(), 1);
2077     QCOMPARE(o.actuals().at(0), QVariant(QPointF(99.3, -10.2)));
2078
2079     o.reset();
2080     QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", v8::Undefined()));
2081     QCOMPARE(o.error(), false);
2082     QCOMPARE(o.invoked(), 12);
2083     QCOMPARE(o.actuals().count(), 1);
2084     QCOMPARE(o.actuals().at(0), QVariant(QPointF(9, 12)));
2085
2086     o.reset();
2087     QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", v8::Undefined()));
2088     QCOMPARE(o.error(), false);
2089     QCOMPARE(o.invoked(), 13);
2090     QCOMPARE(o.actuals().count(), 1);
2091     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2092
2093     o.reset();
2094     QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", v8::Undefined()));
2095     QCOMPARE(o.error(), false);
2096     QCOMPARE(o.invoked(), 13);
2097     QCOMPARE(o.actuals().count(), 1);
2098     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2099
2100     o.reset();
2101     QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", v8::Undefined()));
2102     QCOMPARE(o.error(), false);
2103     QCOMPARE(o.invoked(), 13);
2104     QCOMPARE(o.actuals().count(), 1);
2105     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2106
2107     o.reset();
2108     QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", v8::Undefined()));
2109     QCOMPARE(o.error(), false);
2110     QCOMPARE(o.invoked(), 13);
2111     QCOMPARE(o.actuals().count(), 1);
2112     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2113
2114     o.reset();
2115     QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", v8::Undefined()));
2116     QCOMPARE(o.error(), false);
2117     QCOMPARE(o.invoked(), 13);
2118     QCOMPARE(o.actuals().count(), 1);
2119     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)&o));
2120
2121     o.reset();
2122     QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined()));
2123     QCOMPARE(o.error(), false);
2124     QCOMPARE(o.invoked(), 14);
2125     QCOMPARE(o.actuals().count(), 1);
2126     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isNull());
2127
2128     o.reset();
2129     QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined()));
2130     QCOMPARE(o.error(), false);
2131     QCOMPARE(o.invoked(), 14);
2132     QCOMPARE(o.actuals().count(), 1);
2133     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isUndefined());
2134
2135     o.reset();
2136     QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined()));
2137     QCOMPARE(o.error(), false);
2138     QCOMPARE(o.invoked(), 14);
2139     QCOMPARE(o.actuals().count(), 1);
2140     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).strictlyEquals(QJSValue(19)));
2141
2142     o.reset();
2143     QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined()));
2144     QCOMPARE(o.error(), false);
2145     QCOMPARE(o.invoked(), 14);
2146     QCOMPARE(o.actuals().count(), 1);
2147     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isArray());
2148
2149     o.reset();
2150     QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined()));
2151     QCOMPARE(o.error(), false);
2152     QCOMPARE(o.invoked(), 15);
2153     QCOMPARE(o.actuals().count(), 2);
2154     QCOMPARE(o.actuals().at(0), QVariant(4));
2155     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isNull());
2156
2157     o.reset();
2158     QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined()));
2159     QCOMPARE(o.error(), false);
2160     QCOMPARE(o.invoked(), 15);
2161     QCOMPARE(o.actuals().count(), 2);
2162     QCOMPARE(o.actuals().at(0), QVariant(8));
2163     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isUndefined());
2164
2165     o.reset();
2166     QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined()));
2167     QCOMPARE(o.error(), false);
2168     QCOMPARE(o.invoked(), 15);
2169     QCOMPARE(o.actuals().count(), 2);
2170     QCOMPARE(o.actuals().at(0), QVariant(3));
2171     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).strictlyEquals(QJSValue(19)));
2172
2173     o.reset();
2174     QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined()));
2175     QCOMPARE(o.error(), false);
2176     QCOMPARE(o.invoked(), 15);
2177     QCOMPARE(o.actuals().count(), 2);
2178     QCOMPARE(o.actuals().at(0), QVariant(44));
2179     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isArray());
2180
2181     o.reset();
2182     QVERIFY(EVALUATE_ERROR("object.method_overload()"));
2183     QCOMPARE(o.error(), false);
2184     QCOMPARE(o.invoked(), -1);
2185     QCOMPARE(o.actuals().count(), 0);
2186
2187     o.reset();
2188     QVERIFY(EVALUATE_VALUE("object.method_overload(10)", v8::Undefined()));
2189     QCOMPARE(o.error(), false);
2190     QCOMPARE(o.invoked(), 16);
2191     QCOMPARE(o.actuals().count(), 1);
2192     QCOMPARE(o.actuals().at(0), QVariant(10));
2193
2194     o.reset();
2195     QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", v8::Undefined()));
2196     QCOMPARE(o.error(), false);
2197     QCOMPARE(o.invoked(), 17);
2198     QCOMPARE(o.actuals().count(), 2);
2199     QCOMPARE(o.actuals().at(0), QVariant(10));
2200     QCOMPARE(o.actuals().at(1), QVariant(11));
2201
2202     o.reset();
2203     QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", v8::Undefined()));
2204     QCOMPARE(o.error(), false);
2205     QCOMPARE(o.invoked(), 18);
2206     QCOMPARE(o.actuals().count(), 1);
2207     QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2208
2209     o.reset();
2210     QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", v8::Undefined()));
2211     QCOMPARE(o.error(), false);
2212     QCOMPARE(o.invoked(), 19);
2213     QCOMPARE(o.actuals().count(), 1);
2214     QCOMPARE(o.actuals().at(0), QVariant(9));
2215
2216     o.reset();
2217     QVERIFY(EVALUATE_VALUE("object.method_default(10)", v8::Integer::New(19)));
2218     QCOMPARE(o.error(), false);
2219     QCOMPARE(o.invoked(), 20);
2220     QCOMPARE(o.actuals().count(), 2);
2221     QCOMPARE(o.actuals().at(0), QVariant(10));
2222     QCOMPARE(o.actuals().at(1), QVariant(19));
2223
2224     o.reset();
2225     QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", v8::Integer::New(13)));
2226     QCOMPARE(o.error(), false);
2227     QCOMPARE(o.invoked(), 20);
2228     QCOMPARE(o.actuals().count(), 2);
2229     QCOMPARE(o.actuals().at(0), QVariant(10));
2230     QCOMPARE(o.actuals().at(1), QVariant(13));
2231
2232     o.reset();
2233     QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", v8::Undefined()));
2234     QCOMPARE(o.error(), false);
2235     QCOMPARE(o.invoked(), -3);
2236     QCOMPARE(o.actuals().count(), 1);
2237     QCOMPARE(o.actuals().at(0), QVariant(9));
2238
2239     o.reset();
2240     QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", v8::Undefined()));
2241     QCOMPARE(o.error(), false);
2242     QCOMPARE(o.invoked(), 21);
2243     QCOMPARE(o.actuals().count(), 2);
2244     QCOMPARE(o.actuals().at(0), QVariant(9));
2245     QCOMPARE(o.actuals().at(1), QVariant());
2246
2247     o.reset();
2248     QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", v8::Undefined()));
2249     QCOMPARE(o.error(), false);
2250     QCOMPARE(o.invoked(), 21);
2251     QCOMPARE(o.actuals().count(), 2);
2252     QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2253     QCOMPARE(o.actuals().at(1), QVariant(QString("World")));
2254 }
2255
2256 // QTBUG-13047 (check that you can pass registered object types as args)
2257 void tst_qdeclarativeecmascript::invokableObjectArg()
2258 {
2259     QDeclarativeComponent component(&engine, TEST_FILE("invokableObjectArg.qml"));
2260
2261     QObject *o = component.create();
2262     QVERIFY(o);
2263     MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o);
2264     QVERIFY(qmlobject);
2265     QCOMPARE(qmlobject->myinvokableObject, qmlobject);
2266
2267     delete o;
2268 }
2269
2270 // QTBUG-13047 (check that you can return registered object types from methods)
2271 void tst_qdeclarativeecmascript::invokableObjectRet()
2272 {
2273     QDeclarativeComponent component(&engine, TEST_FILE("invokableObjectRet.qml"));
2274
2275     QObject *o = component.create();
2276     QVERIFY(o);
2277     QCOMPARE(o->property("test").toBool(), true);
2278     delete o;
2279 }
2280
2281 // QTBUG-5675
2282 void tst_qdeclarativeecmascript::listToVariant()
2283 {
2284     QDeclarativeComponent component(&engine, TEST_FILE("listToVariant.qml"));
2285
2286     MyQmlContainer container;
2287
2288     QDeclarativeContext context(engine.rootContext());
2289     context.setContextObject(&container);
2290
2291     QObject *object = component.create(&context);
2292     QVERIFY(object != 0);
2293
2294     QVariant v = object->property("test");
2295     QCOMPARE(v.userType(), qMetaTypeId<QDeclarativeListReference>());
2296     QVERIFY(qvariant_cast<QDeclarativeListReference>(v).object() == &container);
2297
2298     delete object;
2299 }
2300
2301 // QTBUG-16316
2302 Q_DECLARE_METATYPE(QDeclarativeListProperty<MyQmlObject>)
2303 void tst_qdeclarativeecmascript::listAssignment()
2304 {
2305     QDeclarativeComponent component(&engine, TEST_FILE("listAssignment.qml"));
2306     QObject *obj = component.create();
2307     QCOMPARE(obj->property("list1length").toInt(), 2);
2308     QDeclarativeListProperty<MyQmlObject> list1 = obj->property("list1").value<QDeclarativeListProperty<MyQmlObject> >();
2309     QDeclarativeListProperty<MyQmlObject> list2 = obj->property("list2").value<QDeclarativeListProperty<MyQmlObject> >();
2310     QCOMPARE(list1.count(&list1), list2.count(&list2));
2311     QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0));
2312     QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1));
2313     delete obj;
2314 }
2315
2316 // QTBUG-7957
2317 void tst_qdeclarativeecmascript::multiEngineObject()
2318 {
2319     MyQmlObject obj;
2320     obj.setStringProperty("Howdy planet");
2321
2322     QDeclarativeEngine e1;
2323     e1.rootContext()->setContextProperty("thing", &obj);
2324     QDeclarativeComponent c1(&e1, TEST_FILE("multiEngineObject.qml"));
2325
2326     QDeclarativeEngine e2;
2327     e2.rootContext()->setContextProperty("thing", &obj);
2328     QDeclarativeComponent c2(&e2, TEST_FILE("multiEngineObject.qml"));
2329
2330     QObject *o1 = c1.create();
2331     QObject *o2 = c2.create();
2332
2333     QCOMPARE(o1->property("test").toString(), QString("Howdy planet"));
2334     QCOMPARE(o2->property("test").toString(), QString("Howdy planet"));
2335
2336     delete o2;
2337     delete o1;
2338 }
2339
2340 // Test that references to QObjects are cleanup when the object is destroyed
2341 void tst_qdeclarativeecmascript::deletedObject()
2342 {
2343     QDeclarativeComponent component(&engine, TEST_FILE("deletedObject.qml"));
2344
2345     QObject *object = component.create();
2346
2347     QCOMPARE(object->property("test1").toBool(), true);
2348     QCOMPARE(object->property("test2").toBool(), true);
2349     QCOMPARE(object->property("test3").toBool(), true);
2350     QCOMPARE(object->property("test4").toBool(), true);
2351
2352     delete object;
2353 }
2354
2355 void tst_qdeclarativeecmascript::attachedPropertyScope()
2356 {
2357     QDeclarativeComponent component(&engine, TEST_FILE("attachedPropertyScope.qml"));
2358
2359     QObject *object = component.create();
2360     QVERIFY(object != 0);
2361
2362     MyQmlAttachedObject *attached = 
2363         qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
2364     QVERIFY(attached != 0);
2365
2366     QCOMPARE(object->property("value2").toInt(), 0);
2367
2368     attached->emitMySignal();
2369
2370     QCOMPARE(object->property("value2").toInt(), 9);
2371
2372     delete object;
2373 }
2374
2375 void tst_qdeclarativeecmascript::scriptConnect()
2376 {
2377     {
2378         QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.1.qml"));
2379
2380         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2381         QVERIFY(object != 0);
2382
2383         QCOMPARE(object->property("test").toBool(), false);
2384         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2385         QCOMPARE(object->property("test").toBool(), true);
2386
2387         delete object;
2388     }
2389
2390     {
2391         QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.2.qml"));
2392
2393         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2394         QVERIFY(object != 0);
2395
2396         QCOMPARE(object->property("test").toBool(), false);
2397         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2398         QCOMPARE(object->property("test").toBool(), true);
2399
2400         delete object;
2401     }
2402
2403     {
2404         QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.3.qml"));
2405
2406         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2407         QVERIFY(object != 0);
2408
2409         QCOMPARE(object->property("test").toBool(), false);
2410         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2411         QCOMPARE(object->property("test").toBool(), true);
2412
2413         delete object;
2414     }
2415
2416     {
2417         QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.4.qml"));
2418
2419         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2420         QVERIFY(object != 0);
2421
2422         QCOMPARE(object->methodCalled(), false);
2423         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2424         QCOMPARE(object->methodCalled(), true);
2425
2426         delete object;
2427     }
2428
2429     {
2430         QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.5.qml"));
2431
2432         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2433         QVERIFY(object != 0);
2434
2435         QCOMPARE(object->methodCalled(), false);
2436         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2437         QCOMPARE(object->methodCalled(), true);
2438
2439         delete object;
2440     }
2441
2442     {
2443         QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.6.qml"));
2444
2445         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2446         QVERIFY(object != 0);
2447
2448         QCOMPARE(object->property("test").toInt(), 0);
2449         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2450         QCOMPARE(object->property("test").toInt(), 2);
2451
2452         delete object;
2453     }
2454 }
2455
2456 void tst_qdeclarativeecmascript::scriptDisconnect()
2457 {
2458     {
2459         QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.1.qml"));
2460
2461         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2462         QVERIFY(object != 0);
2463
2464         QCOMPARE(object->property("test").toInt(), 0);
2465         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2466         QCOMPARE(object->property("test").toInt(), 1);
2467         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2468         QCOMPARE(object->property("test").toInt(), 2);
2469         emit object->basicSignal();
2470         QCOMPARE(object->property("test").toInt(), 2);
2471         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2472         QCOMPARE(object->property("test").toInt(), 2);
2473
2474         delete object;
2475     }
2476
2477     {
2478         QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.2.qml"));
2479
2480         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2481         QVERIFY(object != 0);
2482
2483         QCOMPARE(object->property("test").toInt(), 0);
2484         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2485         QCOMPARE(object->property("test").toInt(), 1);
2486         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2487         QCOMPARE(object->property("test").toInt(), 2);
2488         emit object->basicSignal();
2489         QCOMPARE(object->property("test").toInt(), 2);
2490         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2491         QCOMPARE(object->property("test").toInt(), 2);
2492
2493         delete object;
2494     }
2495
2496     {
2497         QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.3.qml"));
2498
2499         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2500         QVERIFY(object != 0);
2501
2502         QCOMPARE(object->property("test").toInt(), 0);
2503         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2504         QCOMPARE(object->property("test").toInt(), 1);
2505         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2506         QCOMPARE(object->property("test").toInt(), 2);
2507         emit object->basicSignal();
2508         QCOMPARE(object->property("test").toInt(), 2);
2509         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2510         QCOMPARE(object->property("test").toInt(), 3);
2511
2512         delete object;
2513     }
2514     {
2515         QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.4.qml"));
2516
2517         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2518         QVERIFY(object != 0);
2519
2520         QCOMPARE(object->property("test").toInt(), 0);
2521         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2522         QCOMPARE(object->property("test").toInt(), 1);
2523         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2524         QCOMPARE(object->property("test").toInt(), 2);
2525         emit object->basicSignal();
2526         QCOMPARE(object->property("test").toInt(), 2);
2527         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2528         QCOMPARE(object->property("test").toInt(), 3);
2529
2530         delete object;
2531     }
2532 }
2533
2534 class OwnershipObject : public QObject
2535 {
2536     Q_OBJECT
2537 public:
2538     OwnershipObject() { object = new QObject; }
2539
2540     QPointer<QObject> object;
2541
2542 public slots:
2543     QObject *getObject() { return object; }
2544 };
2545
2546 void tst_qdeclarativeecmascript::ownership()
2547 {
2548     OwnershipObject own;
2549     QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
2550     context->setContextObject(&own);
2551
2552     {
2553         QDeclarativeComponent component(&engine, TEST_FILE("ownership.qml"));
2554
2555         QVERIFY(own.object != 0);
2556
2557         QObject *object = component.create(context);
2558
2559         engine.collectGarbage();
2560
2561         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
2562
2563         QVERIFY(own.object == 0);
2564
2565         delete object;
2566     }
2567
2568     own.object = new QObject(&own);
2569
2570     {
2571         QDeclarativeComponent component(&engine, TEST_FILE("ownership.qml"));
2572
2573         QVERIFY(own.object != 0);
2574
2575         QObject *object = component.create(context);
2576         
2577         engine.collectGarbage();
2578
2579         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
2580
2581         QVERIFY(own.object != 0);
2582
2583         delete object;
2584     }
2585
2586     delete context;
2587 }
2588
2589 class CppOwnershipReturnValue : public QObject
2590 {
2591     Q_OBJECT
2592 public:
2593     CppOwnershipReturnValue() : value(0) {}
2594     ~CppOwnershipReturnValue() { delete value; }
2595
2596     Q_INVOKABLE QObject *create() {
2597         value = new QObject;
2598         QDeclarativeEngine::setObjectOwnership(value, QDeclarativeEngine::CppOwnership);
2599         return value;
2600     }
2601
2602     Q_INVOKABLE MyQmlObject *createQmlObject() {
2603         MyQmlObject *rv = new MyQmlObject;
2604         value = rv;
2605         return rv;
2606     }
2607
2608     QPointer<QObject> value;
2609 };
2610
2611 // QTBUG-15695.  
2612 // Test setObjectOwnership(CppOwnership) works even when there is no QDeclarativeData
2613 void tst_qdeclarativeecmascript::cppOwnershipReturnValue()
2614 {
2615     CppOwnershipReturnValue source;
2616
2617     {
2618     QDeclarativeEngine engine;
2619     engine.rootContext()->setContextProperty("source", &source);
2620
2621     QVERIFY(source.value == 0);
2622
2623     QDeclarativeComponent component(&engine);
2624     component.setData("import QtQuick 1.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl());
2625
2626     QObject *object = component.create();
2627
2628     QVERIFY(object != 0);
2629     QVERIFY(source.value != 0);
2630
2631     delete object;
2632     }
2633
2634     QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion);
2635
2636     QVERIFY(source.value != 0);
2637 }
2638
2639 // QTBUG-15697
2640 void tst_qdeclarativeecmascript::ownershipCustomReturnValue()
2641 {
2642     CppOwnershipReturnValue source;
2643
2644     {
2645     QDeclarativeEngine engine;
2646     engine.rootContext()->setContextProperty("source", &source);
2647
2648     QVERIFY(source.value == 0);
2649
2650     QDeclarativeComponent component(&engine);
2651     component.setData("import QtQuick 1.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl());
2652
2653     QObject *object = component.create();
2654
2655     QVERIFY(object != 0);
2656     QVERIFY(source.value != 0);
2657
2658     delete object;
2659     }
2660
2661     engine.collectGarbage();
2662     QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion);
2663
2664     QVERIFY(source.value == 0);
2665 }
2666
2667 class QListQObjectMethodsObject : public QObject
2668 {
2669     Q_OBJECT
2670 public:
2671     QListQObjectMethodsObject() {
2672         m_objects.append(new MyQmlObject());
2673         m_objects.append(new MyQmlObject());
2674     }
2675
2676     ~QListQObjectMethodsObject() {
2677         qDeleteAll(m_objects);
2678     }
2679
2680 public slots:
2681     QList<QObject *> getObjects() { return m_objects; }
2682
2683 private:
2684     QList<QObject *> m_objects;
2685 };
2686
2687 // Tests that returning a QList<QObject*> from a method works
2688 void tst_qdeclarativeecmascript::qlistqobjectMethods()
2689 {
2690     QListQObjectMethodsObject obj;
2691     QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
2692     context->setContextObject(&obj);
2693
2694     QDeclarativeComponent component(&engine, TEST_FILE("qlistqobjectMethods.qml"));
2695
2696     QObject *object = component.create(context);
2697
2698     QCOMPARE(object->property("test").toInt(), 2);
2699     QCOMPARE(object->property("test2").toBool(), true);
2700
2701     delete object;
2702     delete context;
2703 }
2704
2705 // QTBUG-9205
2706 void tst_qdeclarativeecmascript::strictlyEquals()
2707 {
2708     QDeclarativeComponent component(&engine, TEST_FILE("strictlyEquals.qml"));
2709
2710     QObject *object = component.create();
2711     QVERIFY(object != 0);
2712
2713     QCOMPARE(object->property("test1").toBool(), true);
2714     QCOMPARE(object->property("test2").toBool(), true);
2715     QCOMPARE(object->property("test3").toBool(), true);
2716     QCOMPARE(object->property("test4").toBool(), true);
2717     QCOMPARE(object->property("test5").toBool(), true);
2718     QCOMPARE(object->property("test6").toBool(), true);
2719     QCOMPARE(object->property("test7").toBool(), true);
2720     QCOMPARE(object->property("test8").toBool(), true);
2721
2722     delete object;
2723 }
2724
2725 void tst_qdeclarativeecmascript::compiled()
2726 {
2727     QDeclarativeComponent component(&engine, TEST_FILE("compiled.qml"));
2728
2729     QObject *object = component.create();
2730     QVERIFY(object != 0);
2731
2732     QCOMPARE(object->property("test1").toReal(), qreal(15.7));
2733     QCOMPARE(object->property("test2").toReal(), qreal(-6.7));
2734     QCOMPARE(object->property("test3").toBool(), true);
2735     QCOMPARE(object->property("test4").toBool(), false);
2736     QCOMPARE(object->property("test5").toBool(), false);
2737     QCOMPARE(object->property("test6").toBool(), true);
2738
2739     QCOMPARE(object->property("test7").toInt(), 185);
2740     QCOMPARE(object->property("test8").toInt(), 167);
2741     QCOMPARE(object->property("test9").toBool(), true);
2742     QCOMPARE(object->property("test10").toBool(), false);
2743     QCOMPARE(object->property("test11").toBool(), false);
2744     QCOMPARE(object->property("test12").toBool(), true);
2745
2746     QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld"));
2747     QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World"));
2748     QCOMPARE(object->property("test15").toBool(), false);
2749     QCOMPARE(object->property("test16").toBool(), true);
2750
2751     QCOMPARE(object->property("test17").toInt(), 5);
2752     QCOMPARE(object->property("test18").toReal(), qreal(176));
2753     QCOMPARE(object->property("test19").toInt(), 7);
2754     QCOMPARE(object->property("test20").toReal(), qreal(6.7));
2755     QCOMPARE(object->property("test21").toString(), QLatin1String("6.7"));
2756     QCOMPARE(object->property("test22").toString(), QLatin1String("!"));
2757     QCOMPARE(object->property("test23").toBool(), true);
2758     QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33));
2759     QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA));
2760
2761     delete object;
2762 }
2763
2764 // Test that numbers assigned in bindings as strings work consistently
2765 void tst_qdeclarativeecmascript::numberAssignment()
2766 {
2767     QDeclarativeComponent component(&engine, TEST_FILE("numberAssignment.qml"));
2768
2769     QObject *object = component.create();
2770     QVERIFY(object != 0);
2771
2772     QCOMPARE(object->property("test1"), QVariant((qreal)6.7));
2773     QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
2774     QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
2775     QCOMPARE(object->property("test3"), QVariant((qreal)6));
2776     QCOMPARE(object->property("test4"), QVariant((qreal)6));
2777
2778     QCOMPARE(object->property("test5"), QVariant((int)7));
2779     QCOMPARE(object->property("test6"), QVariant((int)7));
2780     QCOMPARE(object->property("test7"), QVariant((int)6));
2781     QCOMPARE(object->property("test8"), QVariant((int)6));
2782
2783     QCOMPARE(object->property("test9"), QVariant((unsigned int)7));
2784     QCOMPARE(object->property("test10"), QVariant((unsigned int)7));
2785     QCOMPARE(object->property("test11"), QVariant((unsigned int)6));
2786     QCOMPARE(object->property("test12"), QVariant((unsigned int)6));
2787
2788     delete object;
2789 }
2790
2791 void tst_qdeclarativeecmascript::propertySplicing()
2792 {
2793     QDeclarativeComponent component(&engine, TEST_FILE("propertySplicing.qml"));
2794
2795     QObject *object = component.create();
2796     QVERIFY(object != 0);
2797
2798     QCOMPARE(object->property("test").toBool(), true);
2799
2800     delete object;
2801 }
2802
2803 // QTBUG-16683
2804 void tst_qdeclarativeecmascript::signalWithUnknownTypes()
2805 {
2806     QDeclarativeComponent component(&engine, TEST_FILE("signalWithUnknownTypes.qml"));
2807
2808     MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2809     QVERIFY(object != 0);
2810
2811     MyQmlObject::MyType type;
2812     type.value = 0x8971123;
2813     emit object->signalWithUnknownType(type);
2814
2815     MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant());
2816
2817     QCOMPARE(result.value, type.value);
2818
2819
2820     delete object;
2821 }
2822
2823 void tst_qdeclarativeecmascript::signalWithJSValueInVariant_data()
2824 {
2825     QTest::addColumn<QString>("expression");
2826     QTest::addColumn<QString>("compare");
2827
2828     QString compareStrict("(function(a, b) { return a === b; })");
2829     QTest::newRow("true") << "true" << compareStrict;
2830     QTest::newRow("undefined") << "undefined" << compareStrict;
2831     QTest::newRow("null") << "null" << compareStrict;
2832     QTest::newRow("123") << "123" << compareStrict;
2833     QTest::newRow("'ciao'") << "'ciao'" << compareStrict;
2834
2835     QString comparePropertiesStrict(
2836         "(function(a, b) {"
2837         "  if (typeof b != 'object')"
2838         "    return a === b;"
2839         "  var props = Object.getOwnPropertyNames(b);"
2840         "  for (var i = 0; i < props.length; ++i) {"
2841         "    var p = props[i];"
2842         "    return arguments.callee(a[p], b[p]);"
2843         "  }"
2844         "})");
2845     QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })"  << comparePropertiesStrict;
2846     QTest::newRow("[10,20,30]") << "[10,20,30]"  << comparePropertiesStrict;
2847 }
2848
2849 void tst_qdeclarativeecmascript::signalWithJSValueInVariant()
2850 {
2851     QFETCH(QString, expression);
2852     QFETCH(QString, compare);
2853
2854     QDeclarativeComponent component(&engine, TEST_FILE("signalWithJSValueInVariant.qml"));
2855     QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
2856     QVERIFY(object != 0);
2857
2858     QJSValue value = engine.evaluate(expression);
2859     QVERIFY(!engine.hasUncaughtException());
2860     object->setProperty("expression", expression);
2861     object->setProperty("compare", compare);
2862     object->setProperty("pass", false);
2863
2864     emit object->signalWithVariant(QVariant::fromValue(value));
2865     QVERIFY(object->property("pass").toBool());
2866 }
2867
2868 void tst_qdeclarativeecmascript::signalWithJSValueInVariant_twoEngines_data()
2869 {
2870     signalWithJSValueInVariant_data();
2871 }
2872
2873 void tst_qdeclarativeecmascript::signalWithJSValueInVariant_twoEngines()
2874 {
2875     QFETCH(QString, expression);
2876     QFETCH(QString, compare);
2877
2878     QDeclarativeComponent component(&engine, TEST_FILE("signalWithJSValueInVariant.qml"));
2879     QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
2880     QVERIFY(object != 0);
2881
2882     QJSEngine engine2;
2883     QJSValue value = engine2.evaluate(expression);
2884     QVERIFY(!engine2.hasUncaughtException());
2885     object->setProperty("expression", expression);
2886     object->setProperty("compare", compare);
2887     object->setProperty("pass", false);
2888
2889     QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine.");
2890     emit object->signalWithVariant(QVariant::fromValue(value));
2891     QVERIFY(!object->property("pass").toBool());
2892 }
2893
2894 void tst_qdeclarativeecmascript::moduleApi_data()
2895 {
2896     QTest::addColumn<QUrl>("testfile");
2897     QTest::addColumn<QString>("errorMessage");
2898     QTest::addColumn<QStringList>("warningMessages");
2899     QTest::addColumn<QStringList>("readProperties");
2900     QTest::addColumn<QVariantList>("readExpectedValues");
2901     QTest::addColumn<QStringList>("writeProperties");
2902     QTest::addColumn<QVariantList>("writeValues");
2903     QTest::addColumn<QStringList>("readBackProperties");
2904     QTest::addColumn<QVariantList>("readBackExpectedValues");
2905
2906     QTest::newRow("qobject, register + read + method")
2907             << TEST_FILE("moduleapi/qobjectModuleApi.qml")
2908             << QString()
2909             << QStringList()
2910             << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
2911                    << "qobjectMinorVersionTest" << "qobjectMajorVersionTest" << "qobjectParentedTest")
2912             << (QVariantList() << 20 << 20 << 1 << 20 << 20 << 26)
2913             << QStringList()
2914             << QVariantList()
2915             << QStringList()
2916             << QVariantList();
2917
2918     QTest::newRow("script, register + read")
2919             << TEST_FILE("moduleapi/scriptModuleApi.qml")
2920             << QString()
2921             << QStringList()
2922             << (QStringList() << "scriptTest")
2923             << (QVariantList() << 13)
2924             << QStringList()
2925             << QVariantList()
2926             << QStringList()
2927             << QVariantList();
2928
2929     QTest::newRow("qobject, caching + read")
2930             << TEST_FILE("moduleapi/qobjectModuleApiCaching.qml")
2931             << QString()
2932             << QStringList()
2933             << (QStringList() << "existingUriTest" << "qobjectParentedTest")
2934             << (QVariantList() << 20 << 26) // 26, shouldn't have incremented to 27.
2935             << QStringList()
2936             << QVariantList()
2937             << QStringList()
2938             << QVariantList();
2939
2940     QTest::newRow("script, caching + read")
2941             << TEST_FILE("moduleapi/scriptModuleApiCaching.qml")
2942             << QString()
2943             << QStringList()
2944             << (QStringList() << "scriptTest")
2945             << (QVariantList() << 13) // 13, shouldn't have incremented to 14.
2946             << QStringList()
2947             << QVariantList()
2948             << QStringList()
2949             << QVariantList();
2950
2951     QTest::newRow("qobject, writing + readonly constraints")
2952             << TEST_FILE("moduleapi/qobjectModuleApiWriting.qml")
2953             << QString()
2954             << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("moduleapi/qobjectModuleApiWriting.qml").toLocalFile() + QLatin1String(":14: Error: Cannot assign to read-only property \"qobjectTestProperty\"")))
2955             << (QStringList() << "readOnlyProperty" << "writableProperty")
2956             << (QVariantList() << 20 << 50)
2957             << (QStringList() << "firstProperty" << "writableProperty")
2958             << (QVariantList() << 30 << 30)
2959             << (QStringList() << "readOnlyProperty" << "writableProperty")
2960             << (QVariantList() << 20 << 30);
2961
2962     QTest::newRow("script, writing + readonly constraints")
2963             << TEST_FILE("moduleapi/scriptModuleApiWriting.qml")
2964             << QString()
2965             << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("moduleapi/scriptModuleApiWriting.qml").toLocalFile() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\"")))
2966             << (QStringList() << "readBack" << "unchanged")
2967             << (QVariantList() << 13 << 42)
2968             << (QStringList() << "firstProperty" << "secondProperty")
2969             << (QVariantList() << 30 << 30)
2970             << (QStringList() << "readBack" << "unchanged")
2971             << (QVariantList() << 30 << 42);
2972
2973     QTest::newRow("qobject module API enum values in JS")
2974             << TEST_FILE("moduleapi/qobjectModuleApiEnums.qml")
2975             << QString()
2976             << QStringList()
2977             << (QStringList() << "enumValue" << "enumMethod")
2978             << (QVariantList() << 42 << 30)
2979             << QStringList()
2980             << QVariantList()
2981             << QStringList()
2982             << QVariantList();
2983
2984     QTest::newRow("qobject, invalid major version fail")
2985             << TEST_FILE("moduleapi/moduleApiMajorVersionFail.qml")
2986             << QString("QDeclarativeComponent: Component is not ready")
2987             << QStringList()
2988             << QStringList()
2989             << QVariantList()
2990             << QStringList()
2991             << QVariantList()
2992             << QStringList()
2993             << QVariantList();
2994
2995     QTest::newRow("qobject, invalid minor version fail")
2996             << TEST_FILE("moduleapi/moduleApiMinorVersionFail.qml")
2997             << QString("QDeclarativeComponent: Component is not ready")
2998             << QStringList()
2999             << QStringList()
3000             << QVariantList()
3001             << QStringList()
3002             << QVariantList()
3003             << QStringList()
3004             << QVariantList();
3005 }
3006
3007 void tst_qdeclarativeecmascript::moduleApi()
3008 {
3009     QFETCH(QUrl, testfile);
3010     QFETCH(QString, errorMessage);
3011     QFETCH(QStringList, warningMessages);
3012     QFETCH(QStringList, readProperties);
3013     QFETCH(QVariantList, readExpectedValues);
3014     QFETCH(QStringList, writeProperties);
3015     QFETCH(QVariantList, writeValues);
3016     QFETCH(QStringList, readBackProperties);
3017     QFETCH(QVariantList, readBackExpectedValues);
3018
3019     QDeclarativeComponent component(&engine, testfile);
3020
3021     if (!errorMessage.isEmpty())
3022         QTest::ignoreMessage(QtWarningMsg, errorMessage.toAscii().constData());
3023
3024     if (warningMessages.size())
3025         foreach (const QString &warning, warningMessages)
3026             QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
3027
3028     QObject *object = component.create();
3029     if (!errorMessage.isEmpty()) {
3030         QVERIFY(object == 0);
3031     } else {
3032         QVERIFY(object != 0);
3033         for (int i = 0; i < readProperties.size(); ++i)
3034             QCOMPARE(object->property(readProperties.at(i).toAscii().constData()), readExpectedValues.at(i));
3035         for (int i = 0; i < writeProperties.size(); ++i)
3036             QVERIFY(object->setProperty(writeProperties.at(i).toAscii().constData(), writeValues.at(i)));
3037         for (int i = 0; i < readBackProperties.size(); ++i)
3038             QCOMPARE(object->property(readBackProperties.at(i).toAscii().constData()), readBackExpectedValues.at(i));
3039         delete object;
3040     }
3041 }
3042
3043 void tst_qdeclarativeecmascript::importScripts_data()
3044 {
3045     QTest::addColumn<QUrl>("testfile");
3046     QTest::addColumn<QString>("errorMessage");
3047     QTest::addColumn<QStringList>("warningMessages");
3048     QTest::addColumn<QStringList>("propertyNames");
3049     QTest::addColumn<QVariantList>("propertyValues");
3050
3051     QTest::newRow("basic functionality")
3052             << TEST_FILE("jsimport/testImport.qml")
3053             << QString()
3054             << QStringList()
3055             << (QStringList() << QLatin1String("importedScriptStringValue")
3056                               << QLatin1String("importedScriptFunctionValue")
3057                               << QLatin1String("importedModuleAttachedPropertyValue")
3058                               << QLatin1String("importedModuleEnumValue"))
3059             << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3060                                << QVariant(20)
3061                                << QVariant(19)
3062                                << QVariant(2));
3063
3064     QTest::newRow("import scoping")
3065             << TEST_FILE("jsimport/testImportScoping.qml")
3066             << QString()
3067             << QStringList()
3068             << (QStringList() << QLatin1String("componentError"))
3069             << (QVariantList() << QVariant(5));
3070
3071     QTest::newRow("parent scope shouldn't be inherited by import with imports")
3072             << TEST_FILE("jsimportfail/failOne.qml")
3073             << QString()
3074             << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/failOne.qml").toLocalFile() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
3075             << (QStringList() << QLatin1String("importScriptFunctionValue"))
3076             << (QVariantList() << QVariant(QString()));
3077
3078     QTest::newRow("javascript imports in an import should be private to the import scope")
3079             << TEST_FILE("jsimportfail/failTwo.qml")
3080             << QString()
3081             << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/failTwo.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: ImportOneJs")))
3082             << (QStringList() << QLatin1String("importScriptFunctionValue"))
3083             << (QVariantList() << QVariant(QString()));
3084
3085     QTest::newRow("module imports in an import should be private to the import scope")
3086             << TEST_FILE("jsimportfail/failThree.qml")
3087             << QString()
3088             << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/failThree.qml").toLocalFile() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
3089             << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
3090             << (QVariantList() << QVariant(false));
3091
3092     QTest::newRow("typenames in an import should be private to the import scope")
3093             << TEST_FILE("jsimportfail/failFour.qml")
3094             << QString()
3095             << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/failFour.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: JsQtTest")))
3096             << (QStringList() << QLatin1String("importedModuleEnumValue"))
3097             << (QVariantList() << QVariant(0));
3098
3099     QTest::newRow("import with imports has it's own activation scope")
3100             << TEST_FILE("jsimportfail/failFive.qml")
3101             << QString()
3102             << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/importWithImports.js").toLocalFile() + QLatin1String(":8: ReferenceError: Can't find variable: Component"))
3103                               << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/importPragmaLibrary.js").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: Component")))
3104             << (QStringList() << QLatin1String("componentError"))
3105             << (QVariantList() << QVariant(0));
3106
3107     QTest::newRow("import pragma library script")
3108             << TEST_FILE("jsimport/testImportPragmaLibrary.qml")
3109             << QString()
3110             << QStringList()
3111             << (QStringList() << QLatin1String("testValue"))
3112             << (QVariantList() << QVariant(31));
3113
3114     QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3115             << TEST_FILE("jsimportfail/testImportPragmaLibrary.qml")
3116             << QString()
3117             << QStringList()
3118             << (QStringList() << QLatin1String("testValue"))
3119             << (QVariantList() << QVariant(0));
3120
3121     QTest::newRow("import pragma library script which has an import")
3122             << TEST_FILE("jsimport/testImportPragmaLibraryWithImports.qml")
3123             << QString()
3124             << QStringList()
3125             << (QStringList() << QLatin1String("testValue"))
3126             << (QVariantList() << QVariant(55));
3127
3128     QTest::newRow("import pragma library script which has a pragma library import")
3129             << TEST_FILE("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3130             << QString()
3131             << QStringList()
3132             << (QStringList() << QLatin1String("testValue"))
3133             << (QVariantList() << QVariant(18));
3134 }
3135
3136 void tst_qdeclarativeecmascript::importScripts()
3137 {
3138     QFETCH(QUrl, testfile);
3139     QFETCH(QString, errorMessage);
3140     QFETCH(QStringList, warningMessages);
3141     QFETCH(QStringList, propertyNames);
3142     QFETCH(QVariantList, propertyValues);
3143
3144     QDeclarativeComponent component(&engine, testfile);
3145
3146     if (!errorMessage.isEmpty())
3147         QTest::ignoreMessage(QtWarningMsg, errorMessage.toAscii().constData());
3148
3149     if (warningMessages.size())
3150         foreach (const QString &warning, warningMessages)
3151             QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
3152
3153     QObject *object = component.create();
3154     if (!errorMessage.isEmpty()) {
3155         QVERIFY(object == 0);
3156     } else {
3157         QVERIFY(object != 0);
3158         for (int i = 0; i < propertyNames.size(); ++i)
3159             QCOMPARE(object->property(propertyNames.at(i).toAscii().constData()), propertyValues.at(i));
3160         delete object;
3161     }
3162 }
3163
3164 void tst_qdeclarativeecmascript::scarceResources()
3165 {
3166     QPixmap origPixmap(100, 100);
3167     origPixmap.fill(Qt::blue);
3168
3169     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&engine);
3170     ScarceResourceObject *eo = 0;
3171     QObject *object = 0;
3172
3173     // in the following three cases, the instance created from the component
3174     // has a property which is a copy of the scarce resource; hence, the
3175     // resource should NOT be detached prior to deletion of the object instance,
3176     // unless the resource is destroyed explicitly.
3177     QDeclarativeComponent component(&engine, TEST_FILE("scarceresources/scarceResourceCopy.qml"));
3178     object = component.create();
3179     QVERIFY(object != 0);
3180     QVERIFY(object->property("scarceResourceCopy").isValid());
3181     QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3182     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3183     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3184     QVERIFY(!eo->scarceResourceIsDetached()); // there are two copies of it in existence: the property of object, and the property of eo.
3185     delete object;
3186
3187     QDeclarativeComponent componentTwo(&engine, TEST_FILE("scarceresources/scarceResourceCopyFromJs.qml"));
3188     object = componentTwo.create();
3189     QVERIFY(object != 0);
3190     QVERIFY(object->property("scarceResourceCopy").isValid());
3191     QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3192     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3193     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3194     QVERIFY(!eo->scarceResourceIsDetached()); // there are two copies of it in existence: the property of object, and the property of eo.
3195     delete object;
3196
3197     QDeclarativeComponent componentThree(&engine, TEST_FILE("scarceresources/scarceResourceDestroyedCopy.qml"));
3198     object = componentThree.create();
3199     QVERIFY(object != 0);
3200     QVERIFY(!(object->property("scarceResourceCopy").isValid())); // was manually released prior to being returned.
3201     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3202     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3203     QVERIFY(eo->scarceResourceIsDetached()); // should have explicitly been released during the evaluation of the binding.
3204     delete object;
3205
3206     // in the following three cases, no other copy should exist in memory,
3207     // and so it should be detached (unless explicitly preserved).
3208     QDeclarativeComponent componentFour(&engine, TEST_FILE("scarceresources/scarceResourceTest.qml"));
3209     object = componentFour.create();
3210     QVERIFY(object != 0);
3211     QVERIFY(object->property("scarceResourceTest").isValid());
3212     QCOMPARE(object->property("scarceResourceTest").toInt(), 100);
3213     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3214     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3215     QVERIFY(eo->scarceResourceIsDetached()); // the resource should have been released after the binding was evaluated.
3216     delete object;
3217
3218     QDeclarativeComponent componentFive(&engine, TEST_FILE("scarceresources/scarceResourceTestPreserve.qml"));
3219     object = componentFive.create();
3220     QVERIFY(object != 0);
3221     QVERIFY(object->property("scarceResourceTest").isValid());
3222     QCOMPARE(object->property("scarceResourceTest").toInt(), 100);
3223     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3224     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3225     QVERIFY(!eo->scarceResourceIsDetached()); // this won't be detached since we explicitly preserved it.
3226     delete object;
3227
3228     QDeclarativeComponent componentSix(&engine, TEST_FILE("scarceresources/scarceResourceTestMultiple.qml"));
3229     object = componentSix.create();
3230     QVERIFY(object != 0);
3231     QVERIFY(object->property("scarceResourceTest").isValid());
3232     QCOMPARE(object->property("scarceResourceTest").toInt(), 100);
3233     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3234     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3235     QVERIFY(eo->scarceResourceIsDetached()); // all resources were released manually or automatically released.
3236     delete object;
3237
3238     // test that scarce resources are handled correctly for imports
3239     QDeclarativeComponent componentSeven(&engine, TEST_FILE("scarceresources/scarceResourceCopyImportNoBinding.qml"));
3240     object = componentSeven.create();
3241     QVERIFY(object != 0); // the import should have caused the addition of a resource to the ScarceResources list
3242     QVERIFY(ep->scarceResources.isEmpty()); // but they should have been released by this point.
3243     delete object;
3244
3245     QDeclarativeComponent componentEight(&engine, TEST_FILE("scarceresources/scarceResourceCopyImportFail.qml"));
3246     object = componentEight.create();
3247     QVERIFY(object != 0);
3248     QVERIFY(!object->property("scarceResourceCopy").isValid()); // wasn't preserved, so shouldn't be valid.
3249     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3250     delete object;
3251
3252     QDeclarativeComponent componentNine(&engine, TEST_FILE("scarceresources/scarceResourceCopyImport.qml"));
3253     object = componentNine.create();
3254     QVERIFY(object != 0);
3255     QVERIFY(object->property("scarceResourceCopy").isValid()); // preserved, so should be valid.
3256     QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3257     QVERIFY(object->property("scarceResourceAssignedCopyOne").isValid()); // assigned before destroy(), so should be valid.
3258     QCOMPARE(object->property("scarceResourceAssignedCopyOne").value<QPixmap>(), origPixmap);
3259     QVERIFY(!object->property("scarceResourceAssignedCopyTwo").isValid()); // assigned after destroy(), so should be invalid.
3260     QVERIFY(ep->scarceResources.isEmpty()); // this will still be zero, because "preserve()" REMOVES it from this list.
3261     delete object;
3262
3263     // test that scarce resources are handled properly in signal invocation
3264     QDeclarativeComponent componentTen(&engine, TEST_FILE("scarceresources/scarceResourceSignal.qml"));
3265     object = componentTen.create();
3266     QVERIFY(object != 0);
3267     QObject *srsc = object->findChild<QObject*>("srsc");
3268     QVERIFY(srsc);
3269     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3270     QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3271     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3272     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3273     QMetaObject::invokeMethod(srsc, "testSignal");
3274     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3275     QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3276     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3277     QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3278     QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3279     QVERIFY(srsc->property("scarceResourceCopy").isValid());
3280     QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3281     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3282     QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3283     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3284     delete object;
3285
3286     // test that scarce resources are handled properly from js functions in qml files
3287     QDeclarativeComponent componentEleven(&engine, TEST_FILE("scarceresources/scarceResourceFunction.qml"));
3288     object = componentEleven.create();
3289     QVERIFY(object != 0);
3290     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3291     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3292     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3293     QMetaObject::invokeMethod(object, "retrieveScarceResource");
3294     QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3295     QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3296     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3297     QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3298     QMetaObject::invokeMethod(object, "releaseScarceResource");
3299     QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3300     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3301     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3302     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3303     delete object;
3304
3305     // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3306     QDeclarativeComponent componentTwelve(&engine, TEST_FILE("scarceresources/scarceResourceFunctionFail.qml"));
3307     object = componentTwelve.create();
3308     QVERIFY(object != 0);
3309     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3310     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3311     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3312     QString srp_name = object->property("srp_name").toString();
3313     QString expectedWarning = componentTwelve.url().toString() + QLatin1String(":17: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3314     QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3315     QMetaObject::invokeMethod(object, "retrieveScarceResource");
3316     QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3317     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3318     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3319     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3320     delete object;
3321 }
3322
3323 void tst_qdeclarativeecmascript::propertyChangeSlots()
3324 {
3325     // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
3326     QDeclarativeComponent component(&engine, TEST_FILE("changeslots/propertyChangeSlots.qml"));
3327     QObject *object = component.create();
3328     QVERIFY(object != 0);
3329     delete object;
3330
3331     // ensure that invalid property names fail properly.
3332     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
3333     QDeclarativeComponent e1(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.1.qml"));
3334     QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
3335     QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
3336     object = e1.create();
3337     QVERIFY(object == 0);
3338     delete object;
3339
3340     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
3341     QDeclarativeComponent e2(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.2.qml"));
3342     expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
3343     QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
3344     object = e2.create();
3345     QVERIFY(object == 0);
3346     delete object;
3347
3348     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
3349     QDeclarativeComponent e3(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.3.qml"));
3350     expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
3351     QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
3352     object = e3.create();
3353     QVERIFY(object == 0);
3354     delete object;
3355
3356     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
3357     QDeclarativeComponent e4(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.4.qml"));
3358     expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
3359     QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
3360     object = e4.create();
3361     QVERIFY(object == 0);
3362     delete object;
3363 }
3364
3365 void tst_qdeclarativeecmascript::propertyVar_data()
3366 {
3367     QTest::addColumn<QUrl>("qmlFile");
3368
3369     // valid
3370     QTest::newRow("non-bindable object subproperty changed") << TEST_FILE("propertyVar.1.qml");
3371     QTest::newRow("non-bindable object changed") << TEST_FILE("propertyVar.2.qml");
3372     QTest::newRow("primitive changed") << TEST_FILE("propertyVar.3.qml");
3373     QTest::newRow("javascript array modification") << TEST_FILE("propertyVar.4.qml");
3374     QTest::newRow("javascript map modification") << TEST_FILE("propertyVar.5.qml");
3375     QTest::newRow("javascript array assignment") << TEST_FILE("propertyVar.6.qml");
3376     QTest::newRow("javascript map assignment") << TEST_FILE("propertyVar.7.qml");
3377     QTest::newRow("literal property assignment") << TEST_FILE("propertyVar.8.qml");
3378     QTest::newRow("qobject property assignment") << TEST_FILE("propertyVar.9.qml");
3379 }
3380
3381 void tst_qdeclarativeecmascript::propertyVar()
3382 {
3383     QFETCH(QUrl, qmlFile);
3384
3385     QDeclarativeComponent component(&engine, qmlFile);
3386     QObject *object = component.create();
3387     QVERIFY(object != 0);
3388
3389     QCOMPARE(object->property("test").toBool(), true);
3390
3391     delete object;
3392 }
3393
3394 // Tests that we can write QVariant values to var properties from C++
3395 void tst_qdeclarativeecmascript::propertyVarCpp()
3396 {
3397     QObject *object = 0;
3398
3399     // ensure that writing to and reading from a var property from cpp works as required.
3400     // Literal values stored in var properties can be read and written as QVariants
3401     // of a specific type, whereas object values are read as QVariantMaps.
3402     QDeclarativeComponent component(&engine, TEST_FILE("propertyVarCpp.qml"));
3403     object = component.create();
3404     QVERIFY(object != 0);
3405     // assign int to property var that currently has int assigned
3406     QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
3407     QCOMPARE(object->property("varBound"), QVariant(15));
3408     QCOMPARE(object->property("intBound"), QVariant(15));
3409     QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
3410     QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
3411     // assign string to property var that current has bool assigned
3412     QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
3413     QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
3414     QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
3415     QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
3416     // now enforce behaviour when accessing JavaScript objects from cpp.
3417     QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
3418     delete object;
3419 }
3420
3421 static void gc(QDeclarativeEngine &engine)
3422 {
3423     engine.collectGarbage();
3424     QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3425 }
3426
3427 void tst_qdeclarativeecmascript::propertyVarOwnership()
3428 {
3429     // Referenced JS objects are not collected
3430     {
3431     QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.qml"));
3432     QObject *object = component.create();
3433     QVERIFY(object != 0);
3434     QCOMPARE(object->property("test").toBool(), false);
3435     QMetaObject::invokeMethod(object, "runTest");
3436     QCOMPARE(object->property("test").toBool(), true);
3437     delete object;
3438     }
3439     // Referenced JS objects are not collected
3440     {
3441     QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.2.qml"));
3442     QObject *object = component.create();
3443     QVERIFY(object != 0);
3444     QCOMPARE(object->property("test").toBool(), false);
3445     QMetaObject::invokeMethod(object, "runTest");
3446     QCOMPARE(object->property("test").toBool(), true);
3447     delete object;
3448     }
3449     // Qt objects are not collected until they've been dereferenced
3450     {
3451     QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.3.qml"));
3452     QObject *object = component.create();
3453     QVERIFY(object != 0);
3454
3455     QCOMPARE(object->property("test2").toBool(), false);
3456     QCOMPARE(object->property("test2").toBool(), false);
3457
3458     QMetaObject::invokeMethod(object, "runTest");
3459     QCOMPARE(object->property("test1").toBool(), true);
3460
3461     QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
3462     QVERIFY(!referencedObject.isNull());
3463     gc(engine);
3464     QVERIFY(!referencedObject.isNull());
3465
3466     QMetaObject::invokeMethod(object, "runTest2");
3467     QCOMPARE(object->property("test2").toBool(), true);
3468     gc(engine);
3469     QVERIFY(referencedObject.isNull());
3470
3471     delete object;
3472     }
3473     // Self reference does not prevent Qt object collection
3474     {
3475     QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.4.qml"));
3476     QObject *object = component.create();
3477     QVERIFY(object != 0);
3478
3479     QCOMPARE(object->property("test").toBool(), true);
3480
3481     QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
3482     QVERIFY(!referencedObject.isNull());
3483     gc(engine);
3484     QVERIFY(!referencedObject.isNull());
3485
3486     QMetaObject::invokeMethod(object, "runTest");
3487     gc(engine);
3488     QVERIFY(referencedObject.isNull());
3489
3490     delete object;
3491     }
3492 }
3493
3494 void tst_qdeclarativeecmascript::propertyVarImplicitOwnership()
3495 {
3496     // The childObject has a reference to a different QObject.  We want to ensure
3497     // that the different item will not be cleaned up until required.  IE, the childObject
3498     // has implicit ownership of the constructed QObject.
3499     QDeclarativeComponent component(&engine, TEST_FILE("propertyVarImplicitOwnership.qml"));
3500     QObject *object = component.create();
3501     QVERIFY(object != 0);
3502     QMetaObject::invokeMethod(object, "assignCircular");
3503     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3504     QObject *rootObject = object->property("vp").value<QObject*>();
3505     QVERIFY(rootObject != 0);
3506     QObject *childObject = rootObject->findChild<QObject*>("text");
3507     QVERIFY(childObject != 0);
3508     QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
3509     QCOMPARE(childObject->property("textCanary").toInt(), 10);
3510     QMetaObject::invokeMethod(childObject, "constructQObject");    // creates a reference to a constructed QObject.
3511     QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
3512     QVERIFY(!qobjectGuard.isNull());
3513     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3514     QVERIFY(!qobjectGuard.isNull());
3515     QMetaObject::invokeMethod(object, "deassignCircular");
3516     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3517     QVERIFY(qobjectGuard.isNull());                                // should have been collected now.
3518     delete object;
3519 }
3520
3521 void tst_qdeclarativeecmascript::propertyVarReparent()
3522 {
3523     // ensure that nothing breaks if we re-parent objects
3524     QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.reparent.qml"));
3525     QObject *object = component.create();
3526     QVERIFY(object != 0);
3527     QMetaObject::invokeMethod(object, "assignVarProp");
3528     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3529     QObject *rect = object->property("vp").value<QObject*>();
3530     QObject *text = rect->findChild<QObject*>("textOne");
3531     QObject *text2 = rect->findChild<QObject*>("textTwo");
3532     QWeakPointer<QObject> rectGuard(rect);
3533     QWeakPointer<QObject> textGuard(text);
3534     QWeakPointer<QObject> text2Guard(text2);
3535     QVERIFY(!rectGuard.isNull());
3536     QVERIFY(!textGuard.isNull());
3537     QVERIFY(!text2Guard.isNull());
3538     QCOMPARE(text->property("textCanary").toInt(), 11);
3539     QCOMPARE(text2->property("textCanary").toInt(), 12);
3540     // now construct an image which we will reparent.
3541     QMetaObject::invokeMethod(text2, "constructQObject");
3542     QObject *image = text2->property("vp").value<QObject*>();
3543     QWeakPointer<QObject> imageGuard(image);
3544     QVERIFY(!imageGuard.isNull());
3545     QCOMPARE(image->property("imageCanary").toInt(), 13);
3546     // now reparent the "Image" object (currently, it has JS ownership)
3547     image->setParent(text);                                        // shouldn't be collected after deassignVp now, since has a parent.
3548     QMetaObject::invokeMethod(text2, "deassignVp");
3549     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3550     QCOMPARE(text->property("textCanary").toInt(), 11);
3551     QCOMPARE(text2->property("textCanary").toInt(), 22);
3552     QVERIFY(!imageGuard.isNull());                                 // should still be alive.
3553     QCOMPARE(image->property("imageCanary").toInt(), 13);          // still able to access var properties
3554     QMetaObject::invokeMethod(object, "deassignVarProp");          // now deassign the root-object's vp, causing gc of rect+text+text2
3555     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3556     QVERIFY(imageGuard.isNull());                                  // should now have been deleted, due to parent being deleted.
3557     delete object;
3558 }
3559
3560 void tst_qdeclarativeecmascript::propertyVarReparentNullContext()
3561 {
3562     // sometimes reparenting can cause problems
3563     // (eg, if the ctxt is collected, varproperties are no longer available)
3564     // this test ensures that no crash occurs in that situation.
3565     QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.reparent.qml"));
3566     QObject *object = component.create();
3567     QVERIFY(object != 0);
3568     QMetaObject::invokeMethod(object, "assignVarProp");
3569     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3570     QObject *rect = object->property("vp").value<QObject*>();
3571     QObject *text = rect->findChild<QObject*>("textOne");
3572     QObject *text2 = rect->findChild<QObject*>("textTwo");
3573     QWeakPointer<QObject> rectGuard(rect);
3574     QWeakPointer<QObject> textGuard(text);
3575     QWeakPointer<QObject> text2Guard(text2);
3576     QVERIFY(!rectGuard.isNull());
3577     QVERIFY(!textGuard.isNull());
3578     QVERIFY(!text2Guard.isNull());
3579     QCOMPARE(text->property("textCanary").toInt(), 11);
3580     QCOMPARE(text2->property("textCanary").toInt(), 12);
3581     // now construct an image which we will reparent.
3582     QMetaObject::invokeMethod(text2, "constructQObject");
3583     QObject *image = text2->property("vp").value<QObject*>();
3584     QWeakPointer<QObject> imageGuard(image);
3585     QVERIFY(!imageGuard.isNull());
3586     QCOMPARE(image->property("imageCanary").toInt(), 13);
3587     // now reparent the "Image" object (currently, it has JS ownership)
3588     image->setParent(object);                                      // reparented to base object.  after deassignVarProp, the ctxt will be invalid.
3589     QMetaObject::invokeMethod(object, "deassignVarProp");          // now deassign the root-object's vp, causing gc of rect+text+text2
3590     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3591     QVERIFY(!imageGuard.isNull());                                 // should still be alive.
3592     QVERIFY(!image->property("imageCanary").isValid());            // but varProperties won't be available (null context).
3593     delete object;
3594     QVERIFY(imageGuard.isNull());                                  // should now be dead.
3595 }
3596
3597 void tst_qdeclarativeecmascript::propertyVarCircular()
3598 {
3599     // enforce behaviour regarding circular references - ensure qdvmemo deletion.
3600     QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.circular.qml"));
3601     QObject *object = component.create();
3602     QVERIFY(object != 0);
3603     QMetaObject::invokeMethod(object, "assignCircular");           // cause assignment and gc
3604     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3605     QCOMPARE(object->property("canaryInt"), QVariant(5));
3606     QVariant canaryResourceVariant = object->property("canaryResource");
3607     QVERIFY(canaryResourceVariant.isValid());
3608     QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
3609     canaryResourceVariant = QVariant();                            // invalidate it to remove one copy of the pixmap from memory.
3610     QMetaObject::invokeMethod(object, "deassignCanaryResource");   // remove one copy of the pixmap from memory
3611     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3612     QVERIFY(!canaryResourcePixmap.isDetached());                   // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
3613     QMetaObject::invokeMethod(object, "deassignCircular");         // cause deassignment and gc
3614     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3615     QCOMPARE(object->property("canaryInt"), QVariant(2));
3616     QCOMPARE(object->property("canaryResource"), QVariant(1));
3617     QVERIFY(canaryResourcePixmap.isDetached());                    // now detached, since orig copy was member of qdvmemo which was deleted.
3618     delete object;
3619 }
3620
3621 void tst_qdeclarativeecmascript::propertyVarCircular2()
3622 {
3623     // track deletion of JS-owned parent item with Cpp-owned child
3624     // where the child has a var property referencing its parent.
3625     QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.circular.2.qml"));
3626     QObject *object = component.create();
3627     QVERIFY(object != 0);
3628     QMetaObject::invokeMethod(object, "assignCircular");
3629     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3630     QObject *rootObject = object->property("vp").value<QObject*>();
3631     QVERIFY(rootObject != 0);
3632     QObject *childObject = rootObject->findChild<QObject*>("text");
3633     QVERIFY(childObject != 0);
3634     QWeakPointer<QObject> rootObjectTracker(rootObject);
3635     QVERIFY(!rootObjectTracker.isNull());
3636     QWeakPointer<QObject> childObjectTracker(childObject);
3637     QVERIFY(!childObjectTracker.isNull());
3638     gc(engine);
3639     QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
3640     QCOMPARE(childObject->property("textCanary").toInt(), 10);
3641     QMetaObject::invokeMethod(object, "deassignCircular");
3642     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3643     QVERIFY(rootObjectTracker.isNull());                           // should have been collected
3644     QVERIFY(childObjectTracker.isNull());                          // should have been collected
3645     delete object;
3646 }
3647
3648 void tst_qdeclarativeecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
3649 {
3650     *(int*)(parameter) += 1;
3651     qPersistentDispose(object);
3652 }
3653
3654 void tst_qdeclarativeecmascript::propertyVarInheritance()
3655 {
3656     int propertyVarWeakRefCallbackCount = 0;
3657
3658     // enforce behaviour regarding element inheritance - ensure handle disposal.
3659     // The particular component under test here has a chain of references.
3660     QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.inherit.qml"));
3661     QObject *object = component.create();
3662     QVERIFY(object != 0);
3663     QMetaObject::invokeMethod(object, "assignCircular");           // cause assignment and gc
3664     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3665     // we want to be able to track when the varProperties array of the last metaobject is disposed
3666     QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
3667     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*>();
3668     QDeclarativeVMEMetaObject *icovmemo = ((QDeclarativeVMEMetaObject *)(ico5->metaObject()));
3669     QDeclarativeVMEMetaObject *ccovmemo = ((QDeclarativeVMEMetaObject *)(cco5->metaObject()));
3670     v8::Persistent<v8::Value> icoCanaryHandle;
3671     v8::Persistent<v8::Value> ccoCanaryHandle;
3672     {
3673         v8::HandleScope hs;
3674         // XXX NOTE: this is very implementation dependent.  QDVMEMO->vmeProperty() is the only
3675         // public function which can return us a handle to something in the varProperties array.
3676         icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(41));
3677         ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(41));
3678         // we make them weak and invoke the gc, but we should not hit the weak-callback yet
3679         // as the varproperties array of each vmemo still references the resource.
3680         icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
3681         ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
3682         gc(engine);
3683         QVERIFY(propertyVarWeakRefCallbackCount == 0);
3684     }
3685     // now we deassign the var prop, which should trigger collection of item subtrees.
3686     QMetaObject::invokeMethod(object, "deassignCircular");         // cause deassignment and gc
3687     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3688     // ensure that there are only weak handles to the underlying varProperties array remaining.
3689     gc(engine);
3690     QCOMPARE(propertyVarWeakRefCallbackCount, 2);                  // should have been called for both, since all refs should be weak.
3691     delete object;
3692     // since there are no parent vmemo's to keep implicit references alive, and the only handles
3693     // to what remains are weak, all varProperties arrays must have been collected.
3694 }
3695
3696 void tst_qdeclarativeecmascript::propertyVarInheritance2()
3697 {
3698     int propertyVarWeakRefCallbackCount = 0;
3699
3700     // The particular component under test here does NOT have a chain of references; the
3701     // only link between rootObject and childObject is that rootObject is the parent of childObject.
3702     QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.circular.2.qml"));
3703     QObject *object = component.create();
3704     QVERIFY(object != 0);
3705     QMetaObject::invokeMethod(object, "assignCircular");
3706     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3707     QObject *rootObject = object->property("vp").value<QObject*>();
3708     QVERIFY(rootObject != 0);
3709     QObject *childObject = rootObject->findChild<QObject*>("text");
3710     QVERIFY(childObject != 0);
3711     QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
3712     QCOMPARE(childObject->property("textCanary").toInt(), 10);
3713     v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
3714     {
3715         v8::HandleScope hs;
3716         propertyVarWeakRefCallbackCount = 0;                           // reset callback count.
3717         childObjectVarArrayValueHandle = qPersistentNew(((QDeclarativeVMEMetaObject *)(childObject->metaObject()))->vmeProperty(58));
3718         childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
3719         gc(engine);
3720         QVERIFY(propertyVarWeakRefCallbackCount == 0);                 // should not have been collected yet.
3721         QCOMPARE(childObject->property("textCanary").toInt(), 10);
3722     }
3723     QMetaObject::invokeMethod(object, "deassignCircular");
3724     QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
3725     QVERIFY(propertyVarWeakRefCallbackCount == 1);                 // should have been collected now.
3726     delete object;
3727 }
3728
3729 // Ensure that QObject type conversion works on binding assignment
3730 void tst_qdeclarativeecmascript::elementAssign()
3731 {
3732     QDeclarativeComponent component(&engine, TEST_FILE("elementAssign.qml"));
3733
3734     QObject *object = component.create();
3735     QVERIFY(object != 0);
3736
3737     QCOMPARE(object->property("test").toBool(), true);
3738
3739     delete object;
3740 }
3741
3742 // QTBUG-12457
3743 void tst_qdeclarativeecmascript::objectPassThroughSignals()
3744 {
3745     QDeclarativeComponent component(&engine, TEST_FILE("objectsPassThroughSignals.qml"));
3746
3747     QObject *object = component.create();
3748     QVERIFY(object != 0);
3749
3750     QCOMPARE(object->property("test").toBool(), true);
3751
3752     delete object;
3753 }
3754
3755 // QTBUG-21626
3756 void tst_qdeclarativeecmascript::objectConversion()
3757 {
3758     QDeclarativeComponent component(&engine, TEST_FILE("objectConversion.qml"));
3759
3760     QObject *object = component.create();
3761     QVERIFY(object != 0);
3762     QVariant retn;
3763     QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
3764     QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
3765
3766     delete object;
3767 }
3768
3769
3770 // QTBUG-20242
3771 void tst_qdeclarativeecmascript::booleanConversion()
3772 {
3773     QDeclarativeComponent component(&engine, TEST_FILE("booleanConversion.qml"));
3774
3775     QObject *object = component.create();
3776     QVERIFY(object != 0);
3777
3778     QCOMPARE(object->property("test_true1").toBool(), true);
3779     QCOMPARE(object->property("test_true2").toBool(), true);
3780     QCOMPARE(object->property("test_true3").toBool(), true);
3781     QCOMPARE(object->property("test_true4").toBool(), true);
3782     QCOMPARE(object->property("test_true5").toBool(), true);
3783
3784     QCOMPARE(object->property("test_false1").toBool(), false);
3785     QCOMPARE(object->property("test_false2").toBool(), false);
3786     QCOMPARE(object->property("test_false3").toBool(), false);
3787
3788     delete object;
3789 }
3790
3791 void tst_qdeclarativeecmascript::handleReferenceManagement()
3792 {
3793
3794     int dtorCount = 0;
3795     {
3796         // Linear QObject reference
3797         QDeclarativeEngine hrmEngine;
3798         QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.object.1.qml"));
3799         QObject *object = component.create();
3800         QVERIFY(object != 0);
3801         CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
3802         cro->setDtorCount(&dtorCount);
3803         QMetaObject::invokeMethod(object, "createReference");
3804         gc(engine);
3805         QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
3806         delete object;
3807         hrmEngine.collectGarbage();
3808         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3809         QCOMPARE(dtorCount, 3);
3810     }
3811
3812     dtorCount = 0;
3813     {
3814         // Circular QObject reference
3815         QDeclarativeEngine hrmEngine;
3816         QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.object.2.qml"));
3817         QObject *object = component.create();
3818         QVERIFY(object != 0);
3819         CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
3820         cro->setDtorCount(&dtorCount);
3821         QMetaObject::invokeMethod(object, "circularReference");
3822         gc(engine);
3823         QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
3824         delete object;
3825         hrmEngine.collectGarbage();
3826         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3827         QCOMPARE(dtorCount, 3);
3828     }
3829
3830     dtorCount = 0;
3831     {
3832         // Linear handle reference
3833         QDeclarativeEngine hrmEngine;
3834         QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.handle.1.qml"));
3835         QObject *object = component.create();
3836         QVERIFY(object != 0);
3837         CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
3838         QVERIFY(crh != 0);
3839         crh->setDtorCount(&dtorCount);
3840         QMetaObject::invokeMethod(object, "createReference");
3841         CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
3842         CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
3843         QVERIFY(first != 0);
3844         QVERIFY(second != 0);
3845         first->addReference(QDeclarativeData::get(second)->v8object); // create reference
3846         // now we have to reparent second and make second owned by JS.
3847         second->setParent(0);
3848         QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership);
3849         gc(engine);
3850         QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
3851         delete object;
3852         hrmEngine.collectGarbage();
3853         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3854         QCOMPARE(dtorCount, 3);
3855     }
3856
3857     dtorCount = 0;
3858     {
3859         // Circular handle reference
3860         QDeclarativeEngine hrmEngine;
3861         QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.handle.2.qml"));
3862         QObject *object = component.create();
3863         QVERIFY(object != 0);
3864         CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
3865         QVERIFY(crh != 0);
3866         crh->setDtorCount(&dtorCount);
3867         QMetaObject::invokeMethod(object, "circularReference");
3868         CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
3869         CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
3870         QVERIFY(first != 0);
3871         QVERIFY(second != 0);
3872         first->addReference(QDeclarativeData::get(second)->v8object); // create circular reference
3873         second->addReference(QDeclarativeData::get(first)->v8object); // note: must be weak.
3874         // now we have to reparent and change ownership.
3875         first->setParent(0);
3876         second->setParent(0);
3877         QDeclarativeEngine::setObjectOwnership(first, QDeclarativeEngine::JavaScriptOwnership);
3878         QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership);
3879         gc(engine);
3880         QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
3881         delete object;
3882         hrmEngine.collectGarbage();
3883         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3884         QCOMPARE(dtorCount, 3);
3885     }
3886
3887     dtorCount = 0;
3888     {
3889         // multiple engine interaction - linear reference
3890         QDeclarativeEngine hrmEngine1;
3891         QDeclarativeEngine hrmEngine2;
3892         QDeclarativeComponent component1(&hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
3893         QDeclarativeComponent component2(&hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
3894         QObject *object1 = component1.create();
3895         QObject *object2 = component2.create();
3896         QVERIFY(object1 != 0);
3897         QVERIFY(object2 != 0);
3898         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
3899         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
3900         QVERIFY(crh1 != 0);
3901         QVERIFY(crh2 != 0);
3902         crh1->setDtorCount(&dtorCount);
3903         crh2->setDtorCount(&dtorCount);
3904         QMetaObject::invokeMethod(object1, "createReference");
3905         QMetaObject::invokeMethod(object2, "createReference");
3906         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
3907         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
3908         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
3909         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
3910         QVERIFY(first1 != 0);
3911         QVERIFY(second1 != 0);
3912         QVERIFY(first2 != 0);
3913         QVERIFY(second2 != 0);
3914         first1->addReference(QDeclarativeData::get(second2)->v8object); // create reference across engines
3915         // now we have to reparent second2 and make second2 owned by JS.
3916         second2->setParent(0);
3917         QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
3918         gc(engine);
3919         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3920         QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
3921         delete object1;
3922         delete object2;
3923         hrmEngine1.collectGarbage();
3924         hrmEngine2.collectGarbage();
3925         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3926         QCOMPARE(dtorCount, 6);
3927     }
3928
3929     dtorCount = 0;
3930     {
3931         // multiple engine interaction - circular reference
3932         QDeclarativeEngine hrmEngine1;
3933         QDeclarativeEngine hrmEngine2;
3934         QDeclarativeComponent component1(&hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
3935         QDeclarativeComponent component2(&hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
3936         QObject *object1 = component1.create();
3937         QObject *object2 = component2.create();
3938         QVERIFY(object1 != 0);
3939         QVERIFY(object2 != 0);
3940         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
3941         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
3942         QVERIFY(crh1 != 0);
3943         QVERIFY(crh2 != 0);
3944         crh1->setDtorCount(&dtorCount);
3945         crh2->setDtorCount(&dtorCount);
3946         QMetaObject::invokeMethod(object1, "createReference");
3947         QMetaObject::invokeMethod(object2, "createReference");
3948         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
3949         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
3950         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
3951         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
3952         QVERIFY(first1 != 0);
3953         QVERIFY(second1 != 0);
3954         QVERIFY(first2 != 0);
3955         QVERIFY(second2 != 0);
3956         first1->addReference(QDeclarativeData::get(second1)->v8object);  // create linear reference within engine1
3957         second1->addReference(QDeclarativeData::get(second2)->v8object); // create linear reference across engines
3958         second2->addReference(QDeclarativeData::get(first2)->v8object);  // create linear reference within engine2
3959         first2->addReference(QDeclarativeData::get(first1)->v8object);   // close the loop - circular ref across engines
3960         // now we have to reparent and change ownership to JS.
3961         first1->setParent(0);
3962         second1->setParent(0);
3963         first2->setParent(0);
3964         second2->setParent(0);
3965         QDeclarativeEngine::setObjectOwnership(first1, QDeclarativeEngine::JavaScriptOwnership);
3966         QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership);
3967         QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership);
3968         QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
3969         gc(engine);
3970         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3971         QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
3972         delete object1;
3973         delete object2;
3974         hrmEngine1.collectGarbage();
3975         hrmEngine2.collectGarbage();
3976         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
3977         QCOMPARE(dtorCount, 6);
3978     }
3979
3980     dtorCount = 0;
3981     {
3982         // multiple engine interaction - linear reference with engine deletion
3983         QDeclarativeEngine *hrmEngine1 = new QDeclarativeEngine;
3984         QDeclarativeEngine *hrmEngine2 = new QDeclarativeEngine;
3985         QDeclarativeComponent component1(hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
3986         QDeclarativeComponent component2(hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
3987         QObject *object1 = component1.create();
3988         QObject *object2 = component2.create();
3989         QVERIFY(object1 != 0);
3990         QVERIFY(object2 != 0);
3991         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
3992         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
3993         QVERIFY(crh1 != 0);
3994         QVERIFY(crh2 != 0);
3995         crh1->setDtorCount(&dtorCount);
3996         crh2->setDtorCount(&dtorCount);
3997         QMetaObject::invokeMethod(object1, "createReference");
3998         QMetaObject::invokeMethod(object2, "createReference");
3999         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4000         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4001         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4002         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4003         QVERIFY(first1 != 0);
4004         QVERIFY(second1 != 0);
4005         QVERIFY(first2 != 0);
4006         QVERIFY(second2 != 0);
4007         first1->addReference(QDeclarativeData::get(second1)->v8object);  // create linear reference within engine1
4008         second1->addReference(QDeclarativeData::get(second2)->v8object); // create linear reference across engines
4009         second2->addReference(QDeclarativeData::get(first2)->v8object);  // create linear reference within engine2
4010         // now we have to reparent and change ownership to JS.
4011         first1->setParent(crh1);
4012         second1->setParent(0);
4013         first2->setParent(0);
4014         second2->setParent(0);
4015         QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership);
4016         QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership);
4017         QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
4018         gc(engine);
4019         QCOMPARE(dtorCount, 0);
4020         delete hrmEngine2;
4021         gc(engine);
4022         QCOMPARE(dtorCount, 0);
4023         delete object1;
4024         delete object2;
4025         hrmEngine1->collectGarbage();
4026         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
4027         QCOMPARE(dtorCount, 6);
4028         delete hrmEngine1;
4029     }
4030 }
4031
4032 void tst_qdeclarativeecmascript::stringArg()
4033 {
4034     QDeclarativeComponent component(&engine, TEST_FILE("stringArg.qml"));
4035     QObject *object = component.create();
4036     QVERIFY(object != 0);
4037     QMetaObject::invokeMethod(object, "success");
4038     QVERIFY(object->property("returnValue").toBool());
4039
4040     QString w1 = TEST_FILE("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
4041     QTest::ignoreMessage(QtWarningMsg, w1.toAscii().constData());
4042     QMetaObject::invokeMethod(object, "failure");
4043     QVERIFY(object->property("returnValue").toBool());
4044
4045     delete object;
4046 }
4047
4048 void tst_qdeclarativeecmascript::readonlyDeclaration()
4049 {
4050     QDeclarativeComponent component(&engine, TEST_FILE("readonlyDeclaration.qml"));
4051
4052     QObject *object = component.create();
4053     QVERIFY(object != 0);
4054
4055     QCOMPARE(object->property("test").toBool(), true);
4056
4057     delete object;
4058 }
4059
4060 Q_DECLARE_METATYPE(QList<int>)
4061 Q_DECLARE_METATYPE(QList<qreal>)
4062 Q_DECLARE_METATYPE(QList<bool>)
4063 Q_DECLARE_METATYPE(QList<QString>)
4064 Q_DECLARE_METATYPE(QList<QUrl>)
4065 void tst_qdeclarativeecmascript::sequenceConversionRead()
4066 {
4067     {
4068         QUrl qmlFile = TEST_FILE("sequenceConversion.read.qml");
4069         QDeclarativeComponent component(&engine, qmlFile);
4070         QObject *object = component.create();
4071         QVERIFY(object != 0);
4072         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4073         QVERIFY(seq != 0);
4074
4075         QMetaObject::invokeMethod(object, "readSequences");
4076         QList<int> intList; intList << 1 << 2 << 3 << 4;
4077         QCOMPARE(object->property("intListLength").toInt(), intList.length());
4078         QCOMPARE(object->property("intList").value<QList<int> >(), intList);
4079         QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
4080         QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
4081         QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
4082         QList<bool> boolList; boolList << true << false << true << false;
4083         QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
4084         QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
4085         QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
4086         QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
4087         QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
4088         QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
4089         QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
4090         QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
4091         QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
4092         QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
4093         QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
4094
4095         QMetaObject::invokeMethod(object, "readSequenceElements");
4096         QCOMPARE(object->property("intVal").toInt(), 2);
4097         QCOMPARE(object->property("qrealVal").toReal(), 2.2);
4098         QCOMPARE(object->property("boolVal").toBool(), false);
4099         QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
4100         QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
4101         QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
4102
4103         QMetaObject::invokeMethod(object, "enumerateSequenceElements");
4104         QCOMPARE(object->property("enumerationMatches").toBool(), true);
4105
4106         intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
4107         QDeclarativeProperty seqProp(seq, "intListProperty");
4108         QCOMPARE(seqProp.read().value<QList<int> >(), intList);
4109         QDeclarativeProperty seqProp2(seq, "intListProperty", &engine);
4110         QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
4111
4112         QMetaObject::invokeMethod(object, "testReferenceDeletion");
4113         QCOMPARE(object->property("referenceDeletion").toBool(), true);
4114
4115         delete object;
4116     }
4117
4118     {
4119         QUrl qmlFile = TEST_FILE("sequenceConversion.read.error.qml");
4120         QDeclarativeComponent component(&engine, qmlFile);
4121         QObject *object = component.create();
4122         QVERIFY(object != 0);
4123         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4124         QVERIFY(seq != 0);
4125
4126         // we haven't registered QList<QPoint> as a sequence type.
4127         QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
4128         QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
4129         QTest::ignoreMessage(QtWarningMsg, warningOne.toAscii().constData());
4130         QTest::ignoreMessage(QtWarningMsg, warningTwo.toAscii().constData());
4131
4132         QMetaObject::invokeMethod(object, "performTest");
4133
4134         // QList<QPoint> has not been registered as a sequence type.
4135         QCOMPARE(object->property("pointListLength").toInt(), 0);
4136         QVERIFY(!object->property("pointList").isValid());
4137         QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
4138         QDeclarativeProperty seqProp(seq, "pointListProperty", &engine);
4139         QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
4140
4141         delete object;
4142     }
4143 }
4144
4145 void tst_qdeclarativeecmascript::sequenceConversionWrite()
4146 {
4147     {
4148         QUrl qmlFile = TEST_FILE("sequenceConversion.write.qml");
4149         QDeclarativeComponent component(&engine, qmlFile);
4150         QObject *object = component.create();
4151         QVERIFY(object != 0);
4152         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4153         QVERIFY(seq != 0);
4154
4155         QMetaObject::invokeMethod(object, "writeSequences");
4156         QCOMPARE(object->property("success").toBool(), true);
4157
4158         QMetaObject::invokeMethod(object, "writeSequenceElements");
4159         QCOMPARE(object->property("success").toBool(), true);
4160
4161         QMetaObject::invokeMethod(object, "writeOtherElements");
4162         QCOMPARE(object->property("success").toBool(), true);
4163
4164         QMetaObject::invokeMethod(object, "testReferenceDeletion");
4165         QCOMPARE(object->property("referenceDeletion").toBool(), true);
4166
4167         delete object;
4168     }
4169
4170     {
4171         QUrl qmlFile = TEST_FILE("sequenceConversion.write.error.qml");
4172         QDeclarativeComponent component(&engine, qmlFile);
4173         QObject *object = component.create();
4174         QVERIFY(object != 0);
4175         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4176         QVERIFY(seq != 0);
4177
4178         // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
4179         QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to void");
4180         QTest::ignoreMessage(QtWarningMsg, warningOne.toAscii().constData());
4181
4182         QMetaObject::invokeMethod(object, "performTest");
4183
4184         QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
4185         QCOMPARE(seq->pointListProperty(), pointList);
4186
4187         delete object;
4188     }
4189 }
4190
4191 void tst_qdeclarativeecmascript::sequenceConversionArray()
4192 {
4193     // ensure that in JS the returned sequences act just like normal JS Arrays.
4194     QUrl qmlFile = TEST_FILE("sequenceConversion.array.qml");
4195     QDeclarativeComponent component(&engine, qmlFile);
4196     QObject *object = component.create();
4197     QVERIFY(object != 0);
4198     //QMetaObject::invokeMethod(object, "indexedAccess");
4199     //QVERIFY(object->property("success").toBool());
4200     //QMetaObject::invokeMethod(object, "arrayOperations");
4201     //QVERIFY(object->property("success").toBool());
4202     QMetaObject::invokeMethod(object, "testEqualitySemantics");
4203     QVERIFY(object->property("success").toBool());
4204     //QMetaObject::invokeMethod(object, "testReferenceDeletion");
4205     //QCOMPARE(object->property("referenceDeletion").toBool(), true);
4206     delete object;
4207 }
4208
4209 void tst_qdeclarativeecmascript::sequenceConversionThreads()
4210 {
4211     // ensure that sequence conversion operations work correctly in a worker thread
4212     // and that serialisation between the main and worker thread succeeds.
4213     QUrl qmlFile = TEST_FILE("sequenceConversion.threads.qml");
4214     QDeclarativeComponent component(&engine, qmlFile);
4215     QObject *object = component.create();
4216     QVERIFY(object != 0);
4217
4218     QMetaObject::invokeMethod(object, "testIntSequence");
4219     QTRY_VERIFY(object->property("finished").toBool());
4220     QVERIFY(object->property("success").toBool());
4221
4222     QMetaObject::invokeMethod(object, "testQrealSequence");
4223     QTRY_VERIFY(object->property("finished").toBool());
4224     QVERIFY(object->property("success").toBool());
4225
4226     QMetaObject::invokeMethod(object, "testBoolSequence");
4227     QTRY_VERIFY(object->property("finished").toBool());
4228     QVERIFY(object->property("success").toBool());
4229
4230     QMetaObject::invokeMethod(object, "testStringSequence");
4231     QTRY_VERIFY(object->property("finished").toBool());
4232     QVERIFY(object->property("success").toBool());
4233
4234     QMetaObject::invokeMethod(object, "testQStringSequence");
4235     QTRY_VERIFY(object->property("finished").toBool());
4236     QVERIFY(object->property("success").toBool());
4237
4238     QMetaObject::invokeMethod(object, "testUrlSequence");
4239     QTRY_VERIFY(object->property("finished").toBool());
4240     QVERIFY(object->property("success").toBool());
4241
4242     QMetaObject::invokeMethod(object, "testVariantSequence");
4243     QTRY_VERIFY(object->property("finished").toBool());
4244     QVERIFY(object->property("success").toBool());
4245
4246     delete object;
4247 }
4248
4249 void tst_qdeclarativeecmascript::sequenceConversionBindings()
4250 {
4251     {
4252         QUrl qmlFile = TEST_FILE("sequenceConversion.bindings.qml");
4253         QDeclarativeComponent component(&engine, qmlFile);
4254         QObject *object = component.create();
4255         QVERIFY(object != 0);
4256         QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
4257         QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
4258         QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
4259         QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
4260         QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
4261         delete object;
4262     }
4263
4264     {
4265         QUrl qmlFile = TEST_FILE("sequenceConversion.bindings.error.qml");
4266         QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
4267         QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
4268         QDeclarativeComponent component(&engine, qmlFile);
4269         QObject *object = component.create();
4270         QVERIFY(object != 0);
4271         delete object;
4272     }
4273 }
4274
4275 void tst_qdeclarativeecmascript::sequenceConversionCopy()
4276 {
4277     QUrl qmlFile = TEST_FILE("sequenceConversion.copy.qml");
4278     QDeclarativeComponent component(&engine, qmlFile);
4279     QObject *object = component.create();
4280     QVERIFY(object != 0);
4281     QMetaObject::invokeMethod(object, "testCopySequences");
4282     QCOMPARE(object->property("success").toBool(), true);
4283     QMetaObject::invokeMethod(object, "readSequenceCopyElements");
4284     QCOMPARE(object->property("success").toBool(), true);
4285     QMetaObject::invokeMethod(object, "testEqualitySemantics");
4286     QCOMPARE(object->property("success").toBool(), true);
4287     delete object;
4288 }
4289
4290 // Test that assigning a null object works 
4291 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
4292 void tst_qdeclarativeecmascript::nullObjectBinding()
4293 {
4294     QDeclarativeComponent component(&engine, TEST_FILE("nullObjectBinding.qml"));
4295
4296     QObject *object = component.create();
4297     QVERIFY(object != 0);
4298
4299     QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
4300
4301     delete object;
4302 }
4303
4304 // Test that bindings don't evaluate once the engine has been destroyed
4305 void tst_qdeclarativeecmascript::deletedEngine()
4306 {
4307     QDeclarativeEngine *engine = new QDeclarativeEngine;
4308     QDeclarativeComponent component(engine, TEST_FILE("deletedEngine.qml"));
4309
4310     QObject *object = component.create();
4311     QVERIFY(object != 0);
4312
4313     QCOMPARE(object->property("a").toInt(), 39);
4314     object->setProperty("b", QVariant(9));
4315     QCOMPARE(object->property("a").toInt(), 117);
4316
4317     delete engine;
4318
4319     QCOMPARE(object->property("a").toInt(), 117);
4320     object->setProperty("b", QVariant(10));
4321     QCOMPARE(object->property("a").toInt(), 117);
4322
4323     delete object;
4324 }
4325
4326 // Test the crashing part of QTBUG-9705
4327 void tst_qdeclarativeecmascript::libraryScriptAssert()
4328 {
4329     QDeclarativeComponent component(&engine, TEST_FILE("libraryScriptAssert.qml"));
4330
4331     QObject *object = component.create();
4332     QVERIFY(object != 0);
4333
4334     delete object;
4335 }
4336
4337 void tst_qdeclarativeecmascript::variantsAssignedUndefined()
4338 {
4339     QDeclarativeComponent component(&engine, TEST_FILE("variantsAssignedUndefined.qml"));
4340
4341     QObject *object = component.create();
4342     QVERIFY(object != 0);
4343
4344     QCOMPARE(object->property("test1").toInt(), 10);
4345     QCOMPARE(object->property("test2").toInt(), 11);
4346
4347     object->setProperty("runTest", true);
4348
4349     QCOMPARE(object->property("test1"), QVariant());
4350     QCOMPARE(object->property("test2"), QVariant());
4351
4352
4353     delete object;
4354 }
4355
4356 void tst_qdeclarativeecmascript::qtbug_9792()
4357 {
4358     QDeclarativeComponent component(&engine, TEST_FILE("qtbug_9792.qml"));
4359
4360     QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
4361
4362     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
4363     QVERIFY(object != 0);
4364
4365     QTest::ignoreMessage(QtDebugMsg, "Hello world!");
4366     object->basicSignal();
4367
4368     delete context;
4369
4370     transientErrorsMsgCount = 0;
4371     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
4372
4373     object->basicSignal();
4374     
4375     qInstallMsgHandler(old);
4376
4377     QCOMPARE(transientErrorsMsgCount, 0);
4378
4379     delete object;
4380 }
4381
4382 // Verifies that QDeclarativeGuard<>s used in the vmemetaobject are cleaned correctly
4383 void tst_qdeclarativeecmascript::qtcreatorbug_1289()
4384 {
4385     QDeclarativeComponent component(&engine, TEST_FILE("qtcreatorbug_1289.qml"));
4386
4387     QObject *o = component.create();
4388     QVERIFY(o != 0);
4389
4390     QObject *nested = qvariant_cast<QObject *>(o->property("object"));
4391     QVERIFY(nested != 0);
4392
4393     QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
4394
4395     delete nested;
4396     nested = qvariant_cast<QObject *>(o->property("object"));
4397     QVERIFY(nested == 0);
4398
4399     // If the bug is present, the next line will crash
4400     delete o;
4401 }
4402
4403 // Test that we shut down without stupid warnings
4404 void tst_qdeclarativeecmascript::noSpuriousWarningsAtShutdown()
4405 {
4406     {
4407     QDeclarativeComponent component(&engine, TEST_FILE("noSpuriousWarningsAtShutdown.qml"));
4408
4409     QObject *o = component.create();
4410
4411     transientErrorsMsgCount = 0;
4412     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
4413
4414     delete o;
4415
4416     qInstallMsgHandler(old);
4417
4418     QCOMPARE(transientErrorsMsgCount, 0);
4419     }
4420
4421
4422     {
4423     QDeclarativeComponent component(&engine, TEST_FILE("noSpuriousWarningsAtShutdown.2.qml"));
4424
4425     QObject *o = component.create();
4426
4427     transientErrorsMsgCount = 0;
4428     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
4429
4430     delete o;
4431
4432     qInstallMsgHandler(old);
4433
4434     QCOMPARE(transientErrorsMsgCount, 0);
4435     }
4436 }
4437
4438 void tst_qdeclarativeecmascript::canAssignNullToQObject()
4439 {
4440     {
4441     QDeclarativeComponent component(&engine, TEST_FILE("canAssignNullToQObject.1.qml"));
4442
4443     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4444     QVERIFY(o != 0);
4445
4446     QVERIFY(o->objectProperty() != 0);
4447
4448     o->setProperty("runTest", true);
4449
4450     QVERIFY(o->objectProperty() == 0);
4451
4452     delete o;
4453     }
4454
4455     {
4456     QDeclarativeComponent component(&engine, TEST_FILE("canAssignNullToQObject.2.qml"));
4457
4458     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4459     QVERIFY(o != 0);
4460
4461     QVERIFY(o->objectProperty() == 0);
4462
4463     delete o;
4464     }
4465 }
4466
4467 void tst_qdeclarativeecmascript::functionAssignment_fromBinding()
4468 {
4469     QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.1.qml"));
4470
4471     QString url = component.url().toString();
4472     QString warning = url + ":4: Unable to assign a function to a property.";
4473     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
4474     
4475     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4476     QVERIFY(o != 0);
4477
4478     QVERIFY(!o->property("a").isValid());
4479
4480     delete o;
4481 }
4482
4483 void tst_qdeclarativeecmascript::functionAssignment_fromJS()
4484 {
4485     QFETCH(QString, triggerProperty);
4486
4487     QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.2.qml"));
4488     QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
4489
4490     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4491     QVERIFY(o != 0);
4492     QVERIFY(!o->property("a").isValid());
4493
4494     o->setProperty("aNumber", QVariant(5));
4495     o->setProperty(triggerProperty.toUtf8().constData(), true);
4496     QCOMPARE(o->property("a"), QVariant(50));
4497
4498     o->setProperty("aNumber", QVariant(10));
4499     QCOMPARE(o->property("a"), QVariant(100));
4500
4501     delete o;
4502 }
4503
4504 void tst_qdeclarativeecmascript::functionAssignment_fromJS_data()
4505 {
4506     QTest::addColumn<QString>("triggerProperty");
4507
4508     QTest::newRow("assign to property") << "assignToProperty";
4509     QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
4510
4511     QTest::newRow("assign to value type") << "assignToValueType";
4512
4513     QTest::newRow("use 'this'") << "assignWithThis";
4514     QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
4515 }
4516
4517 void tst_qdeclarativeecmascript::functionAssignmentfromJS_invalid()
4518 {
4519     QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.2.qml"));
4520     QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
4521
4522     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4523     QVERIFY(o != 0);
4524     QVERIFY(!o->property("a").isValid());
4525
4526     o->setProperty("assignFuncWithoutReturn", true);
4527     QVERIFY(!o->property("a").isValid());
4528
4529     QString url = component.url().toString();
4530     QString warning = url + ":67: Unable to assign QString to int";
4531     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
4532     o->setProperty("assignWrongType", true);
4533
4534     warning = url + ":71: Unable to assign QString to int";
4535     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
4536     o->setProperty("assignWrongTypeToValueType", true);
4537
4538     delete o;
4539 }
4540
4541 void tst_qdeclarativeecmascript::eval()
4542 {
4543     QDeclarativeComponent component(&engine, TEST_FILE("eval.qml"));
4544
4545     QObject *o = component.create();
4546     QVERIFY(o != 0);
4547
4548     QCOMPARE(o->property("test1").toBool(), true);
4549     QCOMPARE(o->property("test2").toBool(), true);
4550     QCOMPARE(o->property("test3").toBool(), true);
4551     QCOMPARE(o->property("test4").toBool(), true);
4552     QCOMPARE(o->property("test5").toBool(), true);
4553
4554     delete o;
4555 }
4556
4557 void tst_qdeclarativeecmascript::function()
4558 {
4559     QDeclarativeComponent component(&engine, TEST_FILE("function.qml"));
4560
4561     QObject *o = component.create();
4562     QVERIFY(o != 0);
4563
4564     QCOMPARE(o->property("test1").toBool(), true);
4565     QCOMPARE(o->property("test2").toBool(), true);
4566     QCOMPARE(o->property("test3").toBool(), true);
4567
4568     delete o;
4569 }
4570
4571 // Test the "Qt.include" method
4572 void tst_qdeclarativeecmascript::include()
4573 {
4574     // Non-library relative include
4575     {
4576     QDeclarativeComponent component(&engine, TEST_FILE("include.qml"));
4577     QObject *o = component.create();
4578     QVERIFY(o != 0);
4579
4580     QCOMPARE(o->property("test0").toInt(), 99);
4581     QCOMPARE(o->property("test1").toBool(), true);
4582     QCOMPARE(o->property("test2").toBool(), true);
4583     QCOMPARE(o->property("test2_1").toBool(), true);
4584     QCOMPARE(o->property("test3").toBool(), true);
4585     QCOMPARE(o->property("test3_1").toBool(), true);
4586
4587     delete o;
4588     }
4589
4590     // Library relative include
4591     {
4592     QDeclarativeComponent component(&engine, TEST_FILE("include_shared.qml"));
4593     QObject *o = component.create();
4594     QVERIFY(o != 0);
4595
4596     QCOMPARE(o->property("test0").toInt(), 99);
4597     QCOMPARE(o->property("test1").toBool(), true);
4598     QCOMPARE(o->property("test2").toBool(), true);
4599     QCOMPARE(o->property("test2_1").toBool(), true);
4600     QCOMPARE(o->property("test3").toBool(), true);
4601     QCOMPARE(o->property("test3_1").toBool(), true);
4602
4603     delete o;
4604     }
4605
4606     // Callback
4607     {
4608     QDeclarativeComponent component(&engine, TEST_FILE("include_callback.qml"));
4609     QObject *o = component.create();
4610     QVERIFY(o != 0);
4611
4612     QCOMPARE(o->property("test1").toBool(), true);
4613     QCOMPARE(o->property("test2").toBool(), true);
4614     QCOMPARE(o->property("test3").toBool(), true);
4615     QCOMPARE(o->property("test4").toBool(), true);
4616     QCOMPARE(o->property("test5").toBool(), true);
4617     QCOMPARE(o->property("test6").toBool(), true);
4618
4619     delete o;
4620     }
4621
4622     // Including file with ".pragma library"
4623     {
4624     QDeclarativeComponent component(&engine, TEST_FILE("include_pragma.qml"));
4625     QObject *o = component.create();
4626     QVERIFY(o != 0);
4627     QCOMPARE(o->property("test1").toInt(), 100);
4628
4629     delete o;
4630     }
4631
4632     // Remote - success
4633     {
4634     TestHTTPServer server(8111);
4635     QVERIFY(server.isValid());
4636     server.serveDirectory(TESTDATA(""));
4637
4638     QDeclarativeComponent component(&engine, TEST_FILE("include_remote.qml"));
4639     QObject *o = component.create();
4640     QVERIFY(o != 0);
4641
4642     QTRY_VERIFY(o->property("done").toBool() == true);
4643     QTRY_VERIFY(o->property("done2").toBool() == true);
4644
4645     QCOMPARE(o->property("test1").toBool(), true);
4646     QCOMPARE(o->property("test2").toBool(), true);
4647     QCOMPARE(o->property("test3").toBool(), true);
4648     QCOMPARE(o->property("test4").toBool(), true);
4649     QCOMPARE(o->property("test5").toBool(), true);
4650
4651     QCOMPARE(o->property("test6").toBool(), true);
4652     QCOMPARE(o->property("test7").toBool(), true);
4653     QCOMPARE(o->property("test8").toBool(), true);
4654     QCOMPARE(o->property("test9").toBool(), true);
4655     QCOMPARE(o->property("test10").toBool(), true);
4656
4657     delete o;
4658     }
4659
4660     // Remote - error
4661     {
4662     TestHTTPServer server(8111);
4663     QVERIFY(server.isValid());
4664     server.serveDirectory(TESTDATA(""));
4665
4666     QDeclarativeComponent component(&engine, TEST_FILE("include_remote_missing.qml"));
4667     QObject *o = component.create();
4668     QVERIFY(o != 0);
4669
4670     QTRY_VERIFY(o->property("done").toBool() == true);
4671
4672     QCOMPARE(o->property("test1").toBool(), true);
4673     QCOMPARE(o->property("test2").toBool(), true);
4674     QCOMPARE(o->property("test3").toBool(), true);
4675
4676     delete o;
4677     }
4678 }
4679
4680 void tst_qdeclarativeecmascript::signalHandlers()
4681 {
4682     QDeclarativeComponent component(&engine, TEST_FILE("signalHandlers.qml"));
4683     QObject *o = component.create();
4684     QVERIFY(o != 0);
4685
4686     QVERIFY(o->property("count").toInt() == 0);
4687     QMetaObject::invokeMethod(o, "testSignalCall");
4688     QCOMPARE(o->property("count").toInt(), 1);
4689
4690     QMetaObject::invokeMethod(o, "testSignalHandlerCall");
4691     QCOMPARE(o->property("count").toInt(), 1);
4692     QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
4693
4694     QVERIFY(o->property("funcCount").toInt() == 0);
4695     QMetaObject::invokeMethod(o, "testSignalConnection");
4696     QCOMPARE(o->property("funcCount").toInt(), 1);
4697
4698     QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
4699     QCOMPARE(o->property("funcCount").toInt(), 2);
4700
4701     QMetaObject::invokeMethod(o, "testSignalDefined");
4702     QCOMPARE(o->property("definedResult").toBool(), true);
4703
4704     QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
4705     QCOMPARE(o->property("definedHandlerResult").toBool(), true);
4706
4707     delete o;
4708 }
4709
4710 void tst_qdeclarativeecmascript::qtbug_10696()
4711 {
4712     QDeclarativeComponent component(&engine, TEST_FILE("qtbug_10696.qml"));
4713     QObject *o = component.create();
4714     QVERIFY(o != 0);
4715     delete o;
4716 }
4717
4718 void tst_qdeclarativeecmascript::qtbug_11606()
4719 {
4720     QDeclarativeComponent component(&engine, TEST_FILE("qtbug_11606.qml"));
4721     QObject *o = component.create();
4722     QVERIFY(o != 0);
4723     QCOMPARE(o->property("test").toBool(), true);
4724     delete o;
4725 }
4726
4727 void tst_qdeclarativeecmascript::qtbug_11600()
4728 {
4729     QDeclarativeComponent component(&engine, TEST_FILE("qtbug_11600.qml"));
4730     QObject *o = component.create();
4731     QVERIFY(o != 0);
4732     QCOMPARE(o->property("test").toBool(), true);
4733     delete o;
4734 }
4735
4736 // Reading and writing non-scriptable properties should fail
4737 void tst_qdeclarativeecmascript::nonscriptable()
4738 {
4739     QDeclarativeComponent component(&engine, TEST_FILE("nonscriptable.qml"));
4740     QObject *o = component.create();
4741     QVERIFY(o != 0);
4742     QCOMPARE(o->property("readOk").toBool(), true);
4743     QCOMPARE(o->property("writeOk").toBool(), true);
4744     delete o;
4745 }
4746
4747 // deleteLater() should not be callable from QML
4748 void tst_qdeclarativeecmascript::deleteLater()
4749 {
4750     QDeclarativeComponent component(&engine, TEST_FILE("deleteLater.qml"));
4751     QObject *o = component.create();
4752     QVERIFY(o != 0);
4753     QCOMPARE(o->property("test").toBool(), true);
4754     delete o;
4755 }
4756
4757 void tst_qdeclarativeecmascript::in()
4758 {
4759     QDeclarativeComponent component(&engine, TEST_FILE("in.qml"));
4760     QObject *o = component.create();
4761     QVERIFY(o != 0);
4762     QCOMPARE(o->property("test1").toBool(), true);
4763     QCOMPARE(o->property("test2").toBool(), true);
4764     delete o;
4765 }
4766
4767 void tst_qdeclarativeecmascript::typeOf()
4768 {
4769     QDeclarativeComponent component(&engine, TEST_FILE("typeOf.qml"));
4770     QObject *o = component.create();
4771     QVERIFY(o != 0);
4772     QEXPECT_FAIL("", "QTBUG-21864", Abort);
4773     QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
4774     QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
4775     QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
4776     QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
4777     QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
4778     QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
4779     QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
4780     QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
4781     QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
4782
4783     delete o;
4784 }
4785
4786 void tst_qdeclarativeecmascript::sharedAttachedObject()
4787 {
4788     QDeclarativeComponent component(&engine, TEST_FILE("sharedAttachedObject.qml"));
4789     QObject *o = component.create();
4790     QVERIFY(o != 0);
4791     QCOMPARE(o->property("test1").toBool(), true);
4792     QCOMPARE(o->property("test2").toBool(), true);
4793     delete o;
4794 }
4795
4796 // QTBUG-13999
4797 void tst_qdeclarativeecmascript::objectName()
4798 {
4799     QDeclarativeComponent component(&engine, TEST_FILE("objectName.qml"));
4800     QObject *o = component.create();
4801     QVERIFY(o != 0);
4802
4803     QCOMPARE(o->property("test1").toString(), QString("hello"));
4804     QCOMPARE(o->property("test2").toString(), QString("ell"));
4805
4806     o->setObjectName("world");
4807
4808     QCOMPARE(o->property("test1").toString(), QString("world"));
4809     QCOMPARE(o->property("test2").toString(), QString("orl"));
4810
4811     delete o;
4812 }
4813
4814 void tst_qdeclarativeecmascript::writeRemovesBinding()
4815 {
4816     QDeclarativeComponent component(&engine, TEST_FILE("writeRemovesBinding.qml"));
4817     QObject *o = component.create();
4818     QVERIFY(o != 0);
4819
4820     QCOMPARE(o->property("test").toBool(), true);
4821
4822     delete o;
4823 }
4824
4825 // Test bindings assigned to alias properties actually assign to the alias' target
4826 void tst_qdeclarativeecmascript::aliasBindingsAssignCorrectly()
4827 {
4828     QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsAssignCorrectly.qml"));
4829     QObject *o = component.create();
4830     QVERIFY(o != 0);
4831
4832     QCOMPARE(o->property("test").toBool(), true);
4833
4834     delete o;
4835 }
4836
4837 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
4838 void tst_qdeclarativeecmascript::aliasBindingsOverrideTarget()
4839 {
4840     { 
4841     QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.qml"));
4842     QObject *o = component.create();
4843     QVERIFY(o != 0);
4844
4845     QCOMPARE(o->property("test").toBool(), true);
4846
4847     delete o;
4848     }
4849
4850     {
4851     QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.2.qml"));
4852     QObject *o = component.create();
4853     QVERIFY(o != 0);
4854
4855     QCOMPARE(o->property("test").toBool(), true);
4856
4857     delete o;
4858     }
4859
4860     {
4861     QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.3.qml"));
4862     QObject *o = component.create();
4863     QVERIFY(o != 0);
4864
4865     QCOMPARE(o->property("test").toBool(), true);
4866
4867     delete o;
4868     }
4869 }
4870
4871 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
4872 void tst_qdeclarativeecmascript::aliasWritesOverrideBindings()
4873 {
4874     {
4875     QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.qml"));
4876     QObject *o = component.create();
4877     QVERIFY(o != 0);
4878
4879     QCOMPARE(o->property("test").toBool(), true);
4880
4881     delete o;
4882     }
4883
4884     {
4885     QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.2.qml"));
4886     QObject *o = component.create();
4887     QVERIFY(o != 0);
4888
4889     QCOMPARE(o->property("test").toBool(), true);
4890
4891     delete o;
4892     }
4893
4894     {
4895     QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.3.qml"));
4896     QObject *o = component.create();
4897     QVERIFY(o != 0);
4898
4899     QCOMPARE(o->property("test").toBool(), true);
4900
4901     delete o;
4902     }
4903 }
4904
4905 // Allow an alais to a composite element
4906 // QTBUG-20200
4907 void tst_qdeclarativeecmascript::aliasToCompositeElement()
4908 {
4909     QDeclarativeComponent component(&engine, TEST_FILE("aliasToCompositeElement.qml"));
4910
4911     QObject *object = component.create();
4912     QVERIFY(object != 0);
4913
4914     delete object;
4915 }
4916
4917 void tst_qdeclarativeecmascript::qtbug_20344()
4918 {
4919     QDeclarativeComponent component(&engine, TEST_FILE("qtbug_20344.qml"));
4920
4921     QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
4922     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
4923
4924     QObject *object = component.create();
4925     QVERIFY(object != 0);
4926
4927     delete object;
4928 }
4929
4930 void tst_qdeclarativeecmascript::revisionErrors()
4931 {
4932     {
4933         QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors.qml"));
4934         QString url = component.url().toString();
4935
4936         QString warning1 = url + ":8: ReferenceError: Can't find variable: prop2";
4937         QString warning2 = url + ":11: ReferenceError: Can't find variable: prop2";
4938         QString warning3 = url + ":13: ReferenceError: Can't find variable: method2";
4939
4940         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
4941         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
4942         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
4943         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
4944         QVERIFY(object != 0);
4945         delete object;
4946     }
4947     {
4948         QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors2.qml"));
4949         QString url = component.url().toString();
4950
4951         // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
4952         // method2, prop2 from MyRevisionedClass not available
4953         // method4, prop4 from MyRevisionedSubclass not available
4954         QString warning1 = url + ":8: ReferenceError: Can't find variable: prop2";
4955         QString warning2 = url + ":14: ReferenceError: Can't find variable: prop2";
4956         QString warning3 = url + ":10: ReferenceError: Can't find variable: prop4";
4957         QString warning4 = url + ":16: ReferenceError: Can't find variable: prop4";
4958         QString warning5 = url + ":20: ReferenceError: Can't find variable: method2";
4959
4960         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
4961         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
4962         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
4963         QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
4964         QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
4965         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
4966         QVERIFY(object != 0);
4967         delete object;
4968     }
4969     {
4970         QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors3.qml"));
4971         QString url = component.url().toString();
4972
4973         // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
4974         // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
4975         QString warning1 = url + ":30: ReferenceError: Can't find variable: methodD";
4976         QString warning2 = url + ":10: ReferenceError: Can't find variable: propD";
4977         QString warning3 = url + ":20: ReferenceError: Can't find variable: propD";
4978         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
4979         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
4980         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
4981         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
4982         QVERIFY(object != 0);
4983         delete object;
4984     }
4985 }
4986
4987 void tst_qdeclarativeecmascript::revision()
4988 {
4989     {
4990         QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision.qml"));
4991         QString url = component.url().toString();
4992
4993         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
4994         QVERIFY(object != 0);
4995         delete object;
4996     }
4997     {
4998         QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision2.qml"));
4999         QString url = component.url().toString();
5000
5001         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5002         QVERIFY(object != 0);
5003         delete object;
5004     }
5005     {
5006         QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision3.qml"));
5007         QString url = component.url().toString();
5008
5009         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5010         QVERIFY(object != 0);
5011         delete object;
5012     }
5013     // Test that non-root classes can resolve revisioned methods
5014     {
5015         QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision4.qml"));
5016
5017         QObject *object = component.create();
5018         QVERIFY(object != 0);
5019         QCOMPARE(object->property("test").toReal(), 11.);
5020         delete object;
5021     }
5022 }
5023
5024 void tst_qdeclarativeecmascript::realToInt()
5025 {
5026     QDeclarativeComponent component(&engine, TEST_FILE("realToInt.qml"));
5027     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
5028     QVERIFY(object != 0);
5029
5030     QMetaObject::invokeMethod(object, "test1");
5031     QCOMPARE(object->value(), int(4));
5032     QMetaObject::invokeMethod(object, "test2");
5033     QCOMPARE(object->value(), int(8));
5034 }
5035 void tst_qdeclarativeecmascript::dynamicString()
5036 {
5037     QDeclarativeComponent component(&engine, TEST_FILE("dynamicString.qml"));
5038     QObject *object = component.create();
5039     QVERIFY(object != 0);
5040     QCOMPARE(object->property("stringProperty").toString(),
5041              QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
5042 }
5043
5044 void tst_qdeclarativeecmascript::automaticSemicolon()
5045 {
5046     QDeclarativeComponent component(&engine, TEST_FILE("automaticSemicolon.qml"));
5047     QObject *object = component.create();
5048     QVERIFY(object != 0);
5049 }
5050
5051 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
5052 void tst_qdeclarativeecmascript::doubleEvaluate()
5053 {
5054     QDeclarativeComponent component(&engine, TEST_FILE("doubleEvaluate.qml"));
5055     QObject *object = component.create();
5056     QVERIFY(object != 0);
5057     WriteCounter *wc = qobject_cast<WriteCounter *>(object);
5058     QVERIFY(wc != 0);
5059     QCOMPARE(wc->count(), 1);
5060
5061     wc->setProperty("x", 9);
5062
5063     QCOMPARE(wc->count(), 2);
5064
5065     delete object;
5066 }
5067
5068 static QStringList messages;
5069 static void captureMsgHandler(QtMsgType, const char *msg)
5070 {
5071     messages.append(QLatin1String(msg));
5072 }
5073
5074 void tst_qdeclarativeecmascript::nonNotifyable()
5075 {
5076     QV4Compiler::enableV4(false);
5077     QDeclarativeComponent component(&engine, TEST_FILE("nonNotifyable.qml"));
5078     QV4Compiler::enableV4(true);
5079
5080     QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
5081     messages.clear();
5082     QObject *object = component.create();
5083     qInstallMsgHandler(old);
5084
5085     QVERIFY(object != 0);
5086
5087     QString expected1 = QLatin1String("QDeclarativeExpression: Expression ") +
5088                         component.url().toString() +
5089                         QLatin1String(":5 depends on non-NOTIFYable properties:");
5090     QString expected2 = QLatin1String("    ") +
5091                         QLatin1String(object->metaObject()->className()) +
5092                         QLatin1String("::value");
5093
5094     QCOMPARE(messages.length(), 2);
5095     QCOMPARE(messages.at(0), expected1);
5096     QCOMPARE(messages.at(1), expected2);
5097
5098     delete object;
5099 }
5100
5101 void tst_qdeclarativeecmascript::forInLoop()
5102 {
5103     QDeclarativeComponent component(&engine, TEST_FILE("forInLoop.qml"));
5104     QObject *object = component.create();
5105     QVERIFY(object != 0);
5106
5107     QMetaObject::invokeMethod(object, "listProperty");
5108
5109     QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
5110     QCOMPARE(r.size(), 3);
5111     QCOMPARE(r[0],QLatin1String("0=obj1"));
5112     QCOMPARE(r[1],QLatin1String("1=obj2"));
5113     QCOMPARE(r[2],QLatin1String("2=obj3"));
5114
5115     //TODO: should test for in loop for other objects (such as QObjects) as well.
5116
5117     delete object;
5118 }
5119
5120 // An object the binding depends on is deleted while the binding is still running
5121 void tst_qdeclarativeecmascript::deleteWhileBindingRunning()
5122 {
5123     QDeclarativeComponent component(&engine, TEST_FILE("deleteWhileBindingRunning.qml"));
5124     QObject *object = component.create();
5125     QVERIFY(object != 0);
5126     delete object;
5127 }
5128
5129 QTEST_MAIN(tst_qdeclarativeecmascript)
5130
5131 #include "tst_qdeclarativeecmascript.moc"