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