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