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