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