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