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