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