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