V8: Remove extra V8::Context allocated for expressing strong references
[profile/ivi/qtdeclarative.git] / tests / auto / declarative / qdeclarativeecmascript / tst_qdeclarativeecmascript.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include <QtTest/QtTest>
42 #include <QtDeclarative/qdeclarativecomponent.h>
43 #include <QtDeclarative/qdeclarativeengine.h>
44 #include <QtDeclarative/qdeclarativeexpression.h>
45 #include <QtDeclarative/qdeclarativecontext.h>
46 #include <QtCore/qfileinfo.h>
47 #include <QtCore/qdebug.h>
48 #include <QtDeclarative/private/qdeclarativeguard_p.h>
49 #include <QtCore/qdir.h>
50 #include <QtCore/qnumeric.h>
51 #include <private/qdeclarativeengine_p.h>
52 #include <private/qdeclarativevmemetaobject_p.h>
53 #include <private/qv4compiler_p.h>
54 #include "testtypes.h"
55 #include "testhttpserver.h"
56 #include "../../shared/util.h"
57
58 /*
59 This test covers evaluation of ECMAScript expressions and bindings from within
60 QML.  This does not include static QML language issues.
61
62 Static QML language issues are covered in qmllanguage
63 */
64 inline QUrl TEST_FILE(const QString &filename)
65 {
66     return QUrl::fromLocalFile(TESTDATA(filename));
67 }
68
69 inline QUrl TEST_FILE(const char *filename)
70 {
71     return TEST_FILE(QLatin1String(filename));
72 }
73
74 class tst_qdeclarativeecmascript : public QObject
75 {
76     Q_OBJECT
77 public:
78     tst_qdeclarativeecmascript() {}
79
80 private slots:
81     void initTestCase();
82     void assignBasicTypes();
83     void idShortcutInvalidates();
84     void boolPropertiesEvaluateAsBool();
85     void methods();
86     void signalAssignment();
87     void bindingLoop();
88     void basicExpressions();
89     void basicExpressions_data();
90     void arrayExpressions();
91     void contextPropertiesTriggerReeval();
92     void objectPropertiesTriggerReeval();
93     void deferredProperties();
94     void deferredPropertiesErrors();
95     void extensionObjects();
96     void overrideExtensionProperties();
97     void attachedProperties();
98     void enums();
99     void valueTypeFunctions();
100     void constantsOverrideBindings();
101     void outerBindingOverridesInnerBinding();
102     void aliasPropertyAndBinding();
103     void aliasPropertyReset();
104     void nonExistentAttachedObject();
105     void scope();
106     void importScope();
107     void signalParameterTypes();
108     void objectsCompareAsEqual();
109     void dynamicCreation_data();
110     void dynamicCreation();
111     void dynamicDestruction();
112     void objectToString();
113     void objectHasOwnProperty();
114     void selfDeletingBinding();
115     void extendedObjectPropertyLookup();
116     void extendedObjectPropertyLookup2();
117     void scriptErrors();
118     void functionErrors();
119     void propertyAssignmentErrors();
120     void signalTriggeredBindings();
121     void listProperties();
122     void exceptionClearsOnReeval();
123     void exceptionSlotProducesWarning();
124     void exceptionBindingProducesWarning();
125     void transientErrors();
126     void shutdownErrors();
127     void compositePropertyType();
128     void jsObject();
129     void undefinedResetsProperty();
130     void listToVariant();
131     void listAssignment();
132     void multiEngineObject();
133     void deletedObject();
134     void attachedPropertyScope();
135     void scriptConnect();
136     void scriptDisconnect();
137     void ownership();
138     void cppOwnershipReturnValue();
139     void ownershipCustomReturnValue();
140     void qlistqobjectMethods();
141     void strictlyEquals();
142     void compiled();
143     void numberAssignment();
144     void propertySplicing();
145     void signalWithUnknownTypes();
146     void signalWithJSValueInVariant_data();
147     void signalWithJSValueInVariant();
148     void signalWithJSValueInVariant_twoEngines_data();
149     void signalWithJSValueInVariant_twoEngines();
150     void signalWithQJSValue_data();
151     void signalWithQJSValue();
152     void moduleApi_data();
153     void moduleApi();
154     void importScripts_data();
155     void importScripts();
156     void scarceResources();
157     void scarceResources_data();
158     void scarceResources_other();
159     void propertyChangeSlots();
160     void propertyVar_data();
161     void propertyVar();
162     void propertyVarCpp();
163     void propertyVarOwnership();
164     void propertyVarImplicitOwnership();
165     void propertyVarReparent();
166     void propertyVarReparentNullContext();
167     void propertyVarCircular();
168     void propertyVarCircular2();
169     void propertyVarInheritance();
170     void propertyVarInheritance2();
171     void elementAssign();
172     void objectPassThroughSignals();
173     void objectConversion();
174     void booleanConversion();
175     void handleReferenceManagement();
176     void stringArg();
177     void readonlyDeclaration();
178     void sequenceConversionRead();
179     void sequenceConversionWrite();
180     void sequenceConversionArray();
181     void sequenceConversionThreads();
182     void sequenceConversionBindings();
183     void sequenceConversionCopy();
184     void assignSequenceTypes();
185     void qtbug_22464();
186     void qtbug_21580();
187
188     void bug1();
189     void bug2();
190     void dynamicCreationCrash();
191     void dynamicCreationOwnership();
192     void regExpBug();
193     void nullObjectBinding();
194     void deletedEngine();
195     void libraryScriptAssert();
196     void variantsAssignedUndefined();
197     void qtbug_9792();
198     void qtcreatorbug_1289();
199     void noSpuriousWarningsAtShutdown();
200     void canAssignNullToQObject();
201     void functionAssignment_fromBinding();
202     void functionAssignment_fromJS();
203     void functionAssignment_fromJS_data();
204     void functionAssignmentfromJS_invalid();
205     void eval();
206     void function();
207     void qtbug_10696();
208     void qtbug_11606();
209     void qtbug_11600();
210     void qtbug_21864();
211     void nonscriptable();
212     void deleteLater();
213     void in();
214     void typeOf();
215     void sharedAttachedObject();
216     void objectName();
217     void writeRemovesBinding();
218     void aliasBindingsAssignCorrectly();
219     void aliasBindingsOverrideTarget();
220     void aliasWritesOverrideBindings();
221     void aliasToCompositeElement();
222     void realToInt();
223     void urlProperty();
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(ico5->metaObject()->indexOfProperty("circ")));
4007         ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")));
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->setEngine(&hrmEngine);
4133         cro->setDtorCount(&dtorCount);
4134         QMetaObject::invokeMethod(object, "createReference");
4135         gc(engine);
4136         QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
4137         delete object;
4138         hrmEngine.collectGarbage();
4139         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
4140         QCOMPARE(dtorCount, 3);
4141     }
4142
4143     dtorCount = 0;
4144     {
4145         // Circular QObject reference
4146         QDeclarativeEngine hrmEngine;
4147         QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.object.2.qml"));
4148         QObject *object = component.create();
4149         QVERIFY(object != 0);
4150         CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4151         cro->setEngine(&hrmEngine);
4152         cro->setDtorCount(&dtorCount);
4153         QMetaObject::invokeMethod(object, "circularReference");
4154         gc(engine);
4155         QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
4156         delete object;
4157         hrmEngine.collectGarbage();
4158         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
4159         QCOMPARE(dtorCount, 3);
4160     }
4161
4162     dtorCount = 0;
4163     {
4164         // Linear handle reference
4165         QDeclarativeEngine hrmEngine;
4166         QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.handle.1.qml"));
4167         QObject *object = component.create();
4168         QVERIFY(object != 0);
4169         CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4170         QVERIFY(crh != 0);
4171         crh->setEngine(&hrmEngine);
4172         crh->setDtorCount(&dtorCount);
4173         QMetaObject::invokeMethod(object, "createReference");
4174         CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4175         CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4176         QVERIFY(first != 0);
4177         QVERIFY(second != 0);
4178         first->addReference(QDeclarativeData::get(second)->v8object); // create reference
4179         // now we have to reparent second and make second owned by JS.
4180         second->setParent(0);
4181         QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership);
4182         gc(engine);
4183         QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
4184         delete object;
4185         hrmEngine.collectGarbage();
4186         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
4187         QCOMPARE(dtorCount, 3);
4188     }
4189
4190     dtorCount = 0;
4191     {
4192         // Circular handle reference
4193         QDeclarativeEngine hrmEngine;
4194         QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.handle.2.qml"));
4195         QObject *object = component.create();
4196         QVERIFY(object != 0);
4197         CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4198         QVERIFY(crh != 0);
4199         crh->setEngine(&hrmEngine);
4200         crh->setDtorCount(&dtorCount);
4201         QMetaObject::invokeMethod(object, "circularReference");
4202         CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4203         CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4204         QVERIFY(first != 0);
4205         QVERIFY(second != 0);
4206         first->addReference(QDeclarativeData::get(second)->v8object); // create circular reference
4207         second->addReference(QDeclarativeData::get(first)->v8object); // note: must be weak.
4208         // now we have to reparent and change ownership.
4209         first->setParent(0);
4210         second->setParent(0);
4211         QDeclarativeEngine::setObjectOwnership(first, QDeclarativeEngine::JavaScriptOwnership);
4212         QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership);
4213         gc(engine);
4214         QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
4215         delete object;
4216         hrmEngine.collectGarbage();
4217         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
4218         QCOMPARE(dtorCount, 3);
4219     }
4220
4221     dtorCount = 0;
4222     {
4223         // multiple engine interaction - linear reference
4224         QDeclarativeEngine hrmEngine1;
4225         QDeclarativeEngine hrmEngine2;
4226         QDeclarativeComponent component1(&hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
4227         QDeclarativeComponent component2(&hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
4228         QObject *object1 = component1.create();
4229         QObject *object2 = component2.create();
4230         QVERIFY(object1 != 0);
4231         QVERIFY(object2 != 0);
4232         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4233         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4234         QVERIFY(crh1 != 0);
4235         QVERIFY(crh2 != 0);
4236         crh1->setEngine(&hrmEngine1);
4237         crh2->setEngine(&hrmEngine2);
4238         crh1->setDtorCount(&dtorCount);
4239         crh2->setDtorCount(&dtorCount);
4240         QMetaObject::invokeMethod(object1, "createReference");
4241         QMetaObject::invokeMethod(object2, "createReference");
4242         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4243         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4244         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4245         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4246         QVERIFY(first1 != 0);
4247         QVERIFY(second1 != 0);
4248         QVERIFY(first2 != 0);
4249         QVERIFY(second2 != 0);
4250         first1->addReference(QDeclarativeData::get(second2)->v8object); // create reference across engines
4251         // now we have to reparent second2 and make second2 owned by JS.
4252         second2->setParent(0);
4253         QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
4254         gc(engine);
4255         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
4256         QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
4257         delete object1;
4258         delete object2;
4259         hrmEngine1.collectGarbage();
4260         hrmEngine2.collectGarbage();
4261         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
4262         QCOMPARE(dtorCount, 6);
4263     }
4264
4265     dtorCount = 0;
4266     {
4267         // multiple engine interaction - circular reference
4268         QDeclarativeEngine hrmEngine1;
4269         QDeclarativeEngine hrmEngine2;
4270         QDeclarativeComponent component1(&hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
4271         QDeclarativeComponent component2(&hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
4272         QObject *object1 = component1.create();
4273         QObject *object2 = component2.create();
4274         QVERIFY(object1 != 0);
4275         QVERIFY(object2 != 0);
4276         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4277         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4278         QVERIFY(crh1 != 0);
4279         QVERIFY(crh2 != 0);
4280         crh1->setEngine(&hrmEngine1);
4281         crh2->setEngine(&hrmEngine2);
4282         crh1->setDtorCount(&dtorCount);
4283         crh2->setDtorCount(&dtorCount);
4284         QMetaObject::invokeMethod(object1, "createReference");
4285         QMetaObject::invokeMethod(object2, "createReference");
4286         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4287         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4288         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4289         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4290         QVERIFY(first1 != 0);
4291         QVERIFY(second1 != 0);
4292         QVERIFY(first2 != 0);
4293         QVERIFY(second2 != 0);
4294         first1->addReference(QDeclarativeData::get(second1)->v8object);  // create linear reference within engine1
4295         second1->addReference(QDeclarativeData::get(second2)->v8object); // create linear reference across engines
4296         second2->addReference(QDeclarativeData::get(first2)->v8object);  // create linear reference within engine2
4297         first2->addReference(QDeclarativeData::get(first1)->v8object);   // close the loop - circular ref across engines
4298         // now we have to reparent and change ownership to JS.
4299         first1->setParent(0);
4300         second1->setParent(0);
4301         first2->setParent(0);
4302         second2->setParent(0);
4303         QDeclarativeEngine::setObjectOwnership(first1, QDeclarativeEngine::JavaScriptOwnership);
4304         QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership);
4305         QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership);
4306         QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
4307         gc(engine);
4308         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
4309         QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
4310         delete object1;
4311         delete object2;
4312         hrmEngine1.collectGarbage();
4313         hrmEngine2.collectGarbage();
4314         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
4315         QCOMPARE(dtorCount, 6);
4316     }
4317
4318     dtorCount = 0;
4319     {
4320         // multiple engine interaction - linear reference with engine deletion
4321         QDeclarativeEngine *hrmEngine1 = new QDeclarativeEngine;
4322         QDeclarativeEngine *hrmEngine2 = new QDeclarativeEngine;
4323         QDeclarativeComponent component1(hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
4324         QDeclarativeComponent component2(hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
4325         QObject *object1 = component1.create();
4326         QObject *object2 = component2.create();
4327         QVERIFY(object1 != 0);
4328         QVERIFY(object2 != 0);
4329         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4330         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4331         QVERIFY(crh1 != 0);
4332         QVERIFY(crh2 != 0);
4333         crh1->setEngine(hrmEngine1);
4334         crh2->setEngine(hrmEngine2);
4335         crh1->setDtorCount(&dtorCount);
4336         crh2->setDtorCount(&dtorCount);
4337         QMetaObject::invokeMethod(object1, "createReference");
4338         QMetaObject::invokeMethod(object2, "createReference");
4339         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4340         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4341         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4342         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4343         QVERIFY(first1 != 0);
4344         QVERIFY(second1 != 0);
4345         QVERIFY(first2 != 0);
4346         QVERIFY(second2 != 0);
4347         first1->addReference(QDeclarativeData::get(second1)->v8object);  // create linear reference within engine1
4348         second1->addReference(QDeclarativeData::get(second2)->v8object); // create linear reference across engines
4349         second2->addReference(QDeclarativeData::get(first2)->v8object);  // create linear reference within engine2
4350         // now we have to reparent and change ownership to JS.
4351         first1->setParent(crh1);
4352         second1->setParent(0);
4353         first2->setParent(0);
4354         second2->setParent(0);
4355         QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership);
4356         QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership);
4357         QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
4358         gc(engine);
4359         QCOMPARE(dtorCount, 0);
4360         delete hrmEngine2;
4361         gc(engine);
4362         QCOMPARE(dtorCount, 0);
4363         delete object1;
4364         delete object2;
4365         hrmEngine1->collectGarbage();
4366         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
4367         QCOMPARE(dtorCount, 6);
4368         delete hrmEngine1;
4369     }
4370 }
4371
4372 void tst_qdeclarativeecmascript::stringArg()
4373 {
4374     QDeclarativeComponent component(&engine, TEST_FILE("stringArg.qml"));
4375     QObject *object = component.create();
4376     QVERIFY(object != 0);
4377     QMetaObject::invokeMethod(object, "success");
4378     QVERIFY(object->property("returnValue").toBool());
4379
4380     QString w1 = TEST_FILE("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
4381     QTest::ignoreMessage(QtWarningMsg, w1.toAscii().constData());
4382     QMetaObject::invokeMethod(object, "failure");
4383     QVERIFY(object->property("returnValue").toBool());
4384
4385     delete object;
4386 }
4387
4388 void tst_qdeclarativeecmascript::readonlyDeclaration()
4389 {
4390     QDeclarativeComponent component(&engine, TEST_FILE("readonlyDeclaration.qml"));
4391
4392     QObject *object = component.create();
4393     QVERIFY(object != 0);
4394
4395     QCOMPARE(object->property("test").toBool(), true);
4396
4397     delete object;
4398 }
4399
4400 Q_DECLARE_METATYPE(QList<int>)
4401 Q_DECLARE_METATYPE(QList<qreal>)
4402 Q_DECLARE_METATYPE(QList<bool>)
4403 Q_DECLARE_METATYPE(QList<QString>)
4404 Q_DECLARE_METATYPE(QList<QUrl>)
4405 void tst_qdeclarativeecmascript::sequenceConversionRead()
4406 {
4407     {
4408         QUrl qmlFile = TEST_FILE("sequenceConversion.read.qml");
4409         QDeclarativeComponent component(&engine, qmlFile);
4410         QObject *object = component.create();
4411         QVERIFY(object != 0);
4412         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4413         QVERIFY(seq != 0);
4414
4415         QMetaObject::invokeMethod(object, "readSequences");
4416         QList<int> intList; intList << 1 << 2 << 3 << 4;
4417         QCOMPARE(object->property("intListLength").toInt(), intList.length());
4418         QCOMPARE(object->property("intList").value<QList<int> >(), intList);
4419         QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
4420         QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
4421         QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
4422         QList<bool> boolList; boolList << true << false << true << false;
4423         QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
4424         QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
4425         QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
4426         QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
4427         QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
4428         QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
4429         QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
4430         QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
4431         QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
4432         QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
4433         QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
4434
4435         QMetaObject::invokeMethod(object, "readSequenceElements");
4436         QCOMPARE(object->property("intVal").toInt(), 2);
4437         QCOMPARE(object->property("qrealVal").toReal(), 2.2);
4438         QCOMPARE(object->property("boolVal").toBool(), false);
4439         QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
4440         QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
4441         QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
4442
4443         QMetaObject::invokeMethod(object, "enumerateSequenceElements");
4444         QCOMPARE(object->property("enumerationMatches").toBool(), true);
4445
4446         intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
4447         QDeclarativeProperty seqProp(seq, "intListProperty");
4448         QCOMPARE(seqProp.read().value<QList<int> >(), intList);
4449         QDeclarativeProperty seqProp2(seq, "intListProperty", &engine);
4450         QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
4451
4452         QMetaObject::invokeMethod(object, "testReferenceDeletion");
4453         QCOMPARE(object->property("referenceDeletion").toBool(), true);
4454
4455         delete object;
4456     }
4457
4458     {
4459         QUrl qmlFile = TEST_FILE("sequenceConversion.read.error.qml");
4460         QDeclarativeComponent component(&engine, qmlFile);
4461         QObject *object = component.create();
4462         QVERIFY(object != 0);
4463         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4464         QVERIFY(seq != 0);
4465
4466         // we haven't registered QList<QPoint> as a sequence type.
4467         QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
4468         QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
4469         QTest::ignoreMessage(QtWarningMsg, warningOne.toAscii().constData());
4470         QTest::ignoreMessage(QtWarningMsg, warningTwo.toAscii().constData());
4471
4472         QMetaObject::invokeMethod(object, "performTest");
4473
4474         // QList<QPoint> has not been registered as a sequence type.
4475         QCOMPARE(object->property("pointListLength").toInt(), 0);
4476         QVERIFY(!object->property("pointList").isValid());
4477         QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
4478         QDeclarativeProperty seqProp(seq, "pointListProperty", &engine);
4479         QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
4480
4481         delete object;
4482     }
4483 }
4484
4485 void tst_qdeclarativeecmascript::sequenceConversionWrite()
4486 {
4487     {
4488         QUrl qmlFile = TEST_FILE("sequenceConversion.write.qml");
4489         QDeclarativeComponent component(&engine, qmlFile);
4490         QObject *object = component.create();
4491         QVERIFY(object != 0);
4492         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4493         QVERIFY(seq != 0);
4494
4495         QMetaObject::invokeMethod(object, "writeSequences");
4496         QCOMPARE(object->property("success").toBool(), true);
4497
4498         QMetaObject::invokeMethod(object, "writeSequenceElements");
4499         QCOMPARE(object->property("success").toBool(), true);
4500
4501         QMetaObject::invokeMethod(object, "writeOtherElements");
4502         QCOMPARE(object->property("success").toBool(), true);
4503
4504         QMetaObject::invokeMethod(object, "testReferenceDeletion");
4505         QCOMPARE(object->property("referenceDeletion").toBool(), true);
4506
4507         delete object;
4508     }
4509
4510     {
4511         QUrl qmlFile = TEST_FILE("sequenceConversion.write.error.qml");
4512         QDeclarativeComponent component(&engine, qmlFile);
4513         QObject *object = component.create();
4514         QVERIFY(object != 0);
4515         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4516         QVERIFY(seq != 0);
4517
4518         // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
4519         QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to void");
4520         QTest::ignoreMessage(QtWarningMsg, warningOne.toAscii().constData());
4521
4522         QMetaObject::invokeMethod(object, "performTest");
4523
4524         QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
4525         QCOMPARE(seq->pointListProperty(), pointList);
4526
4527         delete object;
4528     }
4529 }
4530
4531 void tst_qdeclarativeecmascript::sequenceConversionArray()
4532 {
4533     // ensure that in JS the returned sequences act just like normal JS Arrays.
4534     QUrl qmlFile = TEST_FILE("sequenceConversion.array.qml");
4535     QDeclarativeComponent component(&engine, qmlFile);
4536     QObject *object = component.create();
4537     QVERIFY(object != 0);
4538     QMetaObject::invokeMethod(object, "indexedAccess");
4539     QVERIFY(object->property("success").toBool());
4540     QMetaObject::invokeMethod(object, "arrayOperations");
4541     QVERIFY(object->property("success").toBool());
4542     QMetaObject::invokeMethod(object, "testEqualitySemantics");
4543     QVERIFY(object->property("success").toBool());
4544     QMetaObject::invokeMethod(object, "testReferenceDeletion");
4545     QCOMPARE(object->property("referenceDeletion").toBool(), true);
4546     delete object;
4547 }
4548
4549 void tst_qdeclarativeecmascript::sequenceConversionThreads()
4550 {
4551     // ensure that sequence conversion operations work correctly in a worker thread
4552     // and that serialisation between the main and worker thread succeeds.
4553     QUrl qmlFile = TEST_FILE("sequenceConversion.threads.qml");
4554     QDeclarativeComponent component(&engine, qmlFile);
4555     QObject *object = component.create();
4556     QVERIFY(object != 0);
4557
4558     QMetaObject::invokeMethod(object, "testIntSequence");
4559     QTRY_VERIFY(object->property("finished").toBool());
4560     QVERIFY(object->property("success").toBool());
4561
4562     QMetaObject::invokeMethod(object, "testQrealSequence");
4563     QTRY_VERIFY(object->property("finished").toBool());
4564     QVERIFY(object->property("success").toBool());
4565
4566     QMetaObject::invokeMethod(object, "testBoolSequence");
4567     QTRY_VERIFY(object->property("finished").toBool());
4568     QVERIFY(object->property("success").toBool());
4569
4570     QMetaObject::invokeMethod(object, "testStringSequence");
4571     QTRY_VERIFY(object->property("finished").toBool());
4572     QVERIFY(object->property("success").toBool());
4573
4574     QMetaObject::invokeMethod(object, "testQStringSequence");
4575     QTRY_VERIFY(object->property("finished").toBool());
4576     QVERIFY(object->property("success").toBool());
4577
4578     QMetaObject::invokeMethod(object, "testUrlSequence");
4579     QTRY_VERIFY(object->property("finished").toBool());
4580     QVERIFY(object->property("success").toBool());
4581
4582     QMetaObject::invokeMethod(object, "testVariantSequence");
4583     QTRY_VERIFY(object->property("finished").toBool());
4584     QVERIFY(object->property("success").toBool());
4585
4586     delete object;
4587 }
4588
4589 void tst_qdeclarativeecmascript::sequenceConversionBindings()
4590 {
4591     {
4592         QUrl qmlFile = TEST_FILE("sequenceConversion.bindings.qml");
4593         QDeclarativeComponent component(&engine, qmlFile);
4594         QObject *object = component.create();
4595         QVERIFY(object != 0);
4596         QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
4597         QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
4598         QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
4599         QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
4600         QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
4601         delete object;
4602     }
4603
4604     {
4605         QUrl qmlFile = TEST_FILE("sequenceConversion.bindings.error.qml");
4606         QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
4607         QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
4608         QDeclarativeComponent component(&engine, qmlFile);
4609         QObject *object = component.create();
4610         QVERIFY(object != 0);
4611         delete object;
4612     }
4613 }
4614
4615 void tst_qdeclarativeecmascript::sequenceConversionCopy()
4616 {
4617     QUrl qmlFile = TEST_FILE("sequenceConversion.copy.qml");
4618     QDeclarativeComponent component(&engine, qmlFile);
4619     QObject *object = component.create();
4620     QVERIFY(object != 0);
4621     QMetaObject::invokeMethod(object, "testCopySequences");
4622     QCOMPARE(object->property("success").toBool(), true);
4623     QMetaObject::invokeMethod(object, "readSequenceCopyElements");
4624     QCOMPARE(object->property("success").toBool(), true);
4625     QMetaObject::invokeMethod(object, "testEqualitySemantics");
4626     QCOMPARE(object->property("success").toBool(), true);
4627     delete object;
4628 }
4629
4630 void tst_qdeclarativeecmascript::assignSequenceTypes()
4631 {
4632     // test binding array to sequence type property
4633     {
4634     QDeclarativeComponent component(&engine, TEST_FILE("assignSequenceTypes.1.qml"));
4635     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
4636     QVERIFY(object != 0);
4637     QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
4638     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
4639     QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
4640     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
4641     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
4642     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
4643     delete object;
4644     }
4645
4646     // test binding literal to sequence type property
4647     {
4648     QDeclarativeComponent component(&engine, TEST_FILE("assignSequenceTypes.2.qml"));
4649     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
4650     QVERIFY(object != 0);
4651     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
4652     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
4653     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
4654     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
4655     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
4656     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
4657     delete object;
4658     }
4659
4660     // test binding single value to sequence type property
4661     {
4662     QDeclarativeComponent component(&engine, TEST_FILE("assignSequenceTypes.3.qml"));
4663     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
4664     QVERIFY(object != 0);
4665     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
4666     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
4667     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
4668     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(TEST_FILE("example.html"))));
4669     delete object;
4670     }
4671
4672     // test assigning array to sequence type property in js function
4673     {
4674     QDeclarativeComponent component(&engine, TEST_FILE("assignSequenceTypes.4.qml"));
4675     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
4676     QVERIFY(object != 0);
4677     QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
4678     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
4679     QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
4680     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
4681     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
4682     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
4683     delete object;
4684     }
4685
4686     // test assigning literal to sequence type property in js function
4687     {
4688     QDeclarativeComponent component(&engine, TEST_FILE("assignSequenceTypes.5.qml"));
4689     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
4690     QVERIFY(object != 0);
4691     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
4692     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
4693     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
4694     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
4695     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
4696     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
4697     delete object;
4698     }
4699
4700     // test assigning single value to sequence type property in js function
4701     {
4702     QDeclarativeComponent component(&engine, TEST_FILE("assignSequenceTypes.6.qml"));
4703     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
4704     QVERIFY(object != 0);
4705     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
4706     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
4707     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
4708     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(TEST_FILE("example.html"))));
4709     delete object;
4710     }
4711 }
4712
4713 // Test that assigning a null object works 
4714 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
4715 void tst_qdeclarativeecmascript::nullObjectBinding()
4716 {
4717     QDeclarativeComponent component(&engine, TEST_FILE("nullObjectBinding.qml"));
4718
4719     QObject *object = component.create();
4720     QVERIFY(object != 0);
4721
4722     QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
4723
4724     delete object;
4725 }
4726
4727 // Test that bindings don't evaluate once the engine has been destroyed
4728 void tst_qdeclarativeecmascript::deletedEngine()
4729 {
4730     QDeclarativeEngine *engine = new QDeclarativeEngine;
4731     QDeclarativeComponent component(engine, TEST_FILE("deletedEngine.qml"));
4732
4733     QObject *object = component.create();
4734     QVERIFY(object != 0);
4735
4736     QCOMPARE(object->property("a").toInt(), 39);
4737     object->setProperty("b", QVariant(9));
4738     QCOMPARE(object->property("a").toInt(), 117);
4739
4740     delete engine;
4741
4742     QCOMPARE(object->property("a").toInt(), 117);
4743     object->setProperty("b", QVariant(10));
4744     QCOMPARE(object->property("a").toInt(), 117);
4745
4746     delete object;
4747 }
4748
4749 // Test the crashing part of QTBUG-9705
4750 void tst_qdeclarativeecmascript::libraryScriptAssert()
4751 {
4752     QDeclarativeComponent component(&engine, TEST_FILE("libraryScriptAssert.qml"));
4753
4754     QObject *object = component.create();
4755     QVERIFY(object != 0);
4756
4757     delete object;
4758 }
4759
4760 void tst_qdeclarativeecmascript::variantsAssignedUndefined()
4761 {
4762     QDeclarativeComponent component(&engine, TEST_FILE("variantsAssignedUndefined.qml"));
4763
4764     QObject *object = component.create();
4765     QVERIFY(object != 0);
4766
4767     QCOMPARE(object->property("test1").toInt(), 10);
4768     QCOMPARE(object->property("test2").toInt(), 11);
4769
4770     object->setProperty("runTest", true);
4771
4772     QCOMPARE(object->property("test1"), QVariant());
4773     QCOMPARE(object->property("test2"), QVariant());
4774
4775
4776     delete object;
4777 }
4778
4779 void tst_qdeclarativeecmascript::qtbug_9792()
4780 {
4781     QDeclarativeComponent component(&engine, TEST_FILE("qtbug_9792.qml"));
4782
4783     QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
4784
4785     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
4786     QVERIFY(object != 0);
4787
4788     QTest::ignoreMessage(QtDebugMsg, "Hello world!");
4789     object->basicSignal();
4790
4791     delete context;
4792
4793     transientErrorsMsgCount = 0;
4794     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
4795
4796     object->basicSignal();
4797     
4798     qInstallMsgHandler(old);
4799
4800     QCOMPARE(transientErrorsMsgCount, 0);
4801
4802     delete object;
4803 }
4804
4805 // Verifies that QDeclarativeGuard<>s used in the vmemetaobject are cleaned correctly
4806 void tst_qdeclarativeecmascript::qtcreatorbug_1289()
4807 {
4808     QDeclarativeComponent component(&engine, TEST_FILE("qtcreatorbug_1289.qml"));
4809
4810     QObject *o = component.create();
4811     QVERIFY(o != 0);
4812
4813     QObject *nested = qvariant_cast<QObject *>(o->property("object"));
4814     QVERIFY(nested != 0);
4815
4816     QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
4817
4818     delete nested;
4819     nested = qvariant_cast<QObject *>(o->property("object"));
4820     QVERIFY(nested == 0);
4821
4822     // If the bug is present, the next line will crash
4823     delete o;
4824 }
4825
4826 // Test that we shut down without stupid warnings
4827 void tst_qdeclarativeecmascript::noSpuriousWarningsAtShutdown()
4828 {
4829     {
4830     QDeclarativeComponent component(&engine, TEST_FILE("noSpuriousWarningsAtShutdown.qml"));
4831
4832     QObject *o = component.create();
4833
4834     transientErrorsMsgCount = 0;
4835     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
4836
4837     delete o;
4838
4839     qInstallMsgHandler(old);
4840
4841     QCOMPARE(transientErrorsMsgCount, 0);
4842     }
4843
4844
4845     {
4846     QDeclarativeComponent component(&engine, TEST_FILE("noSpuriousWarningsAtShutdown.2.qml"));
4847
4848     QObject *o = component.create();
4849
4850     transientErrorsMsgCount = 0;
4851     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
4852
4853     delete o;
4854
4855     qInstallMsgHandler(old);
4856
4857     QCOMPARE(transientErrorsMsgCount, 0);
4858     }
4859 }
4860
4861 void tst_qdeclarativeecmascript::canAssignNullToQObject()
4862 {
4863     {
4864     QDeclarativeComponent component(&engine, TEST_FILE("canAssignNullToQObject.1.qml"));
4865
4866     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4867     QVERIFY(o != 0);
4868
4869     QVERIFY(o->objectProperty() != 0);
4870
4871     o->setProperty("runTest", true);
4872
4873     QVERIFY(o->objectProperty() == 0);
4874
4875     delete o;
4876     }
4877
4878     {
4879     QDeclarativeComponent component(&engine, TEST_FILE("canAssignNullToQObject.2.qml"));
4880
4881     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4882     QVERIFY(o != 0);
4883
4884     QVERIFY(o->objectProperty() == 0);
4885
4886     delete o;
4887     }
4888 }
4889
4890 void tst_qdeclarativeecmascript::functionAssignment_fromBinding()
4891 {
4892     QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.1.qml"));
4893
4894     QString url = component.url().toString();
4895     QString warning = url + ":4: Unable to assign a function to a property.";
4896     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
4897     
4898     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4899     QVERIFY(o != 0);
4900
4901     QVERIFY(!o->property("a").isValid());
4902
4903     delete o;
4904 }
4905
4906 void tst_qdeclarativeecmascript::functionAssignment_fromJS()
4907 {
4908     QFETCH(QString, triggerProperty);
4909
4910     QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.2.qml"));
4911     QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
4912
4913     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4914     QVERIFY(o != 0);
4915     QVERIFY(!o->property("a").isValid());
4916
4917     o->setProperty("aNumber", QVariant(5));
4918     o->setProperty(triggerProperty.toUtf8().constData(), true);
4919     QCOMPARE(o->property("a"), QVariant(50));
4920
4921     o->setProperty("aNumber", QVariant(10));
4922     QCOMPARE(o->property("a"), QVariant(100));
4923
4924     delete o;
4925 }
4926
4927 void tst_qdeclarativeecmascript::functionAssignment_fromJS_data()
4928 {
4929     QTest::addColumn<QString>("triggerProperty");
4930
4931     QTest::newRow("assign to property") << "assignToProperty";
4932     QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
4933
4934     QTest::newRow("assign to value type") << "assignToValueType";
4935
4936     QTest::newRow("use 'this'") << "assignWithThis";
4937     QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
4938 }
4939
4940 void tst_qdeclarativeecmascript::functionAssignmentfromJS_invalid()
4941 {
4942     QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.2.qml"));
4943     QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
4944
4945     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4946     QVERIFY(o != 0);
4947     QVERIFY(!o->property("a").isValid());
4948
4949     o->setProperty("assignFuncWithoutReturn", true);
4950     QVERIFY(!o->property("a").isValid());
4951
4952     QString url = component.url().toString();
4953     QString warning = url + ":67: Unable to assign QString to int";
4954     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
4955     o->setProperty("assignWrongType", true);
4956
4957     warning = url + ":71: Unable to assign QString to int";
4958     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
4959     o->setProperty("assignWrongTypeToValueType", true);
4960
4961     delete o;
4962 }
4963
4964 void tst_qdeclarativeecmascript::eval()
4965 {
4966     QDeclarativeComponent component(&engine, TEST_FILE("eval.qml"));
4967
4968     QObject *o = component.create();
4969     QVERIFY(o != 0);
4970
4971     QCOMPARE(o->property("test1").toBool(), true);
4972     QCOMPARE(o->property("test2").toBool(), true);
4973     QCOMPARE(o->property("test3").toBool(), true);
4974     QCOMPARE(o->property("test4").toBool(), true);
4975     QCOMPARE(o->property("test5").toBool(), true);
4976
4977     delete o;
4978 }
4979
4980 void tst_qdeclarativeecmascript::function()
4981 {
4982     QDeclarativeComponent component(&engine, TEST_FILE("function.qml"));
4983
4984     QObject *o = component.create();
4985     QVERIFY(o != 0);
4986
4987     QCOMPARE(o->property("test1").toBool(), true);
4988     QCOMPARE(o->property("test2").toBool(), true);
4989     QCOMPARE(o->property("test3").toBool(), true);
4990
4991     delete o;
4992 }
4993
4994 // Test the "Qt.include" method
4995 void tst_qdeclarativeecmascript::include()
4996 {
4997     // Non-library relative include
4998     {
4999     QDeclarativeComponent component(&engine, TEST_FILE("include.qml"));
5000     QObject *o = component.create();
5001     QVERIFY(o != 0);
5002
5003     QCOMPARE(o->property("test0").toInt(), 99);
5004     QCOMPARE(o->property("test1").toBool(), true);
5005     QCOMPARE(o->property("test2").toBool(), true);
5006     QCOMPARE(o->property("test2_1").toBool(), true);
5007     QCOMPARE(o->property("test3").toBool(), true);
5008     QCOMPARE(o->property("test3_1").toBool(), true);
5009
5010     delete o;
5011     }
5012
5013     // Library relative include
5014     {
5015     QDeclarativeComponent component(&engine, TEST_FILE("include_shared.qml"));
5016     QObject *o = component.create();
5017     QVERIFY(o != 0);
5018
5019     QCOMPARE(o->property("test0").toInt(), 99);
5020     QCOMPARE(o->property("test1").toBool(), true);
5021     QCOMPARE(o->property("test2").toBool(), true);
5022     QCOMPARE(o->property("test2_1").toBool(), true);
5023     QCOMPARE(o->property("test3").toBool(), true);
5024     QCOMPARE(o->property("test3_1").toBool(), true);
5025
5026     delete o;
5027     }
5028
5029     // Callback
5030     {
5031     QDeclarativeComponent component(&engine, TEST_FILE("include_callback.qml"));
5032     QObject *o = component.create();
5033     QVERIFY(o != 0);
5034
5035     QCOMPARE(o->property("test1").toBool(), true);
5036     QCOMPARE(o->property("test2").toBool(), true);
5037     QCOMPARE(o->property("test3").toBool(), true);
5038     QCOMPARE(o->property("test4").toBool(), true);
5039     QCOMPARE(o->property("test5").toBool(), true);
5040     QCOMPARE(o->property("test6").toBool(), true);
5041
5042     delete o;
5043     }
5044
5045     // Including file with ".pragma library"
5046     {
5047     QDeclarativeComponent component(&engine, TEST_FILE("include_pragma.qml"));
5048     QObject *o = component.create();
5049     QVERIFY(o != 0);
5050     QCOMPARE(o->property("test1").toInt(), 100);
5051
5052     delete o;
5053     }
5054
5055     // Remote - success
5056     {
5057     TestHTTPServer server(8111);
5058     QVERIFY(server.isValid());
5059     server.serveDirectory(TESTDATA(""));
5060
5061     QDeclarativeComponent component(&engine, TEST_FILE("include_remote.qml"));
5062     QObject *o = component.create();
5063     QVERIFY(o != 0);
5064
5065     QTRY_VERIFY(o->property("done").toBool() == true);
5066     QTRY_VERIFY(o->property("done2").toBool() == true);
5067
5068     QCOMPARE(o->property("test1").toBool(), true);
5069     QCOMPARE(o->property("test2").toBool(), true);
5070     QCOMPARE(o->property("test3").toBool(), true);
5071     QCOMPARE(o->property("test4").toBool(), true);
5072     QCOMPARE(o->property("test5").toBool(), true);
5073
5074     QCOMPARE(o->property("test6").toBool(), true);
5075     QCOMPARE(o->property("test7").toBool(), true);
5076     QCOMPARE(o->property("test8").toBool(), true);
5077     QCOMPARE(o->property("test9").toBool(), true);
5078     QCOMPARE(o->property("test10").toBool(), true);
5079
5080     delete o;
5081     }
5082
5083     // Remote - error
5084     {
5085     TestHTTPServer server(8111);
5086     QVERIFY(server.isValid());
5087     server.serveDirectory(TESTDATA(""));
5088
5089     QDeclarativeComponent component(&engine, TEST_FILE("include_remote_missing.qml"));
5090     QObject *o = component.create();
5091     QVERIFY(o != 0);
5092
5093     QTRY_VERIFY(o->property("done").toBool() == true);
5094
5095     QCOMPARE(o->property("test1").toBool(), true);
5096     QCOMPARE(o->property("test2").toBool(), true);
5097     QCOMPARE(o->property("test3").toBool(), true);
5098
5099     delete o;
5100     }
5101 }
5102
5103 void tst_qdeclarativeecmascript::signalHandlers()
5104 {
5105     QDeclarativeComponent component(&engine, TEST_FILE("signalHandlers.qml"));
5106     QObject *o = component.create();
5107     QVERIFY(o != 0);
5108
5109     QVERIFY(o->property("count").toInt() == 0);
5110     QMetaObject::invokeMethod(o, "testSignalCall");
5111     QCOMPARE(o->property("count").toInt(), 1);
5112
5113     QMetaObject::invokeMethod(o, "testSignalHandlerCall");
5114     QCOMPARE(o->property("count").toInt(), 1);
5115     QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
5116
5117     QVERIFY(o->property("funcCount").toInt() == 0);
5118     QMetaObject::invokeMethod(o, "testSignalConnection");
5119     QCOMPARE(o->property("funcCount").toInt(), 1);
5120
5121     QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
5122     QCOMPARE(o->property("funcCount").toInt(), 2);
5123
5124     QMetaObject::invokeMethod(o, "testSignalDefined");
5125     QCOMPARE(o->property("definedResult").toBool(), true);
5126
5127     QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
5128     QCOMPARE(o->property("definedHandlerResult").toBool(), true);
5129
5130     delete o;
5131 }
5132
5133 void tst_qdeclarativeecmascript::qtbug_10696()
5134 {
5135     QDeclarativeComponent component(&engine, TEST_FILE("qtbug_10696.qml"));
5136     QObject *o = component.create();
5137     QVERIFY(o != 0);
5138     delete o;
5139 }
5140
5141 void tst_qdeclarativeecmascript::qtbug_11606()
5142 {
5143     QDeclarativeComponent component(&engine, TEST_FILE("qtbug_11606.qml"));
5144     QObject *o = component.create();
5145     QVERIFY(o != 0);
5146     QCOMPARE(o->property("test").toBool(), true);
5147     delete o;
5148 }
5149
5150 void tst_qdeclarativeecmascript::qtbug_11600()
5151 {
5152     QDeclarativeComponent component(&engine, TEST_FILE("qtbug_11600.qml"));
5153     QObject *o = component.create();
5154     QVERIFY(o != 0);
5155     QCOMPARE(o->property("test").toBool(), true);
5156     delete o;
5157 }
5158
5159 void tst_qdeclarativeecmascript::qtbug_21864()
5160 {
5161     QDeclarativeComponent component(&engine, TEST_FILE("qtbug_21864.qml"));
5162     QObject *o = component.create();
5163     QVERIFY(o != 0);
5164     QCOMPARE(o->property("test").toBool(), true);
5165     delete o;
5166 }
5167
5168 // Reading and writing non-scriptable properties should fail
5169 void tst_qdeclarativeecmascript::nonscriptable()
5170 {
5171     QDeclarativeComponent component(&engine, TEST_FILE("nonscriptable.qml"));
5172     QObject *o = component.create();
5173     QVERIFY(o != 0);
5174     QCOMPARE(o->property("readOk").toBool(), true);
5175     QCOMPARE(o->property("writeOk").toBool(), true);
5176     delete o;
5177 }
5178
5179 // deleteLater() should not be callable from QML
5180 void tst_qdeclarativeecmascript::deleteLater()
5181 {
5182     QDeclarativeComponent component(&engine, TEST_FILE("deleteLater.qml"));
5183     QObject *o = component.create();
5184     QVERIFY(o != 0);
5185     QCOMPARE(o->property("test").toBool(), true);
5186     delete o;
5187 }
5188
5189 void tst_qdeclarativeecmascript::in()
5190 {
5191     QDeclarativeComponent component(&engine, TEST_FILE("in.qml"));
5192     QObject *o = component.create();
5193     QVERIFY(o != 0);
5194     QCOMPARE(o->property("test1").toBool(), true);
5195     QCOMPARE(o->property("test2").toBool(), true);
5196     delete o;
5197 }
5198
5199 void tst_qdeclarativeecmascript::typeOf()
5200 {
5201     QDeclarativeComponent component(&engine, TEST_FILE("typeOf.qml"));
5202
5203     // These warnings should not happen once QTBUG-21864 is fixed
5204     QString warning1 = component.url().toString() + QLatin1String(":16: Error: Cannot assign [undefined] to QString");
5205     QString warning2 = component.url().resolved(QUrl("typeOf.js")).toString() + QLatin1String(":1: ReferenceError: Can't find variable: a");
5206
5207     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
5208     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
5209
5210     QObject *o = component.create();
5211     QVERIFY(o != 0);
5212
5213     QEXPECT_FAIL("", "QTBUG-21864", Abort);
5214     QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
5215     QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
5216     QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
5217     QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
5218     QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
5219     QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
5220     QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
5221     QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
5222     QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
5223
5224     delete o;
5225 }
5226
5227 void tst_qdeclarativeecmascript::sharedAttachedObject()
5228 {
5229     QDeclarativeComponent component(&engine, TEST_FILE("sharedAttachedObject.qml"));
5230     QObject *o = component.create();
5231     QVERIFY(o != 0);
5232     QCOMPARE(o->property("test1").toBool(), true);
5233     QCOMPARE(o->property("test2").toBool(), true);
5234     delete o;
5235 }
5236
5237 // QTBUG-13999
5238 void tst_qdeclarativeecmascript::objectName()
5239 {
5240     QDeclarativeComponent component(&engine, TEST_FILE("objectName.qml"));
5241     QObject *o = component.create();
5242     QVERIFY(o != 0);
5243
5244     QCOMPARE(o->property("test1").toString(), QString("hello"));
5245     QCOMPARE(o->property("test2").toString(), QString("ell"));
5246
5247     o->setObjectName("world");
5248
5249     QCOMPARE(o->property("test1").toString(), QString("world"));
5250     QCOMPARE(o->property("test2").toString(), QString("orl"));
5251
5252     delete o;
5253 }
5254
5255 void tst_qdeclarativeecmascript::writeRemovesBinding()
5256 {
5257     QDeclarativeComponent component(&engine, TEST_FILE("writeRemovesBinding.qml"));
5258     QObject *o = component.create();
5259     QVERIFY(o != 0);
5260
5261     QCOMPARE(o->property("test").toBool(), true);
5262
5263     delete o;
5264 }
5265
5266 // Test bindings assigned to alias properties actually assign to the alias' target
5267 void tst_qdeclarativeecmascript::aliasBindingsAssignCorrectly()
5268 {
5269     QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsAssignCorrectly.qml"));
5270     QObject *o = component.create();
5271     QVERIFY(o != 0);
5272
5273     QCOMPARE(o->property("test").toBool(), true);
5274
5275     delete o;
5276 }
5277
5278 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
5279 void tst_qdeclarativeecmascript::aliasBindingsOverrideTarget()
5280 {
5281     { 
5282     QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.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.2.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     QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.3.qml"));
5303     QObject *o = component.create();
5304     QVERIFY(o != 0);
5305
5306     QCOMPARE(o->property("test").toBool(), true);
5307
5308     delete o;
5309     }
5310 }
5311
5312 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
5313 void tst_qdeclarativeecmascript::aliasWritesOverrideBindings()
5314 {
5315     {
5316     QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.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.2.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     QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.3.qml"));
5337     QObject *o = component.create();
5338     QVERIFY(o != 0);
5339
5340     QCOMPARE(o->property("test").toBool(), true);
5341
5342     delete o;
5343     }
5344 }
5345
5346 // Allow an alais to a composite element
5347 // QTBUG-20200
5348 void tst_qdeclarativeecmascript::aliasToCompositeElement()
5349 {
5350     QDeclarativeComponent component(&engine, TEST_FILE("aliasToCompositeElement.qml"));
5351
5352     QObject *object = component.create();
5353     QVERIFY(object != 0);
5354
5355     delete object;
5356 }
5357
5358 void tst_qdeclarativeecmascript::qtbug_20344()
5359 {
5360     QDeclarativeComponent component(&engine, TEST_FILE("qtbug_20344.qml"));
5361
5362     QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
5363     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5364
5365     QObject *object = component.create();
5366     QVERIFY(object != 0);
5367
5368     delete object;
5369 }
5370
5371 void tst_qdeclarativeecmascript::revisionErrors()
5372 {
5373     {
5374         QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors.qml"));
5375         QString url = component.url().toString();
5376
5377         QString warning1 = url + ":8: ReferenceError: Can't find variable: prop2";
5378         QString warning2 = url + ":11: ReferenceError: Can't find variable: prop2";
5379         QString warning3 = url + ":13: ReferenceError: Can't find variable: method2";
5380
5381         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
5382         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
5383         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
5384         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5385         QVERIFY(object != 0);
5386         delete object;
5387     }
5388     {
5389         QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors2.qml"));
5390         QString url = component.url().toString();
5391
5392         // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
5393         // method2, prop2 from MyRevisionedClass not available
5394         // method4, prop4 from MyRevisionedSubclass not available
5395         QString warning1 = url + ":8: ReferenceError: Can't find variable: prop2";
5396         QString warning2 = url + ":14: ReferenceError: Can't find variable: prop2";
5397         QString warning3 = url + ":10: ReferenceError: Can't find variable: prop4";
5398         QString warning4 = url + ":16: ReferenceError: Can't find variable: prop4";
5399         QString warning5 = url + ":20: ReferenceError: Can't find variable: method2";
5400
5401         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
5402         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
5403         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
5404         QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
5405         QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
5406         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5407         QVERIFY(object != 0);
5408         delete object;
5409     }
5410     {
5411         QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors3.qml"));
5412         QString url = component.url().toString();
5413
5414         // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
5415         // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
5416         QString warning1 = url + ":30: ReferenceError: Can't find variable: methodD";
5417         QString warning2 = url + ":10: ReferenceError: Can't find variable: propD";
5418         QString warning3 = url + ":20: ReferenceError: Can't find variable: propD";
5419         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
5420         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
5421         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
5422         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5423         QVERIFY(object != 0);
5424         delete object;
5425     }
5426 }
5427
5428 void tst_qdeclarativeecmascript::revision()
5429 {
5430     {
5431         QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision.qml"));
5432         QString url = component.url().toString();
5433
5434         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5435         QVERIFY(object != 0);
5436         delete object;
5437     }
5438     {
5439         QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision2.qml"));
5440         QString url = component.url().toString();
5441
5442         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5443         QVERIFY(object != 0);
5444         delete object;
5445     }
5446     {
5447         QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision3.qml"));
5448         QString url = component.url().toString();
5449
5450         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5451         QVERIFY(object != 0);
5452         delete object;
5453     }
5454     // Test that non-root classes can resolve revisioned methods
5455     {
5456         QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision4.qml"));
5457
5458         QObject *object = component.create();
5459         QVERIFY(object != 0);
5460         QCOMPARE(object->property("test").toReal(), 11.);
5461         delete object;
5462     }
5463 }
5464
5465 void tst_qdeclarativeecmascript::realToInt()
5466 {
5467     QDeclarativeComponent component(&engine, TEST_FILE("realToInt.qml"));
5468     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
5469     QVERIFY(object != 0);
5470
5471     QMetaObject::invokeMethod(object, "test1");
5472     QCOMPARE(object->value(), int(4));
5473     QMetaObject::invokeMethod(object, "test2");
5474     QCOMPARE(object->value(), int(8));
5475 }
5476
5477 void tst_qdeclarativeecmascript::urlProperty()
5478 {
5479     {
5480         QDeclarativeComponent component(&engine, TEST_FILE("urlProperty.1.qml"));
5481         MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
5482         QVERIFY(object != 0);
5483         object->setStringProperty("http://qt-project.org");
5484         QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
5485         QCOMPARE(object->intProperty(), 123);
5486         QCOMPARE(object->value(), 1);
5487         QCOMPARE(object->property("result").toBool(), true);
5488     }
5489 }
5490
5491 void tst_qdeclarativeecmascript::dynamicString()
5492 {
5493     QDeclarativeComponent component(&engine, TEST_FILE("dynamicString.qml"));
5494     QObject *object = component.create();
5495     QVERIFY(object != 0);
5496     QCOMPARE(object->property("stringProperty").toString(),
5497              QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
5498 }
5499
5500 void tst_qdeclarativeecmascript::automaticSemicolon()
5501 {
5502     QDeclarativeComponent component(&engine, TEST_FILE("automaticSemicolon.qml"));
5503     QObject *object = component.create();
5504     QVERIFY(object != 0);
5505 }
5506
5507 void tst_qdeclarativeecmascript::unaryExpression()
5508 {
5509     QDeclarativeComponent component(&engine, TEST_FILE("unaryExpression.qml"));
5510     QObject *object = component.create();
5511     QVERIFY(object != 0);
5512 }
5513
5514 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
5515 void tst_qdeclarativeecmascript::doubleEvaluate()
5516 {
5517     QDeclarativeComponent component(&engine, TEST_FILE("doubleEvaluate.qml"));
5518     QObject *object = component.create();
5519     QVERIFY(object != 0);
5520     WriteCounter *wc = qobject_cast<WriteCounter *>(object);
5521     QVERIFY(wc != 0);
5522     QCOMPARE(wc->count(), 1);
5523
5524     wc->setProperty("x", 9);
5525
5526     QCOMPARE(wc->count(), 2);
5527
5528     delete object;
5529 }
5530
5531 static QStringList messages;
5532 static void captureMsgHandler(QtMsgType, const char *msg)
5533 {
5534     messages.append(QLatin1String(msg));
5535 }
5536
5537 void tst_qdeclarativeecmascript::nonNotifyable()
5538 {
5539     QV4Compiler::enableV4(false);
5540     QDeclarativeComponent component(&engine, TEST_FILE("nonNotifyable.qml"));
5541     QV4Compiler::enableV4(true);
5542
5543     QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
5544     messages.clear();
5545     QObject *object = component.create();
5546     qInstallMsgHandler(old);
5547
5548     QVERIFY(object != 0);
5549
5550     QString expected1 = QLatin1String("QDeclarativeExpression: Expression ") +
5551                         component.url().toString() +
5552                         QLatin1String(":5 depends on non-NOTIFYable properties:");
5553     QString expected2 = QLatin1String("    ") +
5554                         QLatin1String(object->metaObject()->className()) +
5555                         QLatin1String("::value");
5556
5557     QCOMPARE(messages.length(), 2);
5558     QCOMPARE(messages.at(0), expected1);
5559     QCOMPARE(messages.at(1), expected2);
5560
5561     delete object;
5562 }
5563
5564 void tst_qdeclarativeecmascript::forInLoop()
5565 {
5566     QDeclarativeComponent component(&engine, TEST_FILE("forInLoop.qml"));
5567     QObject *object = component.create();
5568     QVERIFY(object != 0);
5569
5570     QMetaObject::invokeMethod(object, "listProperty");
5571
5572     QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
5573     QCOMPARE(r.size(), 3);
5574     QCOMPARE(r[0],QLatin1String("0=obj1"));
5575     QCOMPARE(r[1],QLatin1String("1=obj2"));
5576     QCOMPARE(r[2],QLatin1String("2=obj3"));
5577
5578     //TODO: should test for in loop for other objects (such as QObjects) as well.
5579
5580     delete object;
5581 }
5582
5583 // An object the binding depends on is deleted while the binding is still running
5584 void tst_qdeclarativeecmascript::deleteWhileBindingRunning()
5585 {
5586     QDeclarativeComponent component(&engine, TEST_FILE("deleteWhileBindingRunning.qml"));
5587     QObject *object = component.create();
5588     QVERIFY(object != 0);
5589     delete object;
5590 }
5591
5592 void tst_qdeclarativeecmascript::qtbug_22679()
5593 {
5594     MyQmlObject object;
5595     object.setStringProperty(QLatin1String("Please work correctly"));
5596     engine.rootContext()->setContextProperty("contextProp", &object);
5597
5598     QDeclarativeComponent component(&engine, TEST_FILE("qtbug_22679.qml"));
5599     qRegisterMetaType<QList<QDeclarativeError> >("QList<QDeclarativeError>");
5600     QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QDeclarativeError>)));
5601
5602     QObject *o = component.create();
5603     QVERIFY(o != 0);
5604     QCOMPARE(warningsSpy.count(), 0);
5605     delete o;
5606 }
5607
5608 void tst_qdeclarativeecmascript::qtbug_22843_data()
5609 {
5610     QTest::addColumn<bool>("library");
5611
5612     QTest::newRow("without .pragma library") << false;
5613     QTest::newRow("with .pragma library") << true;
5614 }
5615
5616 void tst_qdeclarativeecmascript::qtbug_22843()
5617 {
5618     QFETCH(bool, library);
5619
5620     QString fileName("qtbug_22843");
5621     if (library)
5622         fileName += QLatin1String(".library");
5623     fileName += QLatin1String(".qml");
5624
5625     QDeclarativeComponent component(&engine, TEST_FILE(fileName));
5626     QString url = component.url().toString();
5627     QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )");
5628     QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'");
5629
5630     qRegisterMetaType<QList<QDeclarativeError> >("QList<QDeclarativeError>");
5631     QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QDeclarativeError>)));
5632     for (int x = 0; x < 3; ++x) {
5633         warningsSpy.clear();
5634         // For libraries, only the first import attempt should produce a
5635         // SyntaxError warning; subsequent component creation should not
5636         // attempt to reload the script.
5637         bool expectSyntaxError = !library || (x == 0);
5638         if (expectSyntaxError)
5639             QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
5640         QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
5641         QObject *object = component.create();
5642         QVERIFY(object != 0);
5643         QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
5644         delete object;
5645     }
5646 }
5647
5648
5649 void tst_qdeclarativeecmascript::switchStatement()
5650 {
5651     {
5652         QDeclarativeComponent component(&engine, TEST_FILE("switchStatement.1.qml"));
5653         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5654         QVERIFY(object != 0);
5655
5656         // `object->value()' is the number of executed statements
5657
5658         object->setStringProperty("A");
5659         QCOMPARE(object->value(), 5);
5660
5661         object->setStringProperty("S");
5662         QCOMPARE(object->value(), 3);
5663
5664         object->setStringProperty("D");
5665         QCOMPARE(object->value(), 3);
5666
5667         object->setStringProperty("F");
5668         QCOMPARE(object->value(), 4);
5669
5670         object->setStringProperty("something else");
5671         QCOMPARE(object->value(), 1);
5672     }
5673
5674     {
5675         QDeclarativeComponent component(&engine, TEST_FILE("switchStatement.2.qml"));
5676         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5677         QVERIFY(object != 0);
5678
5679         // `object->value()' is the number of executed statements
5680
5681         object->setStringProperty("A");
5682         QCOMPARE(object->value(), 5);
5683
5684         object->setStringProperty("S");
5685         QCOMPARE(object->value(), 3);
5686
5687         object->setStringProperty("D");
5688         QCOMPARE(object->value(), 3);
5689
5690         object->setStringProperty("F");
5691         QCOMPARE(object->value(), 3);
5692
5693         object->setStringProperty("something else");
5694         QCOMPARE(object->value(), 4);
5695     }
5696
5697     {
5698         QDeclarativeComponent component(&engine, TEST_FILE("switchStatement.3.qml"));
5699         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5700         QVERIFY(object != 0);
5701
5702         // `object->value()' is the number of executed statements
5703
5704         object->setStringProperty("A");
5705         QCOMPARE(object->value(), 5);
5706
5707         object->setStringProperty("S");
5708         QCOMPARE(object->value(), 3);
5709
5710         object->setStringProperty("D");
5711         QCOMPARE(object->value(), 3);
5712
5713         object->setStringProperty("F");
5714         QCOMPARE(object->value(), 3);
5715
5716         object->setStringProperty("something else");
5717         QCOMPARE(object->value(), 6);
5718     }
5719
5720     {
5721         QDeclarativeComponent component(&engine, TEST_FILE("switchStatement.4.qml"));
5722
5723         QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
5724         QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5725
5726         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5727         QVERIFY(object != 0);
5728
5729         // `object->value()' is the number of executed statements
5730
5731         object->setStringProperty("A");
5732         QCOMPARE(object->value(), 5);
5733
5734         object->setStringProperty("S");
5735         QCOMPARE(object->value(), 3);
5736
5737         object->setStringProperty("D");
5738         QCOMPARE(object->value(), 3);
5739
5740         object->setStringProperty("F");
5741         QCOMPARE(object->value(), 3);
5742
5743         QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5744
5745         object->setStringProperty("something else");
5746     }
5747
5748     {
5749         QDeclarativeComponent component(&engine, TEST_FILE("switchStatement.5.qml"));
5750         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5751         QVERIFY(object != 0);
5752
5753         // `object->value()' is the number of executed statements
5754
5755         object->setStringProperty("A");
5756         QCOMPARE(object->value(), 1);
5757
5758         object->setStringProperty("S");
5759         QCOMPARE(object->value(), 1);
5760
5761         object->setStringProperty("D");
5762         QCOMPARE(object->value(), 1);
5763
5764         object->setStringProperty("F");
5765         QCOMPARE(object->value(), 1);
5766
5767         object->setStringProperty("something else");
5768         QCOMPARE(object->value(), 1);
5769     }
5770
5771     {
5772         QDeclarativeComponent component(&engine, TEST_FILE("switchStatement.6.qml"));
5773         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5774         QVERIFY(object != 0);
5775
5776         // `object->value()' is the number of executed statements
5777
5778         object->setStringProperty("A");
5779         QCOMPARE(object->value(), 123);
5780
5781         object->setStringProperty("S");
5782         QCOMPARE(object->value(), 123);
5783
5784         object->setStringProperty("D");
5785         QCOMPARE(object->value(), 321);
5786
5787         object->setStringProperty("F");
5788         QCOMPARE(object->value(), 321);
5789
5790         object->setStringProperty("something else");
5791         QCOMPARE(object->value(), 0);
5792     }
5793 }
5794
5795 void tst_qdeclarativeecmascript::withStatement()
5796 {
5797     {
5798         QDeclarativeComponent component(&engine, TEST_FILE("withStatement.1.qml"));
5799         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5800         QVERIFY(object != 0);
5801
5802         QCOMPARE(object->value(), 123);
5803     }
5804 }
5805
5806 void tst_qdeclarativeecmascript::tryStatement()
5807 {
5808     {
5809         QDeclarativeComponent component(&engine, TEST_FILE("tryStatement.1.qml"));
5810         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5811         QVERIFY(object != 0);
5812
5813         QCOMPARE(object->value(), 123);
5814     }
5815
5816     {
5817         QDeclarativeComponent component(&engine, TEST_FILE("tryStatement.2.qml"));
5818         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5819         QVERIFY(object != 0);
5820
5821         QCOMPARE(object->value(), 321);
5822     }
5823
5824     {
5825         QDeclarativeComponent component(&engine, TEST_FILE("tryStatement.3.qml"));
5826         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5827         QVERIFY(object != 0);
5828
5829         QCOMPARE(object->value(), 1);
5830     }
5831
5832     {
5833         QDeclarativeComponent component(&engine, TEST_FILE("tryStatement.4.qml"));
5834         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5835         QVERIFY(object != 0);
5836
5837         QCOMPARE(object->value(), 1);
5838     }
5839 }
5840
5841 QTEST_MAIN(tst_qdeclarativeecmascript)
5842
5843 #include "tst_qdeclarativeecmascript.moc"