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