d30766f982c81f3c7c9faffa6874c48596771b50
[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     QString warning = component.url().toString() + ":16: SyntaxError: Unexpected token ILLEGAL";
1574     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
1575     QObject *object = component.create();
1576     QVERIFY(object != 0);
1577     delete object;
1578 }
1579
1580 static int transientErrorsMsgCount = 0;
1581 static void transientErrorsMsgHandler(QtMsgType, const char *)
1582 {
1583     ++transientErrorsMsgCount;
1584 }
1585
1586 // Check that transient binding errors are not displayed
1587 void tst_qdeclarativeecmascript::transientErrors()
1588 {
1589     {
1590     QDeclarativeComponent component(&engine, testFileUrl("transientErrors.qml"));
1591
1592     transientErrorsMsgCount = 0;
1593     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1594
1595     QObject *object = component.create();
1596     QVERIFY(object != 0);
1597
1598     qInstallMsgHandler(old);
1599
1600     QCOMPARE(transientErrorsMsgCount, 0);
1601
1602     delete object;
1603     }
1604
1605     // One binding erroring multiple times, but then resolving
1606     {
1607     QDeclarativeComponent component(&engine, testFileUrl("transientErrors.2.qml"));
1608
1609     transientErrorsMsgCount = 0;
1610     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1611
1612     QObject *object = component.create();
1613     QVERIFY(object != 0);
1614
1615     qInstallMsgHandler(old);
1616
1617     QCOMPARE(transientErrorsMsgCount, 0);
1618
1619     delete object;
1620     }
1621 }
1622
1623 // Check that errors during shutdown are minimized
1624 void tst_qdeclarativeecmascript::shutdownErrors()
1625 {
1626     QDeclarativeComponent component(&engine, testFileUrl("shutdownErrors.qml"));
1627     QObject *object = component.create();
1628     QVERIFY(object != 0);
1629
1630     transientErrorsMsgCount = 0;
1631     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
1632
1633     delete object;
1634
1635     qInstallMsgHandler(old);
1636     QCOMPARE(transientErrorsMsgCount, 0);
1637 }
1638
1639 void tst_qdeclarativeecmascript::compositePropertyType()
1640 {
1641     QDeclarativeComponent component(&engine, testFileUrl("compositePropertyType.qml"));
1642
1643     QTest::ignoreMessage(QtDebugMsg, "hello world");
1644     QObject *object = qobject_cast<QObject *>(component.create());
1645     delete object;
1646 }
1647
1648 // QTBUG-5759
1649 void tst_qdeclarativeecmascript::jsObject()
1650 {
1651     QDeclarativeComponent component(&engine, testFileUrl("jsObject.qml"));
1652     QObject *object = component.create();
1653     QVERIFY(object != 0);
1654
1655     QCOMPARE(object->property("test").toInt(), 92);
1656
1657     delete object;
1658 }
1659
1660 void tst_qdeclarativeecmascript::undefinedResetsProperty()
1661 {
1662     {
1663     QDeclarativeComponent component(&engine, testFileUrl("undefinedResetsProperty.qml"));
1664     QObject *object = component.create();
1665     QVERIFY(object != 0);
1666
1667     QCOMPARE(object->property("resettableProperty").toInt(), 92);
1668
1669     object->setProperty("setUndefined", true);
1670
1671     QCOMPARE(object->property("resettableProperty").toInt(), 13);
1672
1673     object->setProperty("setUndefined", false);
1674
1675     QCOMPARE(object->property("resettableProperty").toInt(), 92);
1676
1677     delete object;
1678     }
1679     {
1680     QDeclarativeComponent component(&engine, testFileUrl("undefinedResetsProperty.2.qml"));
1681     QObject *object = component.create();
1682     QVERIFY(object != 0);
1683
1684     QCOMPARE(object->property("resettableProperty").toInt(), 19);
1685
1686     QMetaObject::invokeMethod(object, "doReset");
1687
1688     QCOMPARE(object->property("resettableProperty").toInt(), 13);
1689
1690     delete object;
1691     }
1692 }
1693
1694 // Aliases to variant properties should work
1695 void tst_qdeclarativeecmascript::qtbug_22464()
1696 {
1697     QDeclarativeComponent component(&engine, testFileUrl("qtbug_22464.qml"));
1698     QObject *object = component.create();
1699     QVERIFY(object != 0);
1700
1701     QCOMPARE(object->property("test").toBool(), true);
1702
1703     delete object;
1704 }
1705
1706 void tst_qdeclarativeecmascript::qtbug_21580()
1707 {
1708     QDeclarativeComponent component(&engine, testFileUrl("qtbug_21580.qml"));
1709
1710     QObject *object = component.create();
1711     QVERIFY(object != 0);
1712
1713     QCOMPARE(object->property("test").toBool(), true);
1714
1715     delete object;
1716 }
1717
1718 // QTBUG-6781
1719 void tst_qdeclarativeecmascript::bug1()
1720 {
1721     QDeclarativeComponent component(&engine, testFileUrl("bug.1.qml"));
1722     QObject *object = component.create();
1723     QVERIFY(object != 0);
1724
1725     QCOMPARE(object->property("test").toInt(), 14);
1726
1727     object->setProperty("a", 11);
1728
1729     QCOMPARE(object->property("test").toInt(), 3);
1730
1731     object->setProperty("b", true);
1732
1733     QCOMPARE(object->property("test").toInt(), 9);
1734
1735     delete object;
1736 }
1737
1738 void tst_qdeclarativeecmascript::bug2()
1739 {
1740     QDeclarativeComponent component(&engine);
1741     component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl());
1742
1743     QObject *object = component.create();
1744     QVERIFY(object != 0);
1745
1746     delete object;
1747 }
1748
1749 // Don't crash in createObject when the component has errors.
1750 void tst_qdeclarativeecmascript::dynamicCreationCrash()
1751 {
1752     QDeclarativeComponent component(&engine, testFileUrl("dynamicCreation.qml"));
1753     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1754     QVERIFY(object != 0);
1755
1756     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
1757     QMetaObject::invokeMethod(object, "dontCrash");
1758     QObject *created = object->objectProperty();
1759     QVERIFY(created == 0);
1760
1761     delete object;
1762 }
1763
1764 // ownership transferred to JS, ensure that GC runs the dtor
1765 void tst_qdeclarativeecmascript::dynamicCreationOwnership()
1766 {
1767     int dtorCount = 0;
1768     int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
1769
1770     // allow the engine to go out of scope too.
1771     {
1772         QDeclarativeEngine dcoEngine;
1773         QDeclarativeComponent component(&dcoEngine, testFileUrl("dynamicCreationOwnership.qml"));
1774         QObject *object = component.create();
1775         QVERIFY(object != 0);
1776         MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo");
1777         QVERIFY(mdcdo != 0);
1778         mdcdo->setDtorCount(&dtorCount);
1779
1780         for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
1781             QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
1782             if (i % 90 == 0) {
1783                 // we do this once manually, but it should be done automatically
1784                 // when the engine goes out of scope (since it should gc in dtor)
1785                 QMetaObject::invokeMethod(object, "performGc");
1786             }
1787             if (i % 10 == 0) {
1788                 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1789                 QCoreApplication::processEvents();
1790             }
1791         }
1792
1793         delete object;
1794     }
1795     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
1796     QCoreApplication::processEvents();
1797     QCOMPARE(dtorCount, expectedDtorCount);
1798 }
1799
1800 void tst_qdeclarativeecmascript::regExpBug()
1801 {
1802     //QTBUG-9367
1803     {
1804         QDeclarativeComponent component(&engine, testFileUrl("regExp.qml"));
1805         MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1806         QVERIFY(object != 0);
1807         QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]"));
1808         delete object;
1809     }
1810
1811     //QTBUG-23068
1812     {
1813         QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString());
1814         QDeclarativeComponent component(&engine, testFileUrl("regExp.2.qml"));
1815         QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
1816         MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
1817         QVERIFY(!object);
1818         QCOMPARE(component.errorString(), err);
1819     }
1820 }
1821
1822 static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source)
1823 {
1824     QString functionSource = QLatin1String("(function(object) { return ") + 
1825                              QLatin1String(source) + QLatin1String(" })");
1826     v8::TryCatch tc;
1827     v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
1828     if (tc.HasCaught())
1829         return false;
1830     v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
1831     if (function.IsEmpty())
1832         return false;
1833     v8::Handle<v8::Value> args[] = { o };
1834     function->Call(engine->global(), 1, args);
1835     return tc.HasCaught();
1836 }
1837
1838 static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o, 
1839                                   const char *source, v8::Handle<v8::Value> result)
1840 {
1841     QString functionSource = QLatin1String("(function(object) { return ") + 
1842                              QLatin1String(source) + QLatin1String(" })");
1843     v8::TryCatch tc;
1844     v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
1845     if (tc.HasCaught())
1846         return false;
1847     v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
1848     if (function.IsEmpty())
1849         return false;
1850     v8::Handle<v8::Value> args[] = { o };
1851
1852     v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
1853
1854     if (tc.HasCaught())
1855         return false;
1856
1857     return value->StrictEquals(result);
1858 }
1859
1860 static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o, 
1861                                              const char *source)
1862 {
1863     QString functionSource = QLatin1String("(function(object) { return ") + 
1864                              QLatin1String(source) + QLatin1String(" })");
1865     v8::TryCatch tc;
1866     v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
1867     if (tc.HasCaught())
1868         return v8::Handle<v8::Value>();
1869     v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
1870     if (function.IsEmpty())
1871         return v8::Handle<v8::Value>();
1872     v8::Handle<v8::Value> args[] = { o };
1873
1874     v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
1875
1876     if (tc.HasCaught())
1877         return v8::Handle<v8::Value>();
1878     return value;
1879 }
1880
1881 #define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
1882 #define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result)
1883 #define EVALUATE(source) evaluate(engine, object, source)
1884
1885 void tst_qdeclarativeecmascript::callQtInvokables()
1886 {
1887     MyInvokableObject o;
1888
1889     QDeclarativeEngine qmlengine;
1890     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&qmlengine);
1891     
1892     QV8Engine *engine = ep->v8engine();
1893
1894     v8::HandleScope handle_scope;
1895     v8::Context::Scope scope(engine->context());
1896
1897     v8::Local<v8::Object> object = engine->newQObject(&o)->ToObject();
1898
1899     // Non-existent methods
1900     o.reset();
1901     QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
1902     QCOMPARE(o.error(), false);
1903     QCOMPARE(o.invoked(), -1);
1904     QCOMPARE(o.actuals().count(), 0);
1905
1906     o.reset();
1907     QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)"));
1908     QCOMPARE(o.error(), false);
1909     QCOMPARE(o.invoked(), -1);
1910     QCOMPARE(o.actuals().count(), 0);
1911
1912     // Insufficient arguments
1913     o.reset();
1914     QVERIFY(EVALUATE_ERROR("object.method_int()"));
1915     QCOMPARE(o.error(), false);
1916     QCOMPARE(o.invoked(), -1);
1917     QCOMPARE(o.actuals().count(), 0);
1918
1919     o.reset();
1920     QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
1921     QCOMPARE(o.error(), false);
1922     QCOMPARE(o.invoked(), -1);
1923     QCOMPARE(o.actuals().count(), 0);
1924
1925     // Excessive arguments
1926     o.reset();
1927     QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", v8::Undefined()));
1928     QCOMPARE(o.error(), false);
1929     QCOMPARE(o.invoked(), 8);
1930     QCOMPARE(o.actuals().count(), 1);
1931     QCOMPARE(o.actuals().at(0), QVariant(10));
1932
1933     o.reset();
1934     QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", v8::Undefined()));
1935     QCOMPARE(o.error(), false);
1936     QCOMPARE(o.invoked(), 9);
1937     QCOMPARE(o.actuals().count(), 2);
1938     QCOMPARE(o.actuals().at(0), QVariant(10));
1939     QCOMPARE(o.actuals().at(1), QVariant(11));
1940
1941     // Test return types
1942     o.reset();
1943     QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", v8::Undefined()));
1944     QCOMPARE(o.error(), false);
1945     QCOMPARE(o.invoked(), 0);
1946     QCOMPARE(o.actuals().count(), 0);
1947
1948     o.reset();
1949     QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", v8::Integer::New(6)));
1950     QCOMPARE(o.error(), false);
1951     QCOMPARE(o.invoked(), 1);
1952     QCOMPARE(o.actuals().count(), 0);
1953
1954     o.reset();
1955     QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", v8::Number::New(19.75)));
1956     QCOMPARE(o.error(), false);
1957     QCOMPARE(o.invoked(), 2);
1958     QCOMPARE(o.actuals().count(), 0);
1959
1960     o.reset();
1961     {
1962     v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QPointF()");
1963     QVERIFY(!ret.IsEmpty());
1964     QCOMPARE(engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5)));
1965     QCOMPARE(o.error(), false);
1966     QCOMPARE(o.invoked(), 3);
1967     QCOMPARE(o.actuals().count(), 0);
1968     }
1969
1970     o.reset();
1971     {
1972     v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QObject()");
1973     QCOMPARE(engine->toQObject(ret), (QObject *)&o);
1974     QCOMPARE(o.error(), false);
1975     QCOMPARE(o.invoked(), 4);
1976     QCOMPARE(o.actuals().count(), 0);
1977     }
1978
1979     o.reset();
1980     QVERIFY(EVALUATE_VALUE("object.method_NoArgs_unknown()", v8::Undefined()));
1981     QCOMPARE(o.error(), false);
1982     QCOMPARE(o.invoked(), 5);
1983     QCOMPARE(o.actuals().count(), 0);
1984
1985     o.reset();
1986     {
1987     v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QScriptValue()");
1988     QVERIFY(ret->IsString());
1989     QCOMPARE(engine->toString(ret), QString("Hello world"));
1990     QCOMPARE(o.error(), false);
1991     QCOMPARE(o.invoked(), 6);
1992     QCOMPARE(o.actuals().count(), 0);
1993     }
1994
1995     o.reset();
1996     QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks")));
1997     QCOMPARE(o.error(), false);
1998     QCOMPARE(o.invoked(), 7);
1999     QCOMPARE(o.actuals().count(), 0);
2000
2001     // Test arg types
2002     o.reset();
2003     QVERIFY(EVALUATE_VALUE("object.method_int(94)", v8::Undefined()));
2004     QCOMPARE(o.error(), false);
2005     QCOMPARE(o.invoked(), 8);
2006     QCOMPARE(o.actuals().count(), 1);
2007     QCOMPARE(o.actuals().at(0), QVariant(94));
2008
2009     o.reset();
2010     QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", v8::Undefined()));
2011     QCOMPARE(o.error(), false);
2012     QCOMPARE(o.invoked(), 8);
2013     QCOMPARE(o.actuals().count(), 1);
2014     QCOMPARE(o.actuals().at(0), QVariant(94));
2015
2016     o.reset();
2017     QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", v8::Undefined()));
2018     QCOMPARE(o.error(), false);
2019     QCOMPARE(o.invoked(), 8);
2020     QCOMPARE(o.actuals().count(), 1);
2021     QCOMPARE(o.actuals().at(0), QVariant(0));
2022
2023     o.reset();
2024     QVERIFY(EVALUATE_VALUE("object.method_int(null)", v8::Undefined()));
2025     QCOMPARE(o.error(), false);
2026     QCOMPARE(o.invoked(), 8);
2027     QCOMPARE(o.actuals().count(), 1);
2028     QCOMPARE(o.actuals().at(0), QVariant(0));
2029
2030     o.reset();
2031     QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", v8::Undefined()));
2032     QCOMPARE(o.error(), false);
2033     QCOMPARE(o.invoked(), 8);
2034     QCOMPARE(o.actuals().count(), 1);
2035     QCOMPARE(o.actuals().at(0), QVariant(0));
2036
2037     o.reset();
2038     QVERIFY(EVALUATE_VALUE("object.method_int(object)", v8::Undefined()));
2039     QCOMPARE(o.error(), false);
2040     QCOMPARE(o.invoked(), 8);
2041     QCOMPARE(o.actuals().count(), 1);
2042     QCOMPARE(o.actuals().at(0), QVariant(0));
2043
2044     o.reset();
2045     QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", v8::Undefined()));
2046     QCOMPARE(o.error(), false);
2047     QCOMPARE(o.invoked(), 9);
2048     QCOMPARE(o.actuals().count(), 2);
2049     QCOMPARE(o.actuals().at(0), QVariant(122));
2050     QCOMPARE(o.actuals().at(1), QVariant(9));
2051
2052     o.reset();
2053     QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", v8::Undefined()));
2054     QCOMPARE(o.error(), false);
2055     QCOMPARE(o.invoked(), 10);
2056     QCOMPARE(o.actuals().count(), 1);
2057     QCOMPARE(o.actuals().at(0), QVariant(94.3));
2058
2059     o.reset();
2060     QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", v8::Undefined()));
2061     QCOMPARE(o.error(), false);
2062     QCOMPARE(o.invoked(), 10);
2063     QCOMPARE(o.actuals().count(), 1);
2064     QCOMPARE(o.actuals().at(0), QVariant(94.3));
2065
2066     o.reset();
2067     QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", v8::Undefined()));
2068     QCOMPARE(o.error(), false);
2069     QCOMPARE(o.invoked(), 10);
2070     QCOMPARE(o.actuals().count(), 1);
2071     QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2072
2073     o.reset();
2074     QVERIFY(EVALUATE_VALUE("object.method_real(null)", v8::Undefined()));
2075     QCOMPARE(o.error(), false);
2076     QCOMPARE(o.invoked(), 10);
2077     QCOMPARE(o.actuals().count(), 1);
2078     QCOMPARE(o.actuals().at(0), QVariant(0));
2079
2080     o.reset();
2081     QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", v8::Undefined()));
2082     QCOMPARE(o.error(), false);
2083     QCOMPARE(o.invoked(), 10);
2084     QCOMPARE(o.actuals().count(), 1);
2085     QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2086
2087     o.reset();
2088     QVERIFY(EVALUATE_VALUE("object.method_real(object)", v8::Undefined()));
2089     QCOMPARE(o.error(), false);
2090     QCOMPARE(o.invoked(), 10);
2091     QCOMPARE(o.actuals().count(), 1);
2092     QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
2093
2094     o.reset();
2095     QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", v8::Undefined()));
2096     QCOMPARE(o.error(), false);
2097     QCOMPARE(o.invoked(), 11);
2098     QCOMPARE(o.actuals().count(), 1);
2099     QCOMPARE(o.actuals().at(0), QVariant("Hello world"));
2100
2101     o.reset();
2102     QVERIFY(EVALUATE_VALUE("object.method_QString(19)", v8::Undefined()));
2103     QCOMPARE(o.error(), false);
2104     QCOMPARE(o.invoked(), 11);
2105     QCOMPARE(o.actuals().count(), 1);
2106     QCOMPARE(o.actuals().at(0), QVariant("19"));
2107
2108     o.reset();
2109     {
2110     QString expected = "MyInvokableObject(0x" + QString::number((quintptr)&o, 16) + ")";
2111     QVERIFY(EVALUATE_VALUE("object.method_QString(object)", v8::Undefined()));
2112     QCOMPARE(o.error(), false);
2113     QCOMPARE(o.invoked(), 11);
2114     QCOMPARE(o.actuals().count(), 1);
2115     QCOMPARE(o.actuals().at(0), QVariant(expected));
2116     }
2117
2118     o.reset();
2119     QVERIFY(EVALUATE_VALUE("object.method_QString(null)", v8::Undefined()));
2120     QCOMPARE(o.error(), false);
2121     QCOMPARE(o.invoked(), 11);
2122     QCOMPARE(o.actuals().count(), 1);
2123     QCOMPARE(o.actuals().at(0), QVariant(QString()));
2124
2125     o.reset();
2126     QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", v8::Undefined()));
2127     QCOMPARE(o.error(), false);
2128     QCOMPARE(o.invoked(), 11);
2129     QCOMPARE(o.actuals().count(), 1);
2130     QCOMPARE(o.actuals().at(0), QVariant(QString()));
2131
2132     o.reset();
2133     QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", v8::Undefined()));
2134     QCOMPARE(o.error(), false);
2135     QCOMPARE(o.invoked(), 12);
2136     QCOMPARE(o.actuals().count(), 1);
2137     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2138
2139     o.reset();
2140     QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", v8::Undefined()));
2141     QCOMPARE(o.error(), false);
2142     QCOMPARE(o.invoked(), 12);
2143     QCOMPARE(o.actuals().count(), 1);
2144     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2145
2146     o.reset();
2147     QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", v8::Undefined()));
2148     QCOMPARE(o.error(), false);
2149     QCOMPARE(o.invoked(), 12);
2150     QCOMPARE(o.actuals().count(), 1);
2151     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2152
2153     o.reset();
2154     QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", v8::Undefined()));
2155     QCOMPARE(o.error(), false);
2156     QCOMPARE(o.invoked(), 12);
2157     QCOMPARE(o.actuals().count(), 1);
2158     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
2159
2160     o.reset();
2161     QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", v8::Undefined()));
2162     QCOMPARE(o.error(), false);
2163     QCOMPARE(o.invoked(), 12);
2164     QCOMPARE(o.actuals().count(), 1);
2165     QCOMPARE(o.actuals().at(0), QVariant(QPointF(99.3, -10.2)));
2166
2167     o.reset();
2168     QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", v8::Undefined()));
2169     QCOMPARE(o.error(), false);
2170     QCOMPARE(o.invoked(), 12);
2171     QCOMPARE(o.actuals().count(), 1);
2172     QCOMPARE(o.actuals().at(0), QVariant(QPointF(9, 12)));
2173
2174     o.reset();
2175     QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", v8::Undefined()));
2176     QCOMPARE(o.error(), false);
2177     QCOMPARE(o.invoked(), 13);
2178     QCOMPARE(o.actuals().count(), 1);
2179     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2180
2181     o.reset();
2182     QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", v8::Undefined()));
2183     QCOMPARE(o.error(), false);
2184     QCOMPARE(o.invoked(), 13);
2185     QCOMPARE(o.actuals().count(), 1);
2186     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2187
2188     o.reset();
2189     QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", v8::Undefined()));
2190     QCOMPARE(o.error(), false);
2191     QCOMPARE(o.invoked(), 13);
2192     QCOMPARE(o.actuals().count(), 1);
2193     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2194
2195     o.reset();
2196     QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", v8::Undefined()));
2197     QCOMPARE(o.error(), false);
2198     QCOMPARE(o.invoked(), 13);
2199     QCOMPARE(o.actuals().count(), 1);
2200     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
2201
2202     o.reset();
2203     QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", v8::Undefined()));
2204     QCOMPARE(o.error(), false);
2205     QCOMPARE(o.invoked(), 13);
2206     QCOMPARE(o.actuals().count(), 1);
2207     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)&o));
2208
2209     o.reset();
2210     QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined()));
2211     QCOMPARE(o.error(), false);
2212     QCOMPARE(o.invoked(), 14);
2213     QCOMPARE(o.actuals().count(), 1);
2214     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isNull());
2215
2216     o.reset();
2217     QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined()));
2218     QCOMPARE(o.error(), false);
2219     QCOMPARE(o.invoked(), 14);
2220     QCOMPARE(o.actuals().count(), 1);
2221     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isUndefined());
2222
2223     o.reset();
2224     QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined()));
2225     QCOMPARE(o.error(), false);
2226     QCOMPARE(o.invoked(), 14);
2227     QCOMPARE(o.actuals().count(), 1);
2228     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).strictlyEquals(QJSValue(19)));
2229
2230     o.reset();
2231     QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined()));
2232     QCOMPARE(o.error(), false);
2233     QCOMPARE(o.invoked(), 14);
2234     QCOMPARE(o.actuals().count(), 1);
2235     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isArray());
2236
2237     o.reset();
2238     QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined()));
2239     QCOMPARE(o.error(), false);
2240     QCOMPARE(o.invoked(), 15);
2241     QCOMPARE(o.actuals().count(), 2);
2242     QCOMPARE(o.actuals().at(0), QVariant(4));
2243     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isNull());
2244
2245     o.reset();
2246     QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined()));
2247     QCOMPARE(o.error(), false);
2248     QCOMPARE(o.invoked(), 15);
2249     QCOMPARE(o.actuals().count(), 2);
2250     QCOMPARE(o.actuals().at(0), QVariant(8));
2251     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isUndefined());
2252
2253     o.reset();
2254     QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined()));
2255     QCOMPARE(o.error(), false);
2256     QCOMPARE(o.invoked(), 15);
2257     QCOMPARE(o.actuals().count(), 2);
2258     QCOMPARE(o.actuals().at(0), QVariant(3));
2259     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).strictlyEquals(QJSValue(19)));
2260
2261     o.reset();
2262     QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined()));
2263     QCOMPARE(o.error(), false);
2264     QCOMPARE(o.invoked(), 15);
2265     QCOMPARE(o.actuals().count(), 2);
2266     QCOMPARE(o.actuals().at(0), QVariant(44));
2267     QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isArray());
2268
2269     o.reset();
2270     QVERIFY(EVALUATE_ERROR("object.method_overload()"));
2271     QCOMPARE(o.error(), false);
2272     QCOMPARE(o.invoked(), -1);
2273     QCOMPARE(o.actuals().count(), 0);
2274
2275     o.reset();
2276     QVERIFY(EVALUATE_VALUE("object.method_overload(10)", v8::Undefined()));
2277     QCOMPARE(o.error(), false);
2278     QCOMPARE(o.invoked(), 16);
2279     QCOMPARE(o.actuals().count(), 1);
2280     QCOMPARE(o.actuals().at(0), QVariant(10));
2281
2282     o.reset();
2283     QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", v8::Undefined()));
2284     QCOMPARE(o.error(), false);
2285     QCOMPARE(o.invoked(), 17);
2286     QCOMPARE(o.actuals().count(), 2);
2287     QCOMPARE(o.actuals().at(0), QVariant(10));
2288     QCOMPARE(o.actuals().at(1), QVariant(11));
2289
2290     o.reset();
2291     QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", v8::Undefined()));
2292     QCOMPARE(o.error(), false);
2293     QCOMPARE(o.invoked(), 18);
2294     QCOMPARE(o.actuals().count(), 1);
2295     QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2296
2297     o.reset();
2298     QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", v8::Undefined()));
2299     QCOMPARE(o.error(), false);
2300     QCOMPARE(o.invoked(), 19);
2301     QCOMPARE(o.actuals().count(), 1);
2302     QCOMPARE(o.actuals().at(0), QVariant(9));
2303
2304     o.reset();
2305     QVERIFY(EVALUATE_VALUE("object.method_default(10)", v8::Integer::New(19)));
2306     QCOMPARE(o.error(), false);
2307     QCOMPARE(o.invoked(), 20);
2308     QCOMPARE(o.actuals().count(), 2);
2309     QCOMPARE(o.actuals().at(0), QVariant(10));
2310     QCOMPARE(o.actuals().at(1), QVariant(19));
2311
2312     o.reset();
2313     QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", v8::Integer::New(13)));
2314     QCOMPARE(o.error(), false);
2315     QCOMPARE(o.invoked(), 20);
2316     QCOMPARE(o.actuals().count(), 2);
2317     QCOMPARE(o.actuals().at(0), QVariant(10));
2318     QCOMPARE(o.actuals().at(1), QVariant(13));
2319
2320     o.reset();
2321     QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", v8::Undefined()));
2322     QCOMPARE(o.error(), false);
2323     QCOMPARE(o.invoked(), -3);
2324     QCOMPARE(o.actuals().count(), 1);
2325     QCOMPARE(o.actuals().at(0), QVariant(9));
2326
2327     o.reset();
2328     QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", v8::Undefined()));
2329     QCOMPARE(o.error(), false);
2330     QCOMPARE(o.invoked(), 21);
2331     QCOMPARE(o.actuals().count(), 2);
2332     QCOMPARE(o.actuals().at(0), QVariant(9));
2333     QCOMPARE(o.actuals().at(1), QVariant());
2334
2335     o.reset();
2336     QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", v8::Undefined()));
2337     QCOMPARE(o.error(), false);
2338     QCOMPARE(o.invoked(), 21);
2339     QCOMPARE(o.actuals().count(), 2);
2340     QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
2341     QCOMPARE(o.actuals().at(1), QVariant(QString("World")));
2342 }
2343
2344 // QTBUG-13047 (check that you can pass registered object types as args)
2345 void tst_qdeclarativeecmascript::invokableObjectArg()
2346 {
2347     QDeclarativeComponent component(&engine, testFileUrl("invokableObjectArg.qml"));
2348
2349     QObject *o = component.create();
2350     QVERIFY(o);
2351     MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o);
2352     QVERIFY(qmlobject);
2353     QCOMPARE(qmlobject->myinvokableObject, qmlobject);
2354
2355     delete o;
2356 }
2357
2358 // QTBUG-13047 (check that you can return registered object types from methods)
2359 void tst_qdeclarativeecmascript::invokableObjectRet()
2360 {
2361     QDeclarativeComponent component(&engine, testFileUrl("invokableObjectRet.qml"));
2362
2363     QObject *o = component.create();
2364     QVERIFY(o);
2365     QCOMPARE(o->property("test").toBool(), true);
2366     delete o;
2367 }
2368
2369 // QTBUG-5675
2370 void tst_qdeclarativeecmascript::listToVariant()
2371 {
2372     QDeclarativeComponent component(&engine, testFileUrl("listToVariant.qml"));
2373
2374     MyQmlContainer container;
2375
2376     QDeclarativeContext context(engine.rootContext());
2377     context.setContextObject(&container);
2378
2379     QObject *object = component.create(&context);
2380     QVERIFY(object != 0);
2381
2382     QVariant v = object->property("test");
2383     QCOMPARE(v.userType(), qMetaTypeId<QDeclarativeListReference>());
2384     QVERIFY(qvariant_cast<QDeclarativeListReference>(v).object() == &container);
2385
2386     delete object;
2387 }
2388
2389 // QTBUG-16316
2390 Q_DECLARE_METATYPE(QDeclarativeListProperty<MyQmlObject>)
2391 void tst_qdeclarativeecmascript::listAssignment()
2392 {
2393     QDeclarativeComponent component(&engine, testFileUrl("listAssignment.qml"));
2394     QObject *obj = component.create();
2395     QCOMPARE(obj->property("list1length").toInt(), 2);
2396     QDeclarativeListProperty<MyQmlObject> list1 = obj->property("list1").value<QDeclarativeListProperty<MyQmlObject> >();
2397     QDeclarativeListProperty<MyQmlObject> list2 = obj->property("list2").value<QDeclarativeListProperty<MyQmlObject> >();
2398     QCOMPARE(list1.count(&list1), list2.count(&list2));
2399     QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0));
2400     QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1));
2401     delete obj;
2402 }
2403
2404 // QTBUG-7957
2405 void tst_qdeclarativeecmascript::multiEngineObject()
2406 {
2407     MyQmlObject obj;
2408     obj.setStringProperty("Howdy planet");
2409
2410     QDeclarativeEngine e1;
2411     e1.rootContext()->setContextProperty("thing", &obj);
2412     QDeclarativeComponent c1(&e1, testFileUrl("multiEngineObject.qml"));
2413
2414     QDeclarativeEngine e2;
2415     e2.rootContext()->setContextProperty("thing", &obj);
2416     QDeclarativeComponent c2(&e2, testFileUrl("multiEngineObject.qml"));
2417
2418     QObject *o1 = c1.create();
2419     QObject *o2 = c2.create();
2420
2421     QCOMPARE(o1->property("test").toString(), QString("Howdy planet"));
2422     QCOMPARE(o2->property("test").toString(), QString("Howdy planet"));
2423
2424     delete o2;
2425     delete o1;
2426 }
2427
2428 // Test that references to QObjects are cleanup when the object is destroyed
2429 void tst_qdeclarativeecmascript::deletedObject()
2430 {
2431     QDeclarativeComponent component(&engine, testFileUrl("deletedObject.qml"));
2432
2433     QObject *object = component.create();
2434
2435     QCOMPARE(object->property("test1").toBool(), true);
2436     QCOMPARE(object->property("test2").toBool(), true);
2437     QCOMPARE(object->property("test3").toBool(), true);
2438     QCOMPARE(object->property("test4").toBool(), true);
2439
2440     delete object;
2441 }
2442
2443 void tst_qdeclarativeecmascript::attachedPropertyScope()
2444 {
2445     QDeclarativeComponent component(&engine, testFileUrl("attachedPropertyScope.qml"));
2446
2447     QObject *object = component.create();
2448     QVERIFY(object != 0);
2449
2450     MyQmlAttachedObject *attached = 
2451         qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object));
2452     QVERIFY(attached != 0);
2453
2454     QCOMPARE(object->property("value2").toInt(), 0);
2455
2456     attached->emitMySignal();
2457
2458     QCOMPARE(object->property("value2").toInt(), 9);
2459
2460     delete object;
2461 }
2462
2463 void tst_qdeclarativeecmascript::scriptConnect()
2464 {
2465     {
2466         QDeclarativeComponent component(&engine, testFileUrl("scriptConnect.1.qml"));
2467
2468         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2469         QVERIFY(object != 0);
2470
2471         QCOMPARE(object->property("test").toBool(), false);
2472         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2473         QCOMPARE(object->property("test").toBool(), true);
2474
2475         delete object;
2476     }
2477
2478     {
2479         QDeclarativeComponent component(&engine, testFileUrl("scriptConnect.2.qml"));
2480
2481         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2482         QVERIFY(object != 0);
2483
2484         QCOMPARE(object->property("test").toBool(), false);
2485         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2486         QCOMPARE(object->property("test").toBool(), true);
2487
2488         delete object;
2489     }
2490
2491     {
2492         QDeclarativeComponent component(&engine, testFileUrl("scriptConnect.3.qml"));
2493
2494         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2495         QVERIFY(object != 0);
2496
2497         QCOMPARE(object->property("test").toBool(), false);
2498         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2499         QCOMPARE(object->property("test").toBool(), true);
2500
2501         delete object;
2502     }
2503
2504     {
2505         QDeclarativeComponent component(&engine, testFileUrl("scriptConnect.4.qml"));
2506
2507         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2508         QVERIFY(object != 0);
2509
2510         QCOMPARE(object->methodCalled(), false);
2511         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2512         QCOMPARE(object->methodCalled(), true);
2513
2514         delete object;
2515     }
2516
2517     {
2518         QDeclarativeComponent component(&engine, testFileUrl("scriptConnect.5.qml"));
2519
2520         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2521         QVERIFY(object != 0);
2522
2523         QCOMPARE(object->methodCalled(), false);
2524         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2525         QCOMPARE(object->methodCalled(), true);
2526
2527         delete object;
2528     }
2529
2530     {
2531         QDeclarativeComponent component(&engine, testFileUrl("scriptConnect.6.qml"));
2532
2533         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2534         QVERIFY(object != 0);
2535
2536         QCOMPARE(object->property("test").toInt(), 0);
2537         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2538         QCOMPARE(object->property("test").toInt(), 2);
2539
2540         delete object;
2541     }
2542 }
2543
2544 void tst_qdeclarativeecmascript::scriptDisconnect()
2545 {
2546     {
2547         QDeclarativeComponent component(&engine, testFileUrl("scriptDisconnect.1.qml"));
2548
2549         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2550         QVERIFY(object != 0);
2551
2552         QCOMPARE(object->property("test").toInt(), 0);
2553         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2554         QCOMPARE(object->property("test").toInt(), 1);
2555         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2556         QCOMPARE(object->property("test").toInt(), 2);
2557         emit object->basicSignal();
2558         QCOMPARE(object->property("test").toInt(), 2);
2559         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2560         QCOMPARE(object->property("test").toInt(), 2);
2561
2562         delete object;
2563     }
2564
2565     {
2566         QDeclarativeComponent component(&engine, testFileUrl("scriptDisconnect.2.qml"));
2567
2568         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2569         QVERIFY(object != 0);
2570
2571         QCOMPARE(object->property("test").toInt(), 0);
2572         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2573         QCOMPARE(object->property("test").toInt(), 1);
2574         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2575         QCOMPARE(object->property("test").toInt(), 2);
2576         emit object->basicSignal();
2577         QCOMPARE(object->property("test").toInt(), 2);
2578         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2579         QCOMPARE(object->property("test").toInt(), 2);
2580
2581         delete object;
2582     }
2583
2584     {
2585         QDeclarativeComponent component(&engine, testFileUrl("scriptDisconnect.3.qml"));
2586
2587         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2588         QVERIFY(object != 0);
2589
2590         QCOMPARE(object->property("test").toInt(), 0);
2591         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2592         QCOMPARE(object->property("test").toInt(), 1);
2593         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2594         QCOMPARE(object->property("test").toInt(), 2);
2595         emit object->basicSignal();
2596         QCOMPARE(object->property("test").toInt(), 2);
2597         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2598         QCOMPARE(object->property("test").toInt(), 3);
2599
2600         delete object;
2601     }
2602     {
2603         QDeclarativeComponent component(&engine, testFileUrl("scriptDisconnect.4.qml"));
2604
2605         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2606         QVERIFY(object != 0);
2607
2608         QCOMPARE(object->property("test").toInt(), 0);
2609         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2610         QCOMPARE(object->property("test").toInt(), 1);
2611         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2612         QCOMPARE(object->property("test").toInt(), 2);
2613         emit object->basicSignal();
2614         QCOMPARE(object->property("test").toInt(), 2);
2615         emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
2616         QCOMPARE(object->property("test").toInt(), 3);
2617
2618         delete object;
2619     }
2620 }
2621
2622 class OwnershipObject : public QObject
2623 {
2624     Q_OBJECT
2625 public:
2626     OwnershipObject() { object = new QObject; }
2627
2628     QPointer<QObject> object;
2629
2630 public slots:
2631     QObject *getObject() { return object; }
2632 };
2633
2634 void tst_qdeclarativeecmascript::ownership()
2635 {
2636     OwnershipObject own;
2637     QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
2638     context->setContextObject(&own);
2639
2640     {
2641         QDeclarativeComponent component(&engine, testFileUrl("ownership.qml"));
2642
2643         QVERIFY(own.object != 0);
2644
2645         QObject *object = component.create(context);
2646
2647         engine.collectGarbage();
2648
2649         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2650         QCoreApplication::processEvents();
2651
2652         QVERIFY(own.object == 0);
2653
2654         delete object;
2655     }
2656
2657     own.object = new QObject(&own);
2658
2659     {
2660         QDeclarativeComponent component(&engine, testFileUrl("ownership.qml"));
2661
2662         QVERIFY(own.object != 0);
2663
2664         QObject *object = component.create(context);
2665         
2666         engine.collectGarbage();
2667
2668         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2669         QCoreApplication::processEvents();
2670
2671         QVERIFY(own.object != 0);
2672
2673         delete object;
2674     }
2675
2676     delete context;
2677 }
2678
2679 class CppOwnershipReturnValue : public QObject
2680 {
2681     Q_OBJECT
2682 public:
2683     CppOwnershipReturnValue() : value(0) {}
2684     ~CppOwnershipReturnValue() { delete value; }
2685
2686     Q_INVOKABLE QObject *create() {
2687         value = new QObject;
2688         QDeclarativeEngine::setObjectOwnership(value, QDeclarativeEngine::CppOwnership);
2689         return value;
2690     }
2691
2692     Q_INVOKABLE MyQmlObject *createQmlObject() {
2693         MyQmlObject *rv = new MyQmlObject;
2694         value = rv;
2695         return rv;
2696     }
2697
2698     QPointer<QObject> value;
2699 };
2700
2701 // QTBUG-15695.  
2702 // Test setObjectOwnership(CppOwnership) works even when there is no QDeclarativeData
2703 void tst_qdeclarativeecmascript::cppOwnershipReturnValue()
2704 {
2705     CppOwnershipReturnValue source;
2706
2707     {
2708     QDeclarativeEngine engine;
2709     engine.rootContext()->setContextProperty("source", &source);
2710
2711     QVERIFY(source.value == 0);
2712
2713     QDeclarativeComponent component(&engine);
2714     component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl());
2715
2716     QObject *object = component.create();
2717
2718     QVERIFY(object != 0);
2719     QVERIFY(source.value != 0);
2720
2721     delete object;
2722     }
2723
2724     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2725     QCoreApplication::processEvents();
2726
2727     QVERIFY(source.value != 0);
2728 }
2729
2730 // QTBUG-15697
2731 void tst_qdeclarativeecmascript::ownershipCustomReturnValue()
2732 {
2733     CppOwnershipReturnValue source;
2734
2735     {
2736     QDeclarativeEngine engine;
2737     engine.rootContext()->setContextProperty("source", &source);
2738
2739     QVERIFY(source.value == 0);
2740
2741     QDeclarativeComponent component(&engine);
2742     component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl());
2743
2744     QObject *object = component.create();
2745
2746     QVERIFY(object != 0);
2747     QVERIFY(source.value != 0);
2748
2749     delete object;
2750     }
2751
2752     engine.collectGarbage();
2753     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2754     QCoreApplication::processEvents();
2755
2756     QVERIFY(source.value == 0);
2757 }
2758
2759 class QListQObjectMethodsObject : public QObject
2760 {
2761     Q_OBJECT
2762 public:
2763     QListQObjectMethodsObject() {
2764         m_objects.append(new MyQmlObject());
2765         m_objects.append(new MyQmlObject());
2766     }
2767
2768     ~QListQObjectMethodsObject() {
2769         qDeleteAll(m_objects);
2770     }
2771
2772 public slots:
2773     QList<QObject *> getObjects() { return m_objects; }
2774
2775 private:
2776     QList<QObject *> m_objects;
2777 };
2778
2779 // Tests that returning a QList<QObject*> from a method works
2780 void tst_qdeclarativeecmascript::qlistqobjectMethods()
2781 {
2782     QListQObjectMethodsObject obj;
2783     QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
2784     context->setContextObject(&obj);
2785
2786     QDeclarativeComponent component(&engine, testFileUrl("qlistqobjectMethods.qml"));
2787
2788     QObject *object = component.create(context);
2789
2790     QCOMPARE(object->property("test").toInt(), 2);
2791     QCOMPARE(object->property("test2").toBool(), true);
2792
2793     delete object;
2794     delete context;
2795 }
2796
2797 // QTBUG-9205
2798 void tst_qdeclarativeecmascript::strictlyEquals()
2799 {
2800     QDeclarativeComponent component(&engine, testFileUrl("strictlyEquals.qml"));
2801
2802     QObject *object = component.create();
2803     QVERIFY(object != 0);
2804
2805     QCOMPARE(object->property("test1").toBool(), true);
2806     QCOMPARE(object->property("test2").toBool(), true);
2807     QCOMPARE(object->property("test3").toBool(), true);
2808     QCOMPARE(object->property("test4").toBool(), true);
2809     QCOMPARE(object->property("test5").toBool(), true);
2810     QCOMPARE(object->property("test6").toBool(), true);
2811     QCOMPARE(object->property("test7").toBool(), true);
2812     QCOMPARE(object->property("test8").toBool(), true);
2813
2814     delete object;
2815 }
2816
2817 void tst_qdeclarativeecmascript::compiled()
2818 {
2819     QDeclarativeComponent component(&engine, testFileUrl("compiled.qml"));
2820
2821     QObject *object = component.create();
2822     QVERIFY(object != 0);
2823
2824     QCOMPARE(object->property("test1").toReal(), qreal(15.7));
2825     QCOMPARE(object->property("test2").toReal(), qreal(-6.7));
2826     QCOMPARE(object->property("test3").toBool(), true);
2827     QCOMPARE(object->property("test4").toBool(), false);
2828     QCOMPARE(object->property("test5").toBool(), false);
2829     QCOMPARE(object->property("test6").toBool(), true);
2830
2831     QCOMPARE(object->property("test7").toInt(), 185);
2832     QCOMPARE(object->property("test8").toInt(), 167);
2833     QCOMPARE(object->property("test9").toBool(), true);
2834     QCOMPARE(object->property("test10").toBool(), false);
2835     QCOMPARE(object->property("test11").toBool(), false);
2836     QCOMPARE(object->property("test12").toBool(), true);
2837
2838     QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld"));
2839     QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World"));
2840     QCOMPARE(object->property("test15").toBool(), false);
2841     QCOMPARE(object->property("test16").toBool(), true);
2842
2843     QCOMPARE(object->property("test17").toInt(), 5);
2844     QCOMPARE(object->property("test18").toReal(), qreal(176));
2845     QCOMPARE(object->property("test19").toInt(), 7);
2846     QCOMPARE(object->property("test20").toReal(), qreal(6.7));
2847     QCOMPARE(object->property("test21").toString(), QLatin1String("6.7"));
2848     QCOMPARE(object->property("test22").toString(), QLatin1String("!"));
2849     QCOMPARE(object->property("test23").toBool(), true);
2850     QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33));
2851     QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA));
2852
2853     delete object;
2854 }
2855
2856 // Test that numbers assigned in bindings as strings work consistently
2857 void tst_qdeclarativeecmascript::numberAssignment()
2858 {
2859     QDeclarativeComponent component(&engine, testFileUrl("numberAssignment.qml"));
2860
2861     QObject *object = component.create();
2862     QVERIFY(object != 0);
2863
2864     QCOMPARE(object->property("test1"), QVariant((qreal)6.7));
2865     QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
2866     QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
2867     QCOMPARE(object->property("test3"), QVariant((qreal)6));
2868     QCOMPARE(object->property("test4"), QVariant((qreal)6));
2869
2870     QCOMPARE(object->property("test5"), QVariant((int)7));
2871     QCOMPARE(object->property("test6"), QVariant((int)7));
2872     QCOMPARE(object->property("test7"), QVariant((int)6));
2873     QCOMPARE(object->property("test8"), QVariant((int)6));
2874
2875     QCOMPARE(object->property("test9"), QVariant((unsigned int)7));
2876     QCOMPARE(object->property("test10"), QVariant((unsigned int)7));
2877     QCOMPARE(object->property("test11"), QVariant((unsigned int)6));
2878     QCOMPARE(object->property("test12"), QVariant((unsigned int)6));
2879
2880     delete object;
2881 }
2882
2883 void tst_qdeclarativeecmascript::propertySplicing()
2884 {
2885     QDeclarativeComponent component(&engine, testFileUrl("propertySplicing.qml"));
2886
2887     QObject *object = component.create();
2888     QVERIFY(object != 0);
2889
2890     QCOMPARE(object->property("test").toBool(), true);
2891
2892     delete object;
2893 }
2894
2895 // QTBUG-16683
2896 void tst_qdeclarativeecmascript::signalWithUnknownTypes()
2897 {
2898     QDeclarativeComponent component(&engine, testFileUrl("signalWithUnknownTypes.qml"));
2899
2900     MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
2901     QVERIFY(object != 0);
2902
2903     MyQmlObject::MyType type;
2904     type.value = 0x8971123;
2905     emit object->signalWithUnknownType(type);
2906
2907     MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant());
2908
2909     QCOMPARE(result.value, type.value);
2910
2911
2912     delete object;
2913 }
2914
2915 void tst_qdeclarativeecmascript::signalWithJSValueInVariant_data()
2916 {
2917     QTest::addColumn<QString>("expression");
2918     QTest::addColumn<QString>("compare");
2919
2920     QString compareStrict("(function(a, b) { return a === b; })");
2921     QTest::newRow("true") << "true" << compareStrict;
2922     QTest::newRow("undefined") << "undefined" << compareStrict;
2923     QTest::newRow("null") << "null" << compareStrict;
2924     QTest::newRow("123") << "123" << compareStrict;
2925     QTest::newRow("'ciao'") << "'ciao'" << compareStrict;
2926
2927     QString comparePropertiesStrict(
2928         "(function(a, b) {"
2929         "  if (typeof b != 'object')"
2930         "    return a === b;"
2931         "  var props = Object.getOwnPropertyNames(b);"
2932         "  for (var i = 0; i < props.length; ++i) {"
2933         "    var p = props[i];"
2934         "    return arguments.callee(a[p], b[p]);"
2935         "  }"
2936         "})");
2937     QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })"  << comparePropertiesStrict;
2938     QTest::newRow("[10,20,30]") << "[10,20,30]"  << comparePropertiesStrict;
2939 }
2940
2941 void tst_qdeclarativeecmascript::signalWithJSValueInVariant()
2942 {
2943     QFETCH(QString, expression);
2944     QFETCH(QString, compare);
2945
2946     QDeclarativeComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
2947     QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
2948     QVERIFY(object != 0);
2949
2950     QJSValue value = engine.evaluate(expression);
2951     QVERIFY(!engine.hasUncaughtException());
2952     object->setProperty("expression", expression);
2953     object->setProperty("compare", compare);
2954     object->setProperty("pass", false);
2955
2956     emit object->signalWithVariant(QVariant::fromValue(value));
2957     QVERIFY(object->property("pass").toBool());
2958 }
2959
2960 void tst_qdeclarativeecmascript::signalWithJSValueInVariant_twoEngines_data()
2961 {
2962     signalWithJSValueInVariant_data();
2963 }
2964
2965 void tst_qdeclarativeecmascript::signalWithJSValueInVariant_twoEngines()
2966 {
2967     QFETCH(QString, expression);
2968     QFETCH(QString, compare);
2969
2970     QDeclarativeComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml"));
2971     QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
2972     QVERIFY(object != 0);
2973
2974     QJSEngine engine2;
2975     QJSValue value = engine2.evaluate(expression);
2976     QVERIFY(!engine2.hasUncaughtException());
2977     object->setProperty("expression", expression);
2978     object->setProperty("compare", compare);
2979     object->setProperty("pass", false);
2980
2981     QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine.");
2982     emit object->signalWithVariant(QVariant::fromValue(value));
2983     QVERIFY(!object->property("pass").toBool());
2984 }
2985
2986 void tst_qdeclarativeecmascript::signalWithQJSValue_data()
2987 {
2988     signalWithJSValueInVariant_data();
2989 }
2990
2991 void tst_qdeclarativeecmascript::signalWithQJSValue()
2992 {
2993     QFETCH(QString, expression);
2994     QFETCH(QString, compare);
2995
2996     QDeclarativeComponent component(&engine, testFileUrl("signalWithQJSValue.qml"));
2997     QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
2998     QVERIFY(object != 0);
2999
3000     QJSValue value = engine.evaluate(expression);
3001     QVERIFY(!engine.hasUncaughtException());
3002     object->setProperty("expression", expression);
3003     object->setProperty("compare", compare);
3004     object->setProperty("pass", false);
3005
3006     emit object->signalWithQJSValue(value);
3007
3008     QVERIFY(object->property("pass").toBool());
3009     QVERIFY(object->qjsvalue().strictlyEquals(value));
3010 }
3011
3012 void tst_qdeclarativeecmascript::moduleApi_data()
3013 {
3014     QTest::addColumn<QUrl>("testfile");
3015     QTest::addColumn<QString>("errorMessage");
3016     QTest::addColumn<QStringList>("warningMessages");
3017     QTest::addColumn<QStringList>("readProperties");
3018     QTest::addColumn<QVariantList>("readExpectedValues");
3019     QTest::addColumn<QStringList>("writeProperties");
3020     QTest::addColumn<QVariantList>("writeValues");
3021     QTest::addColumn<QStringList>("readBackProperties");
3022     QTest::addColumn<QVariantList>("readBackExpectedValues");
3023
3024     QTest::newRow("qobject, register + read + method")
3025             << testFileUrl("moduleapi/qobjectModuleApi.qml")
3026             << QString()
3027             << QStringList()
3028             << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
3029                    << "qobjectMinorVersionTest" << "qobjectMajorVersionTest" << "qobjectParentedTest")
3030             << (QVariantList() << 20 << 20 << 1 << 20 << 20 << 26)
3031             << QStringList()
3032             << QVariantList()
3033             << QStringList()
3034             << QVariantList();
3035
3036     QTest::newRow("script, register + read")
3037             << testFileUrl("moduleapi/scriptModuleApi.qml")
3038             << QString()
3039             << QStringList()
3040             << (QStringList() << "scriptTest")
3041             << (QVariantList() << 13)
3042             << QStringList()
3043             << QVariantList()
3044             << QStringList()
3045             << QVariantList();
3046
3047     QTest::newRow("qobject, caching + read")
3048             << testFileUrl("moduleapi/qobjectModuleApiCaching.qml")
3049             << QString()
3050             << QStringList()
3051             << (QStringList() << "existingUriTest" << "qobjectParentedTest")
3052             << (QVariantList() << 20 << 26) // 26, shouldn't have incremented to 27.
3053             << QStringList()
3054             << QVariantList()
3055             << QStringList()
3056             << QVariantList();
3057
3058     QTest::newRow("script, caching + read")
3059             << testFileUrl("moduleapi/scriptModuleApiCaching.qml")
3060             << QString()
3061             << QStringList()
3062             << (QStringList() << "scriptTest")
3063             << (QVariantList() << 13) // 13, shouldn't have incremented to 14.
3064             << QStringList()
3065             << QVariantList()
3066             << QStringList()
3067             << QVariantList();
3068
3069     QTest::newRow("qobject, writing + readonly constraints")
3070             << testFileUrl("moduleapi/qobjectModuleApiWriting.qml")
3071             << QString()
3072             << (QStringList() << QString(QLatin1String("file://") + testFileUrl("moduleapi/qobjectModuleApiWriting.qml").toLocalFile() + QLatin1String(":14: Error: Cannot assign to read-only property \"qobjectTestProperty\"")))
3073             << (QStringList() << "readOnlyProperty" << "writableProperty")
3074             << (QVariantList() << 20 << 50)
3075             << (QStringList() << "firstProperty" << "writableProperty")
3076             << (QVariantList() << 30 << 30)
3077             << (QStringList() << "readOnlyProperty" << "writableProperty")
3078             << (QVariantList() << 20 << 30);
3079
3080     QTest::newRow("script, writing + readonly constraints")
3081             << testFileUrl("moduleapi/scriptModuleApiWriting.qml")
3082             << QString()
3083             << (QStringList() << QString(QLatin1String("file://") + testFileUrl("moduleapi/scriptModuleApiWriting.qml").toLocalFile() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\"")))
3084             << (QStringList() << "readBack" << "unchanged")
3085             << (QVariantList() << 13 << 42)
3086             << (QStringList() << "firstProperty" << "secondProperty")
3087             << (QVariantList() << 30 << 30)
3088             << (QStringList() << "readBack" << "unchanged")
3089             << (QVariantList() << 30 << 42);
3090
3091     QTest::newRow("qobject module API enum values in JS")
3092             << testFileUrl("moduleapi/qobjectModuleApiEnums.qml")
3093             << QString()
3094             << QStringList()
3095             << (QStringList() << "enumValue" << "enumMethod")
3096             << (QVariantList() << 42 << 30)
3097             << QStringList()
3098             << QVariantList()
3099             << QStringList()
3100             << QVariantList();
3101
3102     QTest::newRow("qobject, invalid major version fail")
3103             << testFileUrl("moduleapi/moduleApiMajorVersionFail.qml")
3104             << QString("QDeclarativeComponent: Component is not ready")
3105             << QStringList()
3106             << QStringList()
3107             << QVariantList()
3108             << QStringList()
3109             << QVariantList()
3110             << QStringList()
3111             << QVariantList();
3112
3113     QTest::newRow("qobject, invalid minor version fail")
3114             << testFileUrl("moduleapi/moduleApiMinorVersionFail.qml")
3115             << QString("QDeclarativeComponent: Component is not ready")
3116             << QStringList()
3117             << QStringList()
3118             << QVariantList()
3119             << QStringList()
3120             << QVariantList()
3121             << QStringList()
3122             << QVariantList();
3123 }
3124
3125 void tst_qdeclarativeecmascript::moduleApi()
3126 {
3127     QFETCH(QUrl, testfile);
3128     QFETCH(QString, errorMessage);
3129     QFETCH(QStringList, warningMessages);
3130     QFETCH(QStringList, readProperties);
3131     QFETCH(QVariantList, readExpectedValues);
3132     QFETCH(QStringList, writeProperties);
3133     QFETCH(QVariantList, writeValues);
3134     QFETCH(QStringList, readBackProperties);
3135     QFETCH(QVariantList, readBackExpectedValues);
3136
3137     QDeclarativeComponent component(&engine, testfile);
3138
3139     if (!errorMessage.isEmpty())
3140         QTest::ignoreMessage(QtWarningMsg, errorMessage.toAscii().constData());
3141
3142     if (warningMessages.size())
3143         foreach (const QString &warning, warningMessages)
3144             QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
3145
3146     QObject *object = component.create();
3147     if (!errorMessage.isEmpty()) {
3148         QVERIFY(object == 0);
3149     } else {
3150         QVERIFY(object != 0);
3151         for (int i = 0; i < readProperties.size(); ++i)
3152             QCOMPARE(object->property(readProperties.at(i).toAscii().constData()), readExpectedValues.at(i));
3153         for (int i = 0; i < writeProperties.size(); ++i)
3154             QVERIFY(object->setProperty(writeProperties.at(i).toAscii().constData(), writeValues.at(i)));
3155         for (int i = 0; i < readBackProperties.size(); ++i)
3156             QCOMPARE(object->property(readBackProperties.at(i).toAscii().constData()), readBackExpectedValues.at(i));
3157         delete object;
3158     }
3159 }
3160
3161 void tst_qdeclarativeecmascript::importScripts_data()
3162 {
3163     QTest::addColumn<QUrl>("testfile");
3164     QTest::addColumn<QString>("errorMessage");
3165     QTest::addColumn<QStringList>("warningMessages");
3166     QTest::addColumn<QStringList>("propertyNames");
3167     QTest::addColumn<QVariantList>("propertyValues");
3168
3169     QTest::newRow("basic functionality")
3170             << testFileUrl("jsimport/testImport.qml")
3171             << QString()
3172             << QStringList()
3173             << (QStringList() << QLatin1String("importedScriptStringValue")
3174                               << QLatin1String("importedScriptFunctionValue")
3175                               << QLatin1String("importedModuleAttachedPropertyValue")
3176                               << QLatin1String("importedModuleEnumValue"))
3177             << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
3178                                << QVariant(20)
3179                                << QVariant(19)
3180                                << QVariant(2));
3181
3182     QTest::newRow("import scoping")
3183             << testFileUrl("jsimport/testImportScoping.qml")
3184             << QString()
3185             << QStringList()
3186             << (QStringList() << QLatin1String("componentError"))
3187             << (QVariantList() << QVariant(5));
3188
3189     QTest::newRow("parent scope shouldn't be inherited by import with imports")
3190             << testFileUrl("jsimportfail/failOne.qml")
3191             << QString()
3192             << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/failOne.qml").toLocalFile() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
3193             << (QStringList() << QLatin1String("importScriptFunctionValue"))
3194             << (QVariantList() << QVariant(QString()));
3195
3196     QTest::newRow("javascript imports in an import should be private to the import scope")
3197             << testFileUrl("jsimportfail/failTwo.qml")
3198             << QString()
3199             << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/failTwo.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: ImportOneJs")))
3200             << (QStringList() << QLatin1String("importScriptFunctionValue"))
3201             << (QVariantList() << QVariant(QString()));
3202
3203     QTest::newRow("module imports in an import should be private to the import scope")
3204             << testFileUrl("jsimportfail/failThree.qml")
3205             << QString()
3206             << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/failThree.qml").toLocalFile() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
3207             << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
3208             << (QVariantList() << QVariant(false));
3209
3210     QTest::newRow("typenames in an import should be private to the import scope")
3211             << testFileUrl("jsimportfail/failFour.qml")
3212             << QString()
3213             << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/failFour.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: JsQtTest")))
3214             << (QStringList() << QLatin1String("importedModuleEnumValue"))
3215             << (QVariantList() << QVariant(0));
3216
3217     QTest::newRow("import with imports has it's own activation scope")
3218             << testFileUrl("jsimportfail/failFive.qml")
3219             << QString()
3220             << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/importWithImports.js").toLocalFile() + QLatin1String(":8: ReferenceError: Can't find variable: Component")))
3221             << (QStringList() << QLatin1String("componentError"))
3222             << (QVariantList() << QVariant(0));
3223
3224     QTest::newRow("import pragma library script")
3225             << testFileUrl("jsimport/testImportPragmaLibrary.qml")
3226             << QString()
3227             << QStringList()
3228             << (QStringList() << QLatin1String("testValue"))
3229             << (QVariantList() << QVariant(31));
3230
3231     QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
3232             << testFileUrl("jsimportfail/testImportPragmaLibrary.qml")
3233             << QString()
3234             << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/importPragmaLibrary.js").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: Component")))
3235             << (QStringList() << QLatin1String("testValue"))
3236             << (QVariantList() << QVariant(0));
3237
3238     QTest::newRow("import pragma library script which has an import")
3239             << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
3240             << QString()
3241             << QStringList()
3242             << (QStringList() << QLatin1String("testValue"))
3243             << (QVariantList() << QVariant(55));
3244
3245     QTest::newRow("import pragma library script which has a pragma library import")
3246             << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
3247             << QString()
3248             << QStringList()
3249             << (QStringList() << QLatin1String("testValue"))
3250             << (QVariantList() << QVariant(18));
3251 }
3252
3253 void tst_qdeclarativeecmascript::importScripts()
3254 {
3255     QFETCH(QUrl, testfile);
3256     QFETCH(QString, errorMessage);
3257     QFETCH(QStringList, warningMessages);
3258     QFETCH(QStringList, propertyNames);
3259     QFETCH(QVariantList, propertyValues);
3260
3261     QDeclarativeComponent component(&engine, testfile);
3262
3263     if (!errorMessage.isEmpty())
3264         QTest::ignoreMessage(QtWarningMsg, errorMessage.toAscii().constData());
3265
3266     if (warningMessages.size())
3267         foreach (const QString &warning, warningMessages)
3268             QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
3269
3270     QObject *object = component.create();
3271     if (!errorMessage.isEmpty()) {
3272         QVERIFY(object == 0);
3273     } else {
3274         QVERIFY(object != 0);
3275         for (int i = 0; i < propertyNames.size(); ++i)
3276             QCOMPARE(object->property(propertyNames.at(i).toAscii().constData()), propertyValues.at(i));
3277         delete object;
3278     }
3279 }
3280
3281 void tst_qdeclarativeecmascript::scarceResources_other()
3282 {
3283     /* These tests require knowledge of state, since we test values after
3284        performing signal or function invocation. */
3285
3286     QPixmap origPixmap(100, 100);
3287     origPixmap.fill(Qt::blue);
3288     QString srp_name, expectedWarning;
3289     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&engine);
3290     ScarceResourceObject *eo = 0;
3291     QObject *srsc = 0;
3292     QObject *object = 0;
3293
3294     /* property var semantics */
3295
3296     // test that scarce resources are handled properly in signal invocation
3297     QDeclarativeComponent varComponentTen(&engine, testFileUrl("scarceResourceSignal.var.qml"));
3298     object = varComponentTen.create();
3299     srsc = object->findChild<QObject*>("srsc");
3300     QVERIFY(srsc);
3301     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3302     QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3303     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3304     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3305     QMetaObject::invokeMethod(srsc, "testSignal");
3306     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3307     QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3308     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3309     QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3310     QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3311     QVERIFY(srsc->property("scarceResourceCopy").isValid());
3312     QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3313     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3314     QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3315     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3316     delete object;
3317
3318     // test that scarce resources are handled properly from js functions in qml files
3319     QDeclarativeComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml"));
3320     object = varComponentEleven.create();
3321     QVERIFY(object != 0);
3322     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3323     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3324     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3325     QMetaObject::invokeMethod(object, "retrieveScarceResource");
3326     QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3327     QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3328     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3329     QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3330     QMetaObject::invokeMethod(object, "releaseScarceResource");
3331     QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3332     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3333     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3334     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3335     delete object;
3336
3337     // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3338     QDeclarativeComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml"));
3339     object = varComponentTwelve.create();
3340     QVERIFY(object != 0);
3341     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3342     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3343     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3344     srp_name = object->property("srp_name").toString();
3345     expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3346     QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3347     QMetaObject::invokeMethod(object, "retrieveScarceResource");
3348     QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3349     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3350     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3351     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3352     delete object;
3353
3354     // test that if an Item which has JS ownership but has a scarce resource property is garbage collected,
3355     // that the scarce resource is removed from the engine's list of scarce resources to clean up.
3356     QDeclarativeComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml"));
3357     object = varComponentThirteen.create();
3358     QVERIFY(object != 0);
3359     QVERIFY(!object->property("varProperty").isValid()); // not assigned yet
3360     QMetaObject::invokeMethod(object, "assignVarProperty");
3361     QVERIFY(ep->scarceResources.isEmpty());             // the scarce resource is a VME property.
3362     QMetaObject::invokeMethod(object, "deassignVarProperty");
3363     QVERIFY(ep->scarceResources.isEmpty());             // should still be empty; the resource should have been released on gc.
3364     delete object;
3365
3366     /* property variant semantics */
3367
3368     // test that scarce resources are handled properly in signal invocation
3369     QDeclarativeComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml"));
3370     object = variantComponentTen.create();
3371     QVERIFY(object != 0);
3372     srsc = object->findChild<QObject*>("srsc");
3373     QVERIFY(srsc);
3374     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
3375     QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
3376     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3377     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3378     QMetaObject::invokeMethod(srsc, "testSignal");
3379     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
3380     QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
3381     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3382     QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
3383     QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
3384     QVERIFY(srsc->property("scarceResourceCopy").isValid());
3385     QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3386     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3387     QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
3388     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3389     delete object;
3390
3391     // test that scarce resources are handled properly from js functions in qml files
3392     QDeclarativeComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml"));
3393     object = variantComponentEleven.create();
3394     QVERIFY(object != 0);
3395     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3396     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3397     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3398     QMetaObject::invokeMethod(object, "retrieveScarceResource");
3399     QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
3400     QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
3401     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3402     QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
3403     QMetaObject::invokeMethod(object, "releaseScarceResource");
3404     QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
3405     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3406     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3407     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3408     delete object;
3409
3410     // test that if an exception occurs while invoking js function from cpp, that the resources are released.
3411     QDeclarativeComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml"));
3412     object = variantComponentTwelve.create();
3413     QVERIFY(object != 0);
3414     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
3415     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3416     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3417     srp_name = object->property("srp_name").toString();
3418     expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
3419     QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3420     QMetaObject::invokeMethod(object, "retrieveScarceResource");
3421     QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
3422     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3423     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
3424     QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
3425     delete object;
3426 }
3427
3428 void tst_qdeclarativeecmascript::scarceResources_data()
3429 {
3430     QTest::addColumn<QUrl>("qmlFile");
3431     QTest::addColumn<bool>("readDetachStatus");
3432     QTest::addColumn<bool>("expectedDetachStatus");
3433     QTest::addColumn<QStringList>("propertyNames");
3434     QTest::addColumn<QVariantList>("expectedValidity");
3435     QTest::addColumn<QVariantList>("expectedValues");
3436     QTest::addColumn<QStringList>("expectedErrors");
3437
3438     QPixmap origPixmap(100, 100);
3439     origPixmap.fill(Qt::blue);
3440
3441     /* property var semantics */
3442
3443     // in the following three cases, the instance created from the component
3444     // has a property which is a copy of the scarce resource; hence, the
3445     // resource should NOT be detached prior to deletion of the object instance,
3446     // unless the resource is destroyed explicitly.
3447     QTest::newRow("var: import scarce resource copy directly")
3448         << testFileUrl("scarceResourceCopy.var.qml")
3449         << true
3450         << false // won't be detached, because assigned to property and not explicitly released
3451         << (QStringList() << QLatin1String("scarceResourceCopy"))
3452         << (QList<QVariant>() << true)
3453         << (QList<QVariant>() << origPixmap)
3454         << QStringList();
3455
3456     QTest::newRow("var: import scarce resource copy from JS")
3457         << testFileUrl("scarceResourceCopyFromJs.var.qml")
3458         << true
3459         << false // won't be detached, because assigned to property and not explicitly released
3460         << (QStringList() << QLatin1String("scarceResourceCopy"))
3461         << (QList<QVariant>() << true)
3462         << (QList<QVariant>() << origPixmap)
3463         << QStringList();
3464
3465     QTest::newRow("var: import released scarce resource copy from JS")
3466         << testFileUrl("scarceResourceDestroyedCopy.var.qml")
3467         << true
3468         << true // explicitly released, so it will be detached
3469         << (QStringList() << QLatin1String("scarceResourceCopy"))
3470         << (QList<QVariant>() << false)
3471         << (QList<QVariant>() << QVariant())
3472         << QStringList();
3473
3474     // in the following three cases, no other copy should exist in memory,
3475     // and so it should be detached (unless explicitly preserved).
3476     QTest::newRow("var: import auto-release SR from JS in binding side-effect")
3477         << testFileUrl("scarceResourceTest.var.qml")
3478         << true
3479         << true // auto released, so it will be detached
3480         << (QStringList() << QLatin1String("scarceResourceTest"))
3481         << (QList<QVariant>() << true)
3482         << (QList<QVariant>() << QVariant(100))
3483         << QStringList();
3484     QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
3485         << testFileUrl("scarceResourceTestPreserve.var.qml")
3486         << true
3487         << false // won't be detached because we explicitly preserve it
3488         << (QStringList() << QLatin1String("scarceResourceTest"))
3489         << (QList<QVariant>() << true)
3490         << (QList<QVariant>() << QVariant(100))
3491         << QStringList();
3492     QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
3493         << testFileUrl("scarceResourceTestMultiple.var.qml")
3494         << true
3495         << true // will be detached because all resources were released manually or automatically.
3496         << (QStringList() << QLatin1String("scarceResourceTest"))
3497         << (QList<QVariant>() << true)
3498         << (QList<QVariant>() << QVariant(100))
3499         << QStringList();
3500
3501     // In the following three cases, test that scarce resources are handled
3502     // correctly for imports.
3503     QTest::newRow("var: import with no binding")
3504         << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
3505         << false // cannot check detach status.
3506         << false
3507         << QStringList()
3508         << QList<QVariant>()
3509         << QList<QVariant>()
3510         << QStringList();
3511     QTest::newRow("var: import with binding without explicit preserve")
3512         << testFileUrl("scarceResourceCopyImportNoBinding.var.qml")
3513         << false
3514         << false
3515         << (QStringList() << QLatin1String("scarceResourceCopy"))
3516         << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
3517         << (QList<QVariant>() << QVariant())
3518         << QStringList();
3519     QTest::newRow("var: import with explicit release after binding evaluation")
3520         << testFileUrl("scarceResourceCopyImport.var.qml")
3521         << false
3522         << false
3523         << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
3524         << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated.
3525         << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true))
3526         << QStringList();
3527     QTest::newRow("var: import with different js objects")
3528         << testFileUrl("scarceResourceCopyImportDifferent.var.qml")
3529         << false
3530         << false
3531         << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
3532         << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
3533         << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false))
3534         << QStringList();
3535     QTest::newRow("var: import with different js objects and explicit release")
3536         << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml")
3537         << false
3538         << false
3539         << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
3540         << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
3541         << (QList<QVariant>() << QVariant(origPixmap) << QVariant())
3542         << QStringList();
3543     QTest::newRow("var: import with same js objects and explicit release")
3544         << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml")
3545         << false
3546         << false
3547         << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
3548         << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
3549         << (QList<QVariant>() << QVariant() << QVariant())
3550         << QStringList();
3551     QTest::newRow("var: binding with same js objects and explicit release")
3552         << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml")
3553         << false
3554         << false
3555         << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
3556         << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
3557         << (QList<QVariant>() << QVariant() << QVariant())
3558         << QStringList();
3559
3560
3561     /* property variant semantics */
3562
3563     // in the following three cases, the instance created from the component
3564     // has a property which is a copy of the scarce resource; hence, the
3565     // resource should NOT be detached prior to deletion of the object instance,
3566     // unless the resource is destroyed explicitly.
3567     QTest::newRow("variant: import scarce resource copy directly")
3568         << testFileUrl("scarceResourceCopy.variant.qml")
3569         << true
3570         << false // won't be detached, because assigned to property and not explicitly released
3571         << (QStringList() << QLatin1String("scarceResourceCopy"))
3572         << (QList<QVariant>() << true)
3573         << (QList<QVariant>() << origPixmap)
3574         << QStringList();
3575
3576     QTest::newRow("variant: import scarce resource copy from JS")
3577         << testFileUrl("scarceResourceCopyFromJs.variant.qml")
3578         << true
3579         << false // won't be detached, because assigned to property and not explicitly released
3580         << (QStringList() << QLatin1String("scarceResourceCopy"))
3581         << (QList<QVariant>() << true)
3582         << (QList<QVariant>() << origPixmap)
3583         << QStringList();
3584
3585     QTest::newRow("variant: import released scarce resource copy from JS")
3586         << testFileUrl("scarceResourceDestroyedCopy.variant.qml")
3587         << true
3588         << true // explicitly released, so it will be detached
3589         << (QStringList() << QLatin1String("scarceResourceCopy"))
3590         << (QList<QVariant>() << false)
3591         << (QList<QVariant>() << QVariant())
3592         << QStringList();
3593
3594     // in the following three cases, no other copy should exist in memory,
3595     // and so it should be detached (unless explicitly preserved).
3596     QTest::newRow("variant: import auto-release SR from JS in binding side-effect")
3597         << testFileUrl("scarceResourceTest.variant.qml")
3598         << true
3599         << true // auto released, so it will be detached
3600         << (QStringList() << QLatin1String("scarceResourceTest"))
3601         << (QList<QVariant>() << true)
3602         << (QList<QVariant>() << QVariant(100))
3603         << QStringList();
3604     QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect")
3605         << testFileUrl("scarceResourceTestPreserve.variant.qml")
3606         << true
3607         << false // won't be detached because we explicitly preserve it
3608         << (QStringList() << QLatin1String("scarceResourceTest"))
3609         << (QList<QVariant>() << true)
3610         << (QList<QVariant>() << QVariant(100))
3611         << QStringList();
3612     QTest::newRow("variant: import multiple scarce resources")
3613         << testFileUrl("scarceResourceTestMultiple.variant.qml")
3614         << true
3615         << true // will be detached because all resources were released manually or automatically.
3616         << (QStringList() << QLatin1String("scarceResourceTest"))
3617         << (QList<QVariant>() << true)
3618         << (QList<QVariant>() << QVariant(100))
3619         << QStringList();
3620
3621     // In the following three cases, test that scarce resources are handled
3622     // correctly for imports.
3623     QTest::newRow("variant: import with no binding")
3624         << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
3625         << false // cannot check detach status.
3626         << false
3627         << QStringList()
3628         << QList<QVariant>()
3629         << QList<QVariant>()
3630         << QStringList();
3631     QTest::newRow("variant: import with binding without explicit preserve")
3632         << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml")
3633         << false
3634         << false
3635         << (QStringList() << QLatin1String("scarceResourceCopy"))
3636         << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
3637         << (QList<QVariant>() << QVariant())
3638         << QStringList();
3639     QTest::newRow("variant: import with explicit release after binding evaluation")
3640         << testFileUrl("scarceResourceCopyImport.variant.qml")
3641         << false
3642         << false
3643         << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo"))
3644         << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies.
3645         << (QList<QVariant>() << origPixmap << origPixmap << QVariant())
3646         << QStringList();
3647 }
3648
3649 void tst_qdeclarativeecmascript::scarceResources()
3650 {
3651     QFETCH(QUrl, qmlFile);
3652     QFETCH(bool, readDetachStatus);
3653     QFETCH(bool, expectedDetachStatus);
3654     QFETCH(QStringList, propertyNames);
3655     QFETCH(QVariantList, expectedValidity);
3656     QFETCH(QVariantList, expectedValues);
3657     QFETCH(QStringList, expectedErrors);
3658
3659     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&engine);
3660     ScarceResourceObject *eo = 0;
3661     QObject *object = 0;
3662
3663     QDeclarativeComponent c(&engine, qmlFile);
3664     object = c.create();
3665     QVERIFY(object != 0);
3666     for (int i = 0; i < propertyNames.size(); ++i) {
3667         QString prop = propertyNames.at(i);
3668         bool validity = expectedValidity.at(i).toBool();
3669         QVariant value = expectedValues.at(i);
3670
3671         QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity);
3672         if (value.type() == QVariant::Int) {
3673             QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt());
3674         } else if (value.type() == QVariant::Pixmap) {
3675             QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>());
3676         }
3677     }
3678
3679     if (readDetachStatus) {
3680         eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
3681         QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
3682     }
3683
3684     QVERIFY(ep->scarceResources.isEmpty());
3685     delete object;
3686 }
3687
3688 void tst_qdeclarativeecmascript::propertyChangeSlots()
3689 {
3690     // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
3691     QDeclarativeComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml"));
3692     QObject *object = component.create();
3693     QVERIFY(object != 0);
3694     delete object;
3695
3696     // ensure that invalid property names fail properly.
3697     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
3698     QDeclarativeComponent e1(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.1.qml"));
3699     QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
3700     QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
3701     object = e1.create();
3702     QVERIFY(object == 0);
3703     delete object;
3704
3705     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
3706     QDeclarativeComponent e2(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.2.qml"));
3707     expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
3708     QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
3709     object = e2.create();
3710     QVERIFY(object == 0);
3711     delete object;
3712
3713     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
3714     QDeclarativeComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml"));
3715     expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
3716     QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
3717     object = e3.create();
3718     QVERIFY(object == 0);
3719     delete object;
3720
3721     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
3722     QDeclarativeComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml"));
3723     expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
3724     QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
3725     object = e4.create();
3726     QVERIFY(object == 0);
3727     delete object;
3728 }
3729
3730 void tst_qdeclarativeecmascript::propertyVar_data()
3731 {
3732     QTest::addColumn<QUrl>("qmlFile");
3733
3734     // valid
3735     QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyVar.1.qml");
3736     QTest::newRow("non-bindable object changed") << testFileUrl("propertyVar.2.qml");
3737     QTest::newRow("primitive changed") << testFileUrl("propertyVar.3.qml");
3738     QTest::newRow("javascript array modification") << testFileUrl("propertyVar.4.qml");
3739     QTest::newRow("javascript map modification") << testFileUrl("propertyVar.5.qml");
3740     QTest::newRow("javascript array assignment") << testFileUrl("propertyVar.6.qml");
3741     QTest::newRow("javascript map assignment") << testFileUrl("propertyVar.7.qml");
3742     QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml");
3743     QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml");
3744     QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml");
3745 }
3746
3747 void tst_qdeclarativeecmascript::propertyVar()
3748 {
3749     QFETCH(QUrl, qmlFile);
3750
3751     QDeclarativeComponent component(&engine, qmlFile);
3752     QObject *object = component.create();
3753     QVERIFY(object != 0);
3754
3755     QCOMPARE(object->property("test").toBool(), true);
3756
3757     delete object;
3758 }
3759
3760 // Tests that we can write QVariant values to var properties from C++
3761 void tst_qdeclarativeecmascript::propertyVarCpp()
3762 {
3763     QObject *object = 0;
3764
3765     // ensure that writing to and reading from a var property from cpp works as required.
3766     // Literal values stored in var properties can be read and written as QVariants
3767     // of a specific type, whereas object values are read as QVariantMaps.
3768     QDeclarativeComponent component(&engine, testFileUrl("propertyVarCpp.qml"));
3769     object = component.create();
3770     QVERIFY(object != 0);
3771     // assign int to property var that currently has int assigned
3772     QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
3773     QCOMPARE(object->property("varBound"), QVariant(15));
3774     QCOMPARE(object->property("intBound"), QVariant(15));
3775     QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
3776     QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
3777     // assign string to property var that current has bool assigned
3778     QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
3779     QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
3780     QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
3781     QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
3782     // now enforce behaviour when accessing JavaScript objects from cpp.
3783     QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
3784     delete object;
3785 }
3786
3787 static void gc(QDeclarativeEngine &engine)
3788 {
3789     engine.collectGarbage();
3790     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
3791     QCoreApplication::processEvents();
3792 }
3793
3794 void tst_qdeclarativeecmascript::propertyVarOwnership()
3795 {
3796     // Referenced JS objects are not collected
3797     {
3798     QDeclarativeComponent component(&engine, testFileUrl("propertyVarOwnership.qml"));
3799     QObject *object = component.create();
3800     QVERIFY(object != 0);
3801     QCOMPARE(object->property("test").toBool(), false);
3802     QMetaObject::invokeMethod(object, "runTest");
3803     QCOMPARE(object->property("test").toBool(), true);
3804     delete object;
3805     }
3806     // Referenced JS objects are not collected
3807     {
3808     QDeclarativeComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml"));
3809     QObject *object = component.create();
3810     QVERIFY(object != 0);
3811     QCOMPARE(object->property("test").toBool(), false);
3812     QMetaObject::invokeMethod(object, "runTest");
3813     QCOMPARE(object->property("test").toBool(), true);
3814     delete object;
3815     }
3816     // Qt objects are not collected until they've been dereferenced
3817     {
3818     QDeclarativeComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml"));
3819     QObject *object = component.create();
3820     QVERIFY(object != 0);
3821
3822     QCOMPARE(object->property("test2").toBool(), false);
3823     QCOMPARE(object->property("test2").toBool(), false);
3824
3825     QMetaObject::invokeMethod(object, "runTest");
3826     QCOMPARE(object->property("test1").toBool(), true);
3827
3828     QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
3829     QVERIFY(!referencedObject.isNull());
3830     gc(engine);
3831     QVERIFY(!referencedObject.isNull());
3832
3833     QMetaObject::invokeMethod(object, "runTest2");
3834     QCOMPARE(object->property("test2").toBool(), true);
3835     gc(engine);
3836     QVERIFY(referencedObject.isNull());
3837
3838     delete object;
3839     }
3840     // Self reference does not prevent Qt object collection
3841     {
3842     QDeclarativeComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml"));
3843     QObject *object = component.create();
3844     QVERIFY(object != 0);
3845
3846     QCOMPARE(object->property("test").toBool(), true);
3847
3848     QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
3849     QVERIFY(!referencedObject.isNull());
3850     gc(engine);
3851     QVERIFY(!referencedObject.isNull());
3852
3853     QMetaObject::invokeMethod(object, "runTest");
3854     gc(engine);
3855     QVERIFY(referencedObject.isNull());
3856
3857     delete object;
3858     }
3859 }
3860
3861 void tst_qdeclarativeecmascript::propertyVarImplicitOwnership()
3862 {
3863     // The childObject has a reference to a different QObject.  We want to ensure
3864     // that the different item will not be cleaned up until required.  IE, the childObject
3865     // has implicit ownership of the constructed QObject.
3866     QDeclarativeComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml"));
3867     QObject *object = component.create();
3868     QVERIFY(object != 0);
3869     QMetaObject::invokeMethod(object, "assignCircular");
3870     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
3871     QCoreApplication::processEvents();
3872     QObject *rootObject = object->property("vp").value<QObject*>();
3873     QVERIFY(rootObject != 0);
3874     QObject *childObject = rootObject->findChild<QObject*>("text");
3875     QVERIFY(childObject != 0);
3876     QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
3877     QCOMPARE(childObject->property("textCanary").toInt(), 10);
3878     QMetaObject::invokeMethod(childObject, "constructQObject");    // creates a reference to a constructed QObject.
3879     QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
3880     QVERIFY(!qobjectGuard.isNull());
3881     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
3882     QCoreApplication::processEvents();
3883     QVERIFY(!qobjectGuard.isNull());
3884     QMetaObject::invokeMethod(object, "deassignCircular");
3885     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
3886     QCoreApplication::processEvents();
3887     QVERIFY(qobjectGuard.isNull());                                // should have been collected now.
3888     delete object;
3889 }
3890
3891 void tst_qdeclarativeecmascript::propertyVarReparent()
3892 {
3893     // ensure that nothing breaks if we re-parent objects
3894     QDeclarativeComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
3895     QObject *object = component.create();
3896     QVERIFY(object != 0);
3897     QMetaObject::invokeMethod(object, "assignVarProp");
3898     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
3899     QCoreApplication::processEvents();
3900     QObject *rect = object->property("vp").value<QObject*>();
3901     QObject *text = rect->findChild<QObject*>("textOne");
3902     QObject *text2 = rect->findChild<QObject*>("textTwo");
3903     QWeakPointer<QObject> rectGuard(rect);
3904     QWeakPointer<QObject> textGuard(text);
3905     QWeakPointer<QObject> text2Guard(text2);
3906     QVERIFY(!rectGuard.isNull());
3907     QVERIFY(!textGuard.isNull());
3908     QVERIFY(!text2Guard.isNull());
3909     QCOMPARE(text->property("textCanary").toInt(), 11);
3910     QCOMPARE(text2->property("textCanary").toInt(), 12);
3911     // now construct an image which we will reparent.
3912     QMetaObject::invokeMethod(text2, "constructQObject");
3913     QObject *image = text2->property("vp").value<QObject*>();
3914     QWeakPointer<QObject> imageGuard(image);
3915     QVERIFY(!imageGuard.isNull());
3916     QCOMPARE(image->property("imageCanary").toInt(), 13);
3917     // now reparent the "Image" object (currently, it has JS ownership)
3918     image->setParent(text);                                        // shouldn't be collected after deassignVp now, since has a parent.
3919     QMetaObject::invokeMethod(text2, "deassignVp");
3920     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
3921     QCoreApplication::processEvents();
3922     QCOMPARE(text->property("textCanary").toInt(), 11);
3923     QCOMPARE(text2->property("textCanary").toInt(), 22);
3924     QVERIFY(!imageGuard.isNull());                                 // should still be alive.
3925     QCOMPARE(image->property("imageCanary").toInt(), 13);          // still able to access var properties
3926     QMetaObject::invokeMethod(object, "deassignVarProp");          // now deassign the root-object's vp, causing gc of rect+text+text2
3927     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
3928     QCoreApplication::processEvents();
3929     QVERIFY(imageGuard.isNull());                                  // should now have been deleted, due to parent being deleted.
3930     delete object;
3931 }
3932
3933 void tst_qdeclarativeecmascript::propertyVarReparentNullContext()
3934 {
3935     // sometimes reparenting can cause problems
3936     // (eg, if the ctxt is collected, varproperties are no longer available)
3937     // this test ensures that no crash occurs in that situation.
3938     QDeclarativeComponent component(&engine, testFileUrl("propertyVar.reparent.qml"));
3939     QObject *object = component.create();
3940     QVERIFY(object != 0);
3941     QMetaObject::invokeMethod(object, "assignVarProp");
3942     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
3943     QCoreApplication::processEvents();
3944     QObject *rect = object->property("vp").value<QObject*>();
3945     QObject *text = rect->findChild<QObject*>("textOne");
3946     QObject *text2 = rect->findChild<QObject*>("textTwo");
3947     QWeakPointer<QObject> rectGuard(rect);
3948     QWeakPointer<QObject> textGuard(text);
3949     QWeakPointer<QObject> text2Guard(text2);
3950     QVERIFY(!rectGuard.isNull());
3951     QVERIFY(!textGuard.isNull());
3952     QVERIFY(!text2Guard.isNull());
3953     QCOMPARE(text->property("textCanary").toInt(), 11);
3954     QCOMPARE(text2->property("textCanary").toInt(), 12);
3955     // now construct an image which we will reparent.
3956     QMetaObject::invokeMethod(text2, "constructQObject");
3957     QObject *image = text2->property("vp").value<QObject*>();
3958     QWeakPointer<QObject> imageGuard(image);
3959     QVERIFY(!imageGuard.isNull());
3960     QCOMPARE(image->property("imageCanary").toInt(), 13);
3961     // now reparent the "Image" object (currently, it has JS ownership)
3962     image->setParent(object);                                      // reparented to base object.  after deassignVarProp, the ctxt will be invalid.
3963     QMetaObject::invokeMethod(object, "deassignVarProp");          // now deassign the root-object's vp, causing gc of rect+text+text2
3964     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
3965     QCoreApplication::processEvents();
3966     QVERIFY(!imageGuard.isNull());                                 // should still be alive.
3967     QVERIFY(!image->property("imageCanary").isValid());            // but varProperties won't be available (null context).
3968     delete object;
3969     QVERIFY(imageGuard.isNull());                                  // should now be dead.
3970 }
3971
3972 void tst_qdeclarativeecmascript::propertyVarCircular()
3973 {
3974     // enforce behaviour regarding circular references - ensure qdvmemo deletion.
3975     QDeclarativeComponent component(&engine, testFileUrl("propertyVar.circular.qml"));
3976     QObject *object = component.create();
3977     QVERIFY(object != 0);
3978     QMetaObject::invokeMethod(object, "assignCircular");           // cause assignment and gc
3979     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
3980     QCoreApplication::processEvents();
3981     QCOMPARE(object->property("canaryInt"), QVariant(5));
3982     QVariant canaryResourceVariant = object->property("canaryResource");
3983     QVERIFY(canaryResourceVariant.isValid());
3984     QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
3985     canaryResourceVariant = QVariant();                            // invalidate it to remove one copy of the pixmap from memory.
3986     QMetaObject::invokeMethod(object, "deassignCanaryResource");   // remove one copy of the pixmap from memory
3987     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
3988     QCoreApplication::processEvents();
3989     QVERIFY(!canaryResourcePixmap.isDetached());                   // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
3990     QMetaObject::invokeMethod(object, "deassignCircular");         // cause deassignment and gc
3991     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
3992     QCoreApplication::processEvents();
3993     QCOMPARE(object->property("canaryInt"), QVariant(2));
3994     QCOMPARE(object->property("canaryResource"), QVariant(1));
3995     QVERIFY(canaryResourcePixmap.isDetached());                    // now detached, since orig copy was member of qdvmemo which was deleted.
3996     delete object;
3997 }
3998
3999 void tst_qdeclarativeecmascript::propertyVarCircular2()
4000 {
4001     // track deletion of JS-owned parent item with Cpp-owned child
4002     // where the child has a var property referencing its parent.
4003     QDeclarativeComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4004     QObject *object = component.create();
4005     QVERIFY(object != 0);
4006     QMetaObject::invokeMethod(object, "assignCircular");
4007     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4008     QCoreApplication::processEvents();
4009     QObject *rootObject = object->property("vp").value<QObject*>();
4010     QVERIFY(rootObject != 0);
4011     QObject *childObject = rootObject->findChild<QObject*>("text");
4012     QVERIFY(childObject != 0);
4013     QWeakPointer<QObject> rootObjectTracker(rootObject);
4014     QVERIFY(!rootObjectTracker.isNull());
4015     QWeakPointer<QObject> childObjectTracker(childObject);
4016     QVERIFY(!childObjectTracker.isNull());
4017     gc(engine);
4018     QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4019     QCOMPARE(childObject->property("textCanary").toInt(), 10);
4020     QMetaObject::invokeMethod(object, "deassignCircular");
4021     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4022     QCoreApplication::processEvents();
4023     QVERIFY(rootObjectTracker.isNull());                           // should have been collected
4024     QVERIFY(childObjectTracker.isNull());                          // should have been collected
4025     delete object;
4026 }
4027
4028 void tst_qdeclarativeecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
4029 {
4030     *(int*)(parameter) += 1;
4031     qPersistentDispose(object);
4032 }
4033
4034 void tst_qdeclarativeecmascript::propertyVarInheritance()
4035 {
4036     int propertyVarWeakRefCallbackCount = 0;
4037
4038     // enforce behaviour regarding element inheritance - ensure handle disposal.
4039     // The particular component under test here has a chain of references.
4040     QDeclarativeComponent component(&engine, testFileUrl("propertyVar.inherit.qml"));
4041     QObject *object = component.create();
4042     QVERIFY(object != 0);
4043     QMetaObject::invokeMethod(object, "assignCircular");           // cause assignment and gc
4044     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4045     QCoreApplication::processEvents();
4046     // we want to be able to track when the varProperties array of the last metaobject is disposed
4047     QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
4048     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*>();
4049     QDeclarativeVMEMetaObject *icovmemo = ((QDeclarativeVMEMetaObject *)(ico5->metaObject()));
4050     QDeclarativeVMEMetaObject *ccovmemo = ((QDeclarativeVMEMetaObject *)(cco5->metaObject()));
4051     v8::Persistent<v8::Value> icoCanaryHandle;
4052     v8::Persistent<v8::Value> ccoCanaryHandle;
4053     {
4054         v8::HandleScope hs;
4055         // XXX NOTE: this is very implementation dependent.  QDVMEMO->vmeProperty() is the only
4056         // public function which can return us a handle to something in the varProperties array.
4057         icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ")));
4058         ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")));
4059         // we make them weak and invoke the gc, but we should not hit the weak-callback yet
4060         // as the varproperties array of each vmemo still references the resource.
4061         icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4062         ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4063         gc(engine);
4064         QVERIFY(propertyVarWeakRefCallbackCount == 0);
4065     }
4066     // now we deassign the var prop, which should trigger collection of item subtrees.
4067     QMetaObject::invokeMethod(object, "deassignCircular");         // cause deassignment and gc
4068     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4069     QCoreApplication::processEvents();
4070     // ensure that there are only weak handles to the underlying varProperties array remaining.
4071     gc(engine);
4072     QCOMPARE(propertyVarWeakRefCallbackCount, 2);                  // should have been called for both, since all refs should be weak.
4073     delete object;
4074     // since there are no parent vmemo's to keep implicit references alive, and the only handles
4075     // to what remains are weak, all varProperties arrays must have been collected.
4076 }
4077
4078 void tst_qdeclarativeecmascript::propertyVarInheritance2()
4079 {
4080     int propertyVarWeakRefCallbackCount = 0;
4081
4082     // The particular component under test here does NOT have a chain of references; the
4083     // only link between rootObject and childObject is that rootObject is the parent of childObject.
4084     QDeclarativeComponent component(&engine, testFileUrl("propertyVar.circular.2.qml"));
4085     QObject *object = component.create();
4086     QVERIFY(object != 0);
4087     QMetaObject::invokeMethod(object, "assignCircular");
4088     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4089     QCoreApplication::processEvents();
4090     QObject *rootObject = object->property("vp").value<QObject*>();
4091     QVERIFY(rootObject != 0);
4092     QObject *childObject = rootObject->findChild<QObject*>("text");
4093     QVERIFY(childObject != 0);
4094     QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
4095     QCOMPARE(childObject->property("textCanary").toInt(), 10);
4096     v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
4097     {
4098         v8::HandleScope hs;
4099         propertyVarWeakRefCallbackCount = 0;                           // reset callback count.
4100         childObjectVarArrayValueHandle = qPersistentNew(((QDeclarativeVMEMetaObject *)(childObject->metaObject()))->vmeProperty(childObject->metaObject()->indexOfProperty("vp")));
4101         childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
4102         gc(engine);
4103         QVERIFY(propertyVarWeakRefCallbackCount == 0);                 // should not have been collected yet.
4104         QCOMPARE(childObject->property("vp").value<QObject*>(), rootObject);
4105         QCOMPARE(childObject->property("textCanary").toInt(), 10);
4106     }
4107     QMetaObject::invokeMethod(object, "deassignCircular");
4108     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
4109     QCoreApplication::processEvents();
4110     QVERIFY(propertyVarWeakRefCallbackCount == 1);                 // should have been collected now.
4111     delete object;
4112 }
4113
4114 // Ensure that QObject type conversion works on binding assignment
4115 void tst_qdeclarativeecmascript::elementAssign()
4116 {
4117     QDeclarativeComponent component(&engine, testFileUrl("elementAssign.qml"));
4118
4119     QObject *object = component.create();
4120     QVERIFY(object != 0);
4121
4122     QCOMPARE(object->property("test").toBool(), true);
4123
4124     delete object;
4125 }
4126
4127 // QTBUG-12457
4128 void tst_qdeclarativeecmascript::objectPassThroughSignals()
4129 {
4130     QDeclarativeComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml"));
4131
4132     QObject *object = component.create();
4133     QVERIFY(object != 0);
4134
4135     QCOMPARE(object->property("test").toBool(), true);
4136
4137     delete object;
4138 }
4139
4140 // QTBUG-21626
4141 void tst_qdeclarativeecmascript::objectConversion()
4142 {
4143     QDeclarativeComponent component(&engine, testFileUrl("objectConversion.qml"));
4144
4145     QObject *object = component.create();
4146     QVERIFY(object != 0);
4147     QVariant retn;
4148     QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
4149     QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
4150
4151     delete object;
4152 }
4153
4154
4155 // QTBUG-20242
4156 void tst_qdeclarativeecmascript::booleanConversion()
4157 {
4158     QDeclarativeComponent component(&engine, testFileUrl("booleanConversion.qml"));
4159
4160     QObject *object = component.create();
4161     QVERIFY(object != 0);
4162
4163     QCOMPARE(object->property("test_true1").toBool(), true);
4164     QCOMPARE(object->property("test_true2").toBool(), true);
4165     QCOMPARE(object->property("test_true3").toBool(), true);
4166     QCOMPARE(object->property("test_true4").toBool(), true);
4167     QCOMPARE(object->property("test_true5").toBool(), true);
4168
4169     QCOMPARE(object->property("test_false1").toBool(), false);
4170     QCOMPARE(object->property("test_false2").toBool(), false);
4171     QCOMPARE(object->property("test_false3").toBool(), false);
4172
4173     delete object;
4174 }
4175
4176 void tst_qdeclarativeecmascript::handleReferenceManagement()
4177 {
4178
4179     int dtorCount = 0;
4180     {
4181         // Linear QObject reference
4182         QDeclarativeEngine hrmEngine;
4183         QDeclarativeComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml"));
4184         QObject *object = component.create();
4185         QVERIFY(object != 0);
4186         CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4187         cro->setEngine(&hrmEngine);
4188         cro->setDtorCount(&dtorCount);
4189         QMetaObject::invokeMethod(object, "createReference");
4190         gc(engine);
4191         QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
4192         delete object;
4193         hrmEngine.collectGarbage();
4194         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4195         QCoreApplication::processEvents();
4196         QCOMPARE(dtorCount, 3);
4197     }
4198
4199     dtorCount = 0;
4200     {
4201         // Circular QObject reference
4202         QDeclarativeEngine hrmEngine;
4203         QDeclarativeComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml"));
4204         QObject *object = component.create();
4205         QVERIFY(object != 0);
4206         CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
4207         cro->setEngine(&hrmEngine);
4208         cro->setDtorCount(&dtorCount);
4209         QMetaObject::invokeMethod(object, "circularReference");
4210         gc(engine);
4211         QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
4212         delete object;
4213         hrmEngine.collectGarbage();
4214         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4215         QCoreApplication::processEvents();
4216         QCOMPARE(dtorCount, 3);
4217     }
4218
4219     dtorCount = 0;
4220     {
4221         // Linear handle reference
4222         QDeclarativeEngine hrmEngine;
4223         QDeclarativeComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.1.qml"));
4224         QObject *object = component.create();
4225         QVERIFY(object != 0);
4226         CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4227         QVERIFY(crh != 0);
4228         crh->setEngine(&hrmEngine);
4229         crh->setDtorCount(&dtorCount);
4230         QMetaObject::invokeMethod(object, "createReference");
4231         CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4232         CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4233         QVERIFY(first != 0);
4234         QVERIFY(second != 0);
4235         first->addReference(QDeclarativeData::get(second)->v8object); // create reference
4236         // now we have to reparent second and make second owned by JS.
4237         second->setParent(0);
4238         QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership);
4239         gc(engine);
4240         QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
4241         delete object;
4242         hrmEngine.collectGarbage();
4243         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4244         QCoreApplication::processEvents();
4245         QCOMPARE(dtorCount, 3);
4246     }
4247
4248     dtorCount = 0;
4249     {
4250         // Circular handle reference
4251         QDeclarativeEngine hrmEngine;
4252         QDeclarativeComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.2.qml"));
4253         QObject *object = component.create();
4254         QVERIFY(object != 0);
4255         CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
4256         QVERIFY(crh != 0);
4257         crh->setEngine(&hrmEngine);
4258         crh->setDtorCount(&dtorCount);
4259         QMetaObject::invokeMethod(object, "circularReference");
4260         CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
4261         CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
4262         QVERIFY(first != 0);
4263         QVERIFY(second != 0);
4264         first->addReference(QDeclarativeData::get(second)->v8object); // create circular reference
4265         second->addReference(QDeclarativeData::get(first)->v8object); // note: must be weak.
4266         // now we have to reparent and change ownership.
4267         first->setParent(0);
4268         second->setParent(0);
4269         QDeclarativeEngine::setObjectOwnership(first, QDeclarativeEngine::JavaScriptOwnership);
4270         QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership);
4271         gc(engine);
4272         QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
4273         delete object;
4274         hrmEngine.collectGarbage();
4275         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4276         QCoreApplication::processEvents();
4277         QCOMPARE(dtorCount, 3);
4278     }
4279
4280     dtorCount = 0;
4281     {
4282         // multiple engine interaction - linear reference
4283         QDeclarativeEngine hrmEngine1;
4284         QDeclarativeEngine hrmEngine2;
4285         QDeclarativeComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4286         QDeclarativeComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4287         QObject *object1 = component1.create();
4288         QObject *object2 = component2.create();
4289         QVERIFY(object1 != 0);
4290         QVERIFY(object2 != 0);
4291         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4292         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4293         QVERIFY(crh1 != 0);
4294         QVERIFY(crh2 != 0);
4295         crh1->setEngine(&hrmEngine1);
4296         crh2->setEngine(&hrmEngine2);
4297         crh1->setDtorCount(&dtorCount);
4298         crh2->setDtorCount(&dtorCount);
4299         QMetaObject::invokeMethod(object1, "createReference");
4300         QMetaObject::invokeMethod(object2, "createReference");
4301         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4302         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4303         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4304         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4305         QVERIFY(first1 != 0);
4306         QVERIFY(second1 != 0);
4307         QVERIFY(first2 != 0);
4308         QVERIFY(second2 != 0);
4309         first1->addReference(QDeclarativeData::get(second2)->v8object); // create reference across engines
4310         // now we have to reparent second2 and make second2 owned by JS.
4311         second2->setParent(0);
4312         QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
4313         gc(engine);
4314         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4315         QCoreApplication::processEvents();
4316         QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
4317         delete object1;
4318         delete object2;
4319         hrmEngine1.collectGarbage();
4320         hrmEngine2.collectGarbage();
4321         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4322         QCoreApplication::processEvents();
4323         QCOMPARE(dtorCount, 6);
4324     }
4325
4326     dtorCount = 0;
4327     {
4328         // multiple engine interaction - circular reference
4329         QDeclarativeEngine hrmEngine1;
4330         QDeclarativeEngine hrmEngine2;
4331         QDeclarativeComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4332         QDeclarativeComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4333         QObject *object1 = component1.create();
4334         QObject *object2 = component2.create();
4335         QVERIFY(object1 != 0);
4336         QVERIFY(object2 != 0);
4337         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4338         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4339         QVERIFY(crh1 != 0);
4340         QVERIFY(crh2 != 0);
4341         crh1->setEngine(&hrmEngine1);
4342         crh2->setEngine(&hrmEngine2);
4343         crh1->setDtorCount(&dtorCount);
4344         crh2->setDtorCount(&dtorCount);
4345         QMetaObject::invokeMethod(object1, "createReference");
4346         QMetaObject::invokeMethod(object2, "createReference");
4347         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4348         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4349         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4350         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4351         QVERIFY(first1 != 0);
4352         QVERIFY(second1 != 0);
4353         QVERIFY(first2 != 0);
4354         QVERIFY(second2 != 0);
4355         first1->addReference(QDeclarativeData::get(second1)->v8object);  // create linear reference within engine1
4356         second1->addReference(QDeclarativeData::get(second2)->v8object); // create linear reference across engines
4357         second2->addReference(QDeclarativeData::get(first2)->v8object);  // create linear reference within engine2
4358         first2->addReference(QDeclarativeData::get(first1)->v8object);   // close the loop - circular ref across engines
4359         // now we have to reparent and change ownership to JS.
4360         first1->setParent(0);
4361         second1->setParent(0);
4362         first2->setParent(0);
4363         second2->setParent(0);
4364         QDeclarativeEngine::setObjectOwnership(first1, QDeclarativeEngine::JavaScriptOwnership);
4365         QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership);
4366         QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership);
4367         QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
4368         gc(engine);
4369         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4370         QCoreApplication::processEvents();
4371         QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
4372         delete object1;
4373         delete object2;
4374         hrmEngine1.collectGarbage();
4375         hrmEngine2.collectGarbage();
4376         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4377         QCoreApplication::processEvents();
4378         QCOMPARE(dtorCount, 6);
4379     }
4380
4381     dtorCount = 0;
4382     {
4383         // multiple engine interaction - linear reference with engine deletion
4384         QDeclarativeEngine *hrmEngine1 = new QDeclarativeEngine;
4385         QDeclarativeEngine *hrmEngine2 = new QDeclarativeEngine;
4386         QDeclarativeComponent component1(hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml"));
4387         QDeclarativeComponent component2(hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml"));
4388         QObject *object1 = component1.create();
4389         QObject *object2 = component2.create();
4390         QVERIFY(object1 != 0);
4391         QVERIFY(object2 != 0);
4392         CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
4393         CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
4394         QVERIFY(crh1 != 0);
4395         QVERIFY(crh2 != 0);
4396         crh1->setEngine(hrmEngine1);
4397         crh2->setEngine(hrmEngine2);
4398         crh1->setDtorCount(&dtorCount);
4399         crh2->setDtorCount(&dtorCount);
4400         QMetaObject::invokeMethod(object1, "createReference");
4401         QMetaObject::invokeMethod(object2, "createReference");
4402         CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
4403         CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
4404         CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
4405         CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
4406         QVERIFY(first1 != 0);
4407         QVERIFY(second1 != 0);
4408         QVERIFY(first2 != 0);
4409         QVERIFY(second2 != 0);
4410         first1->addReference(QDeclarativeData::get(second1)->v8object);  // create linear reference within engine1
4411         second1->addReference(QDeclarativeData::get(second2)->v8object); // create linear reference across engines
4412         second2->addReference(QDeclarativeData::get(first2)->v8object);  // create linear reference within engine2
4413         // now we have to reparent and change ownership to JS.
4414         first1->setParent(crh1);
4415         second1->setParent(0);
4416         first2->setParent(0);
4417         second2->setParent(0);
4418         QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership);
4419         QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership);
4420         QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
4421         gc(engine);
4422         QCOMPARE(dtorCount, 0);
4423         delete hrmEngine2;
4424         gc(engine);
4425         QCOMPARE(dtorCount, 0);
4426         delete object1;
4427         delete object2;
4428         hrmEngine1->collectGarbage();
4429         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
4430         QCoreApplication::processEvents();
4431         QCOMPARE(dtorCount, 6);
4432         delete hrmEngine1;
4433     }
4434 }
4435
4436 void tst_qdeclarativeecmascript::stringArg()
4437 {
4438     QDeclarativeComponent component(&engine, testFileUrl("stringArg.qml"));
4439     QObject *object = component.create();
4440     QVERIFY(object != 0);
4441     QMetaObject::invokeMethod(object, "success");
4442     QVERIFY(object->property("returnValue").toBool());
4443
4444     QString w1 = testFileUrl("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
4445     QTest::ignoreMessage(QtWarningMsg, w1.toAscii().constData());
4446     QMetaObject::invokeMethod(object, "failure");
4447     QVERIFY(object->property("returnValue").toBool());
4448
4449     delete object;
4450 }
4451
4452 void tst_qdeclarativeecmascript::readonlyDeclaration()
4453 {
4454     QDeclarativeComponent component(&engine, testFileUrl("readonlyDeclaration.qml"));
4455
4456     QObject *object = component.create();
4457     QVERIFY(object != 0);
4458
4459     QCOMPARE(object->property("test").toBool(), true);
4460
4461     delete object;
4462 }
4463
4464 Q_DECLARE_METATYPE(QList<int>)
4465 Q_DECLARE_METATYPE(QList<qreal>)
4466 Q_DECLARE_METATYPE(QList<bool>)
4467 Q_DECLARE_METATYPE(QList<QString>)
4468 Q_DECLARE_METATYPE(QList<QUrl>)
4469 void tst_qdeclarativeecmascript::sequenceConversionRead()
4470 {
4471     {
4472         QUrl qmlFile = testFileUrl("sequenceConversion.read.qml");
4473         QDeclarativeComponent component(&engine, qmlFile);
4474         QObject *object = component.create();
4475         QVERIFY(object != 0);
4476         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4477         QVERIFY(seq != 0);
4478
4479         QMetaObject::invokeMethod(object, "readSequences");
4480         QList<int> intList; intList << 1 << 2 << 3 << 4;
4481         QCOMPARE(object->property("intListLength").toInt(), intList.length());
4482         QCOMPARE(object->property("intList").value<QList<int> >(), intList);
4483         QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
4484         QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
4485         QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
4486         QList<bool> boolList; boolList << true << false << true << false;
4487         QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
4488         QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
4489         QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
4490         QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
4491         QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
4492         QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
4493         QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
4494         QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
4495         QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
4496         QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
4497         QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
4498
4499         QMetaObject::invokeMethod(object, "readSequenceElements");
4500         QCOMPARE(object->property("intVal").toInt(), 2);
4501         QCOMPARE(object->property("qrealVal").toReal(), 2.2);
4502         QCOMPARE(object->property("boolVal").toBool(), false);
4503         QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
4504         QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
4505         QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
4506
4507         QMetaObject::invokeMethod(object, "enumerateSequenceElements");
4508         QCOMPARE(object->property("enumerationMatches").toBool(), true);
4509
4510         intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
4511         QDeclarativeProperty seqProp(seq, "intListProperty");
4512         QCOMPARE(seqProp.read().value<QList<int> >(), intList);
4513         QDeclarativeProperty seqProp2(seq, "intListProperty", &engine);
4514         QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
4515
4516         QMetaObject::invokeMethod(object, "testReferenceDeletion");
4517         QCOMPARE(object->property("referenceDeletion").toBool(), true);
4518
4519         delete object;
4520     }
4521
4522     {
4523         QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml");
4524         QDeclarativeComponent component(&engine, qmlFile);
4525         QObject *object = component.create();
4526         QVERIFY(object != 0);
4527         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4528         QVERIFY(seq != 0);
4529
4530         // we haven't registered QList<QPoint> as a sequence type.
4531         QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
4532         QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
4533         QTest::ignoreMessage(QtWarningMsg, warningOne.toAscii().constData());
4534         QTest::ignoreMessage(QtWarningMsg, warningTwo.toAscii().constData());
4535
4536         QMetaObject::invokeMethod(object, "performTest");
4537
4538         // QList<QPoint> has not been registered as a sequence type.
4539         QCOMPARE(object->property("pointListLength").toInt(), 0);
4540         QVERIFY(!object->property("pointList").isValid());
4541         QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
4542         QDeclarativeProperty seqProp(seq, "pointListProperty", &engine);
4543         QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
4544
4545         delete object;
4546     }
4547 }
4548
4549 void tst_qdeclarativeecmascript::sequenceConversionWrite()
4550 {
4551     {
4552         QUrl qmlFile = testFileUrl("sequenceConversion.write.qml");
4553         QDeclarativeComponent component(&engine, qmlFile);
4554         QObject *object = component.create();
4555         QVERIFY(object != 0);
4556         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4557         QVERIFY(seq != 0);
4558
4559         QMetaObject::invokeMethod(object, "writeSequences");
4560         QCOMPARE(object->property("success").toBool(), true);
4561
4562         QMetaObject::invokeMethod(object, "writeSequenceElements");
4563         QCOMPARE(object->property("success").toBool(), true);
4564
4565         QMetaObject::invokeMethod(object, "writeOtherElements");
4566         QCOMPARE(object->property("success").toBool(), true);
4567
4568         QMetaObject::invokeMethod(object, "testReferenceDeletion");
4569         QCOMPARE(object->property("referenceDeletion").toBool(), true);
4570
4571         delete object;
4572     }
4573
4574     {
4575         QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml");
4576         QDeclarativeComponent component(&engine, qmlFile);
4577         QObject *object = component.create();
4578         QVERIFY(object != 0);
4579         MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
4580         QVERIFY(seq != 0);
4581
4582         // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
4583         QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to void");
4584         QTest::ignoreMessage(QtWarningMsg, warningOne.toAscii().constData());
4585
4586         QMetaObject::invokeMethod(object, "performTest");
4587
4588         QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
4589         QCOMPARE(seq->pointListProperty(), pointList);
4590
4591         delete object;
4592     }
4593 }
4594
4595 void tst_qdeclarativeecmascript::sequenceConversionArray()
4596 {
4597     // ensure that in JS the returned sequences act just like normal JS Arrays.
4598     QUrl qmlFile = testFileUrl("sequenceConversion.array.qml");
4599     QDeclarativeComponent component(&engine, qmlFile);
4600     QObject *object = component.create();
4601     QVERIFY(object != 0);
4602     QMetaObject::invokeMethod(object, "indexedAccess");
4603     QVERIFY(object->property("success").toBool());
4604     QMetaObject::invokeMethod(object, "arrayOperations");
4605     QVERIFY(object->property("success").toBool());
4606     QMetaObject::invokeMethod(object, "testEqualitySemantics");
4607     QVERIFY(object->property("success").toBool());
4608     QMetaObject::invokeMethod(object, "testReferenceDeletion");
4609     QCOMPARE(object->property("referenceDeletion").toBool(), true);
4610     delete object;
4611 }
4612
4613
4614 void tst_qdeclarativeecmascript::sequenceConversionIndexes()
4615 {
4616     // ensure that we gracefully fail if unsupported index values are specified.
4617     // Qt container classes only support non-negative, signed integer index values.
4618     QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
4619     QDeclarativeComponent component(&engine, qmlFile);
4620     QObject *object = component.create();
4621     QVERIFY(object != 0);
4622     QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
4623     QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
4624     QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
4625     QString w4 = qmlFile.toString() + QLatin1String(":78: std::bad_alloc during length set");
4626     QString w5 = qmlFile.toString() + QLatin1String(":83: std::bad_alloc during indexed set");
4627     QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
4628     QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
4629     QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
4630     QTest::ignoreMessage(QtWarningMsg, qPrintable(w4));
4631     QTest::ignoreMessage(QtWarningMsg, qPrintable(w5));
4632     QMetaObject::invokeMethod(object, "indexedAccess");
4633     QVERIFY(object->property("success").toBool());
4634     delete object;
4635 }
4636
4637 void tst_qdeclarativeecmascript::sequenceConversionThreads()
4638 {
4639     // ensure that sequence conversion operations work correctly in a worker thread
4640     // and that serialisation between the main and worker thread succeeds.
4641     QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml");
4642     QDeclarativeComponent component(&engine, qmlFile);
4643     QObject *object = component.create();
4644     QVERIFY(object != 0);
4645
4646     QMetaObject::invokeMethod(object, "testIntSequence");
4647     QTRY_VERIFY(object->property("finished").toBool());
4648     QVERIFY(object->property("success").toBool());
4649
4650     QMetaObject::invokeMethod(object, "testQrealSequence");
4651     QTRY_VERIFY(object->property("finished").toBool());
4652     QVERIFY(object->property("success").toBool());
4653
4654     QMetaObject::invokeMethod(object, "testBoolSequence");
4655     QTRY_VERIFY(object->property("finished").toBool());
4656     QVERIFY(object->property("success").toBool());
4657
4658     QMetaObject::invokeMethod(object, "testStringSequence");
4659     QTRY_VERIFY(object->property("finished").toBool());
4660     QVERIFY(object->property("success").toBool());
4661
4662     QMetaObject::invokeMethod(object, "testQStringSequence");
4663     QTRY_VERIFY(object->property("finished").toBool());
4664     QVERIFY(object->property("success").toBool());
4665
4666     QMetaObject::invokeMethod(object, "testUrlSequence");
4667     QTRY_VERIFY(object->property("finished").toBool());
4668     QVERIFY(object->property("success").toBool());
4669
4670     QMetaObject::invokeMethod(object, "testVariantSequence");
4671     QTRY_VERIFY(object->property("finished").toBool());
4672     QVERIFY(object->property("success").toBool());
4673
4674     delete object;
4675 }
4676
4677 void tst_qdeclarativeecmascript::sequenceConversionBindings()
4678 {
4679     {
4680         QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml");
4681         QDeclarativeComponent component(&engine, qmlFile);
4682         QObject *object = component.create();
4683         QVERIFY(object != 0);
4684         QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
4685         QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
4686         QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
4687         QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
4688         QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
4689         delete object;
4690     }
4691
4692     {
4693         QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml");
4694         QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
4695         QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
4696         QDeclarativeComponent component(&engine, qmlFile);
4697         QObject *object = component.create();
4698         QVERIFY(object != 0);
4699         delete object;
4700     }
4701 }
4702
4703 void tst_qdeclarativeecmascript::sequenceConversionCopy()
4704 {
4705     QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml");
4706     QDeclarativeComponent component(&engine, qmlFile);
4707     QObject *object = component.create();
4708     QVERIFY(object != 0);
4709     QMetaObject::invokeMethod(object, "testCopySequences");
4710     QCOMPARE(object->property("success").toBool(), true);
4711     QMetaObject::invokeMethod(object, "readSequenceCopyElements");
4712     QCOMPARE(object->property("success").toBool(), true);
4713     QMetaObject::invokeMethod(object, "testEqualitySemantics");
4714     QCOMPARE(object->property("success").toBool(), true);
4715     delete object;
4716 }
4717
4718 void tst_qdeclarativeecmascript::assignSequenceTypes()
4719 {
4720     // test binding array to sequence type property
4721     {
4722     QDeclarativeComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml"));
4723     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
4724     QVERIFY(object != 0);
4725     QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
4726     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
4727     QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
4728     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
4729     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
4730     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
4731     delete object;
4732     }
4733
4734     // test binding literal to sequence type property
4735     {
4736     QDeclarativeComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml"));
4737     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
4738     QVERIFY(object != 0);
4739     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
4740     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
4741     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
4742     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
4743     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
4744     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
4745     delete object;
4746     }
4747
4748     // test binding single value to sequence type property
4749     {
4750     QDeclarativeComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml"));
4751     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
4752     QVERIFY(object != 0);
4753     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
4754     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
4755     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
4756     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
4757     delete object;
4758     }
4759
4760     // test assigning array to sequence type property in js function
4761     {
4762     QDeclarativeComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml"));
4763     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
4764     QVERIFY(object != 0);
4765     QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
4766     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
4767     QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
4768     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
4769     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
4770     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
4771     delete object;
4772     }
4773
4774     // test assigning literal to sequence type property in js function
4775     {
4776     QDeclarativeComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml"));
4777     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
4778     QVERIFY(object != 0);
4779     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
4780     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
4781     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
4782     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
4783     QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
4784     QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
4785     delete object;
4786     }
4787
4788     // test assigning single value to sequence type property in js function
4789     {
4790     QDeclarativeComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml"));
4791     MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
4792     QVERIFY(object != 0);
4793     QCOMPARE(object->intListProperty(), (QList<int>() << 1));
4794     QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
4795     QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
4796     QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
4797     delete object;
4798     }
4799
4800     // test QList<QUrl> literal assignment and binding assignment causes url resolution when required
4801     {
4802     QDeclarativeComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml"));
4803     QObject *object = component.create();
4804     QVERIFY(object != 0);
4805     MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
4806     MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
4807     MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
4808     MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
4809     MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco5"));
4810     QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0);
4811     QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
4812     QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
4813     QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
4814     QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
4815     QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
4816     delete object;
4817     }
4818 }
4819
4820 // Test that assigning a null object works 
4821 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
4822 void tst_qdeclarativeecmascript::nullObjectBinding()
4823 {
4824     QDeclarativeComponent component(&engine, testFileUrl("nullObjectBinding.qml"));
4825
4826     QObject *object = component.create();
4827     QVERIFY(object != 0);
4828
4829     QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
4830
4831     delete object;
4832 }
4833
4834 // Test that bindings don't evaluate once the engine has been destroyed
4835 void tst_qdeclarativeecmascript::deletedEngine()
4836 {
4837     QDeclarativeEngine *engine = new QDeclarativeEngine;
4838     QDeclarativeComponent component(engine, testFileUrl("deletedEngine.qml"));
4839
4840     QObject *object = component.create();
4841     QVERIFY(object != 0);
4842
4843     QCOMPARE(object->property("a").toInt(), 39);
4844     object->setProperty("b", QVariant(9));
4845     QCOMPARE(object->property("a").toInt(), 117);
4846
4847     delete engine;
4848
4849     QCOMPARE(object->property("a").toInt(), 117);
4850     object->setProperty("b", QVariant(10));
4851     QCOMPARE(object->property("a").toInt(), 117);
4852
4853     delete object;
4854 }
4855
4856 // Test the crashing part of QTBUG-9705
4857 void tst_qdeclarativeecmascript::libraryScriptAssert()
4858 {
4859     QDeclarativeComponent component(&engine, testFileUrl("libraryScriptAssert.qml"));
4860
4861     QObject *object = component.create();
4862     QVERIFY(object != 0);
4863
4864     delete object;
4865 }
4866
4867 void tst_qdeclarativeecmascript::variantsAssignedUndefined()
4868 {
4869     QDeclarativeComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml"));
4870
4871     QObject *object = component.create();
4872     QVERIFY(object != 0);
4873
4874     QCOMPARE(object->property("test1").toInt(), 10);
4875     QCOMPARE(object->property("test2").toInt(), 11);
4876
4877     object->setProperty("runTest", true);
4878
4879     QCOMPARE(object->property("test1"), QVariant());
4880     QCOMPARE(object->property("test2"), QVariant());
4881
4882
4883     delete object;
4884 }
4885
4886 void tst_qdeclarativeecmascript::qtbug_9792()
4887 {
4888     QDeclarativeComponent component(&engine, testFileUrl("qtbug_9792.qml"));
4889
4890     QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext());
4891
4892     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context));
4893     QVERIFY(object != 0);
4894
4895     QTest::ignoreMessage(QtDebugMsg, "Hello world!");
4896     object->basicSignal();
4897
4898     delete context;
4899
4900     transientErrorsMsgCount = 0;
4901     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
4902
4903     object->basicSignal();
4904     
4905     qInstallMsgHandler(old);
4906
4907     QCOMPARE(transientErrorsMsgCount, 0);
4908
4909     delete object;
4910 }
4911
4912 // Verifies that QDeclarativeGuard<>s used in the vmemetaobject are cleaned correctly
4913 void tst_qdeclarativeecmascript::qtcreatorbug_1289()
4914 {
4915     QDeclarativeComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml"));
4916
4917     QObject *o = component.create();
4918     QVERIFY(o != 0);
4919
4920     QObject *nested = qvariant_cast<QObject *>(o->property("object"));
4921     QVERIFY(nested != 0);
4922
4923     QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
4924
4925     delete nested;
4926     nested = qvariant_cast<QObject *>(o->property("object"));
4927     QVERIFY(nested == 0);
4928
4929     // If the bug is present, the next line will crash
4930     delete o;
4931 }
4932
4933 // Test that we shut down without stupid warnings
4934 void tst_qdeclarativeecmascript::noSpuriousWarningsAtShutdown()
4935 {
4936     {
4937     QDeclarativeComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml"));
4938
4939     QObject *o = component.create();
4940
4941     transientErrorsMsgCount = 0;
4942     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
4943
4944     delete o;
4945
4946     qInstallMsgHandler(old);
4947
4948     QCOMPARE(transientErrorsMsgCount, 0);
4949     }
4950
4951
4952     {
4953     QDeclarativeComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml"));
4954
4955     QObject *o = component.create();
4956
4957     transientErrorsMsgCount = 0;
4958     QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
4959
4960     delete o;
4961
4962     qInstallMsgHandler(old);
4963
4964     QCOMPARE(transientErrorsMsgCount, 0);
4965     }
4966 }
4967
4968 void tst_qdeclarativeecmascript::canAssignNullToQObject()
4969 {
4970     {
4971     QDeclarativeComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml"));
4972
4973     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4974     QVERIFY(o != 0);
4975
4976     QVERIFY(o->objectProperty() != 0);
4977
4978     o->setProperty("runTest", true);
4979
4980     QVERIFY(o->objectProperty() == 0);
4981
4982     delete o;
4983     }
4984
4985     {
4986     QDeclarativeComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml"));
4987
4988     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
4989     QVERIFY(o != 0);
4990
4991     QVERIFY(o->objectProperty() == 0);
4992
4993     delete o;
4994     }
4995 }
4996
4997 void tst_qdeclarativeecmascript::functionAssignment_fromBinding()
4998 {
4999     QDeclarativeComponent component(&engine, testFileUrl("functionAssignment.1.qml"));
5000
5001     QString url = component.url().toString();
5002     QString warning = url + ":4: Unable to assign a function to a property.";
5003     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5004     
5005     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5006     QVERIFY(o != 0);
5007
5008     QVERIFY(!o->property("a").isValid());
5009
5010     delete o;
5011 }
5012
5013 void tst_qdeclarativeecmascript::functionAssignment_fromJS()
5014 {
5015     QFETCH(QString, triggerProperty);
5016
5017     QDeclarativeComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5018     QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5019
5020     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5021     QVERIFY(o != 0);
5022     QVERIFY(!o->property("a").isValid());
5023
5024     o->setProperty("aNumber", QVariant(5));
5025     o->setProperty(triggerProperty.toUtf8().constData(), true);
5026     QCOMPARE(o->property("a"), QVariant(50));
5027
5028     o->setProperty("aNumber", QVariant(10));
5029     QCOMPARE(o->property("a"), QVariant(100));
5030
5031     delete o;
5032 }
5033
5034 void tst_qdeclarativeecmascript::functionAssignment_fromJS_data()
5035 {
5036     QTest::addColumn<QString>("triggerProperty");
5037
5038     QTest::newRow("assign to property") << "assignToProperty";
5039     QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile";
5040
5041     QTest::newRow("assign to value type") << "assignToValueType";
5042
5043     QTest::newRow("use 'this'") << "assignWithThis";
5044     QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile";
5045 }
5046
5047 void tst_qdeclarativeecmascript::functionAssignmentfromJS_invalid()
5048 {
5049     QDeclarativeComponent component(&engine, testFileUrl("functionAssignment.2.qml"));
5050     QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
5051
5052     MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
5053     QVERIFY(o != 0);
5054     QVERIFY(!o->property("a").isValid());
5055
5056     o->setProperty("assignFuncWithoutReturn", true);
5057     QVERIFY(!o->property("a").isValid());
5058
5059     QString url = component.url().toString();
5060     QString warning = url + ":67: Unable to assign QString to int";
5061     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5062     o->setProperty("assignWrongType", true);
5063
5064     warning = url + ":71: Unable to assign QString to int";
5065     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
5066     o->setProperty("assignWrongTypeToValueType", true);
5067
5068     delete o;
5069 }
5070
5071 void tst_qdeclarativeecmascript::eval()
5072 {
5073     QDeclarativeComponent component(&engine, testFileUrl("eval.qml"));
5074
5075     QObject *o = component.create();
5076     QVERIFY(o != 0);
5077
5078     QCOMPARE(o->property("test1").toBool(), true);
5079     QCOMPARE(o->property("test2").toBool(), true);
5080     QCOMPARE(o->property("test3").toBool(), true);
5081     QCOMPARE(o->property("test4").toBool(), true);
5082     QCOMPARE(o->property("test5").toBool(), true);
5083
5084     delete o;
5085 }
5086
5087 void tst_qdeclarativeecmascript::function()
5088 {
5089     QDeclarativeComponent component(&engine, testFileUrl("function.qml"));
5090
5091     QObject *o = component.create();
5092     QVERIFY(o != 0);
5093
5094     QCOMPARE(o->property("test1").toBool(), true);
5095     QCOMPARE(o->property("test2").toBool(), true);
5096     QCOMPARE(o->property("test3").toBool(), true);
5097
5098     delete o;
5099 }
5100
5101 void tst_qdeclarativeecmascript::functionException()
5102 {
5103     // QTBUG-24037 - shouldn't crash.
5104     QString errstr = testFileUrl("v8functionException.qml").toString() + QLatin1String(":13: SyntaxError: Unexpected token ILLEGAL");
5105     QTest::ignoreMessage(QtWarningMsg, qPrintable(errstr));
5106     QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Exception occurred during compilation of function: dynamicSlot()");
5107     QDeclarativeComponent component(&engine, testFileUrl("v8functionException.qml"));
5108     QObject *o = component.create();
5109     QVERIFY(o != 0);
5110     QMetaObject::invokeMethod(o, "dynamicSlot");
5111     delete o;
5112 }
5113
5114 // Test the "Qt.include" method
5115 void tst_qdeclarativeecmascript::include()
5116 {
5117     // Non-library relative include
5118     {
5119     QDeclarativeComponent component(&engine, testFileUrl("include.qml"));
5120     QObject *o = component.create();
5121     QVERIFY(o != 0);
5122
5123     QCOMPARE(o->property("test0").toInt(), 99);
5124     QCOMPARE(o->property("test1").toBool(), true);
5125     QCOMPARE(o->property("test2").toBool(), true);
5126     QCOMPARE(o->property("test2_1").toBool(), true);
5127     QCOMPARE(o->property("test3").toBool(), true);
5128     QCOMPARE(o->property("test3_1").toBool(), true);
5129
5130     delete o;
5131     }
5132
5133     // Library relative include
5134     {
5135     QDeclarativeComponent component(&engine, testFileUrl("include_shared.qml"));
5136     QObject *o = component.create();
5137     QVERIFY(o != 0);
5138
5139     QCOMPARE(o->property("test0").toInt(), 99);
5140     QCOMPARE(o->property("test1").toBool(), true);
5141     QCOMPARE(o->property("test2").toBool(), true);
5142     QCOMPARE(o->property("test2_1").toBool(), true);
5143     QCOMPARE(o->property("test3").toBool(), true);
5144     QCOMPARE(o->property("test3_1").toBool(), true);
5145
5146     delete o;
5147     }
5148
5149     // Callback
5150     {
5151     QDeclarativeComponent component(&engine, testFileUrl("include_callback.qml"));
5152     QObject *o = component.create();
5153     QVERIFY(o != 0);
5154
5155     QCOMPARE(o->property("test1").toBool(), true);
5156     QCOMPARE(o->property("test2").toBool(), true);
5157     QCOMPARE(o->property("test3").toBool(), true);
5158     QCOMPARE(o->property("test4").toBool(), true);
5159     QCOMPARE(o->property("test5").toBool(), true);
5160     QCOMPARE(o->property("test6").toBool(), true);
5161
5162     delete o;
5163     }
5164
5165     // Including file with ".pragma library"
5166     {
5167     QDeclarativeComponent component(&engine, testFileUrl("include_pragma.qml"));
5168     QObject *o = component.create();
5169     QVERIFY(o != 0);
5170     QCOMPARE(o->property("test1").toInt(), 100);
5171
5172     delete o;
5173     }
5174
5175     // Remote - success
5176     {
5177     TestHTTPServer server(8111);
5178     QVERIFY(server.isValid());
5179     server.serveDirectory(dataDirectory());
5180
5181     QDeclarativeComponent component(&engine, testFileUrl("include_remote.qml"));
5182     QObject *o = component.create();
5183     QVERIFY(o != 0);
5184
5185     QTRY_VERIFY(o->property("done").toBool() == true);
5186     QTRY_VERIFY(o->property("done2").toBool() == true);
5187
5188     QCOMPARE(o->property("test1").toBool(), true);
5189     QCOMPARE(o->property("test2").toBool(), true);
5190     QCOMPARE(o->property("test3").toBool(), true);
5191     QCOMPARE(o->property("test4").toBool(), true);
5192     QCOMPARE(o->property("test5").toBool(), true);
5193
5194     QCOMPARE(o->property("test6").toBool(), true);
5195     QCOMPARE(o->property("test7").toBool(), true);
5196     QCOMPARE(o->property("test8").toBool(), true);
5197     QCOMPARE(o->property("test9").toBool(), true);
5198     QCOMPARE(o->property("test10").toBool(), true);
5199
5200     delete o;
5201     }
5202
5203     // Remote - error
5204     {
5205     TestHTTPServer server(8111);
5206     QVERIFY(server.isValid());
5207     server.serveDirectory(dataDirectory());
5208
5209     QDeclarativeComponent component(&engine, testFileUrl("include_remote_missing.qml"));
5210     QObject *o = component.create();
5211     QVERIFY(o != 0);
5212
5213     QTRY_VERIFY(o->property("done").toBool() == true);
5214
5215     QCOMPARE(o->property("test1").toBool(), true);
5216     QCOMPARE(o->property("test2").toBool(), true);
5217     QCOMPARE(o->property("test3").toBool(), true);
5218
5219     delete o;
5220     }
5221 }
5222
5223 void tst_qdeclarativeecmascript::signalHandlers()
5224 {
5225     QDeclarativeComponent component(&engine, testFileUrl("signalHandlers.qml"));
5226     QObject *o = component.create();
5227     QVERIFY(o != 0);
5228
5229     QVERIFY(o->property("count").toInt() == 0);
5230     QMetaObject::invokeMethod(o, "testSignalCall");
5231     QCOMPARE(o->property("count").toInt(), 1);
5232
5233     QMetaObject::invokeMethod(o, "testSignalHandlerCall");
5234     QCOMPARE(o->property("count").toInt(), 1);
5235     QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
5236
5237     QVERIFY(o->property("funcCount").toInt() == 0);
5238     QMetaObject::invokeMethod(o, "testSignalConnection");
5239     QCOMPARE(o->property("funcCount").toInt(), 1);
5240
5241     QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
5242     QCOMPARE(o->property("funcCount").toInt(), 2);
5243
5244     QMetaObject::invokeMethod(o, "testSignalDefined");
5245     QCOMPARE(o->property("definedResult").toBool(), true);
5246
5247     QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
5248     QCOMPARE(o->property("definedHandlerResult").toBool(), true);
5249
5250     delete o;
5251 }
5252
5253 void tst_qdeclarativeecmascript::qtbug_10696()
5254 {
5255     QDeclarativeComponent component(&engine, testFileUrl("qtbug_10696.qml"));
5256     QObject *o = component.create();
5257     QVERIFY(o != 0);
5258     delete o;
5259 }
5260
5261 void tst_qdeclarativeecmascript::qtbug_11606()
5262 {
5263     QDeclarativeComponent component(&engine, testFileUrl("qtbug_11606.qml"));
5264     QObject *o = component.create();
5265     QVERIFY(o != 0);
5266     QCOMPARE(o->property("test").toBool(), true);
5267     delete o;
5268 }
5269
5270 void tst_qdeclarativeecmascript::qtbug_11600()
5271 {
5272     QDeclarativeComponent component(&engine, testFileUrl("qtbug_11600.qml"));
5273     QObject *o = component.create();
5274     QVERIFY(o != 0);
5275     QCOMPARE(o->property("test").toBool(), true);
5276     delete o;
5277 }
5278
5279 void tst_qdeclarativeecmascript::qtbug_21864()
5280 {
5281     QDeclarativeComponent component(&engine, testFileUrl("qtbug_21864.qml"));
5282     QObject *o = component.create();
5283     QVERIFY(o != 0);
5284     QCOMPARE(o->property("test").toBool(), true);
5285     delete o;
5286 }
5287
5288 void tst_qdeclarativeecmascript::rewriteMultiLineStrings()
5289 {
5290     // QTBUG-23387
5291     QDeclarativeComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml"));
5292     QObject *o = component.create();
5293     QVERIFY(o != 0);
5294     QTRY_COMPARE(o->property("test").toBool(), true);
5295     delete o;
5296 }
5297
5298 void tst_qdeclarativeecmascript::qobjectConnectionListExceptionHandling()
5299 {
5300     // QTBUG-23375
5301     QDeclarativeComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml"));
5302     QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined");
5303     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5304     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5305     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5306     QObject *o = component.create();
5307     QVERIFY(o != 0);
5308     QCOMPARE(o->property("test").toBool(), true);
5309     delete o;
5310 }
5311
5312 // Reading and writing non-scriptable properties should fail
5313 void tst_qdeclarativeecmascript::nonscriptable()
5314 {
5315     QDeclarativeComponent component(&engine, testFileUrl("nonscriptable.qml"));
5316     QObject *o = component.create();
5317     QVERIFY(o != 0);
5318     QCOMPARE(o->property("readOk").toBool(), true);
5319     QCOMPARE(o->property("writeOk").toBool(), true);
5320     delete o;
5321 }
5322
5323 // deleteLater() should not be callable from QML
5324 void tst_qdeclarativeecmascript::deleteLater()
5325 {
5326     QDeclarativeComponent component(&engine, testFileUrl("deleteLater.qml"));
5327     QObject *o = component.create();
5328     QVERIFY(o != 0);
5329     QCOMPARE(o->property("test").toBool(), true);
5330     delete o;
5331 }
5332
5333 void tst_qdeclarativeecmascript::in()
5334 {
5335     QDeclarativeComponent component(&engine, testFileUrl("in.qml"));
5336     QObject *o = component.create();
5337     QVERIFY(o != 0);
5338     QCOMPARE(o->property("test1").toBool(), true);
5339     QCOMPARE(o->property("test2").toBool(), true);
5340     delete o;
5341 }
5342
5343 void tst_qdeclarativeecmascript::typeOf()
5344 {
5345     QDeclarativeComponent component(&engine, testFileUrl("typeOf.qml"));
5346
5347     // These warnings should not happen once QTBUG-21864 is fixed
5348     QString warning1 = component.url().toString() + QLatin1String(":16: Error: Cannot assign [undefined] to QString");
5349     QString warning2 = component.url().resolved(QUrl("typeOf.js")).toString() + QLatin1String(":1: ReferenceError: Can't find variable: a");
5350
5351     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
5352     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
5353
5354     QObject *o = component.create();
5355     QVERIFY(o != 0);
5356
5357     QEXPECT_FAIL("", "QTBUG-21864", Abort);
5358     QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
5359     QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
5360     QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
5361     QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
5362     QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
5363     QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
5364     QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
5365     QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
5366     QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
5367
5368     delete o;
5369 }
5370
5371 void tst_qdeclarativeecmascript::sharedAttachedObject()
5372 {
5373     QDeclarativeComponent component(&engine, testFileUrl("sharedAttachedObject.qml"));
5374     QObject *o = component.create();
5375     QVERIFY(o != 0);
5376     QCOMPARE(o->property("test1").toBool(), true);
5377     QCOMPARE(o->property("test2").toBool(), true);
5378     delete o;
5379 }
5380
5381 // QTBUG-13999
5382 void tst_qdeclarativeecmascript::objectName()
5383 {
5384     QDeclarativeComponent component(&engine, testFileUrl("objectName.qml"));
5385     QObject *o = component.create();
5386     QVERIFY(o != 0);
5387
5388     QCOMPARE(o->property("test1").toString(), QString("hello"));
5389     QCOMPARE(o->property("test2").toString(), QString("ell"));
5390
5391     o->setObjectName("world");
5392
5393     QCOMPARE(o->property("test1").toString(), QString("world"));
5394     QCOMPARE(o->property("test2").toString(), QString("orl"));
5395
5396     delete o;
5397 }
5398
5399 void tst_qdeclarativeecmascript::writeRemovesBinding()
5400 {
5401     QDeclarativeComponent component(&engine, testFileUrl("writeRemovesBinding.qml"));
5402     QObject *o = component.create();
5403     QVERIFY(o != 0);
5404
5405     QCOMPARE(o->property("test").toBool(), true);
5406
5407     delete o;
5408 }
5409
5410 // Test bindings assigned to alias properties actually assign to the alias' target
5411 void tst_qdeclarativeecmascript::aliasBindingsAssignCorrectly()
5412 {
5413     QDeclarativeComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml"));
5414     QObject *o = component.create();
5415     QVERIFY(o != 0);
5416
5417     QCOMPARE(o->property("test").toBool(), true);
5418
5419     delete o;
5420 }
5421
5422 // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
5423 void tst_qdeclarativeecmascript::aliasBindingsOverrideTarget()
5424 {
5425     { 
5426     QDeclarativeComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml"));
5427     QObject *o = component.create();
5428     QVERIFY(o != 0);
5429
5430     QCOMPARE(o->property("test").toBool(), true);
5431
5432     delete o;
5433     }
5434
5435     {
5436     QDeclarativeComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml"));
5437     QObject *o = component.create();
5438     QVERIFY(o != 0);
5439
5440     QCOMPARE(o->property("test").toBool(), true);
5441
5442     delete o;
5443     }
5444
5445     {
5446     QDeclarativeComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml"));
5447     QObject *o = component.create();
5448     QVERIFY(o != 0);
5449
5450     QCOMPARE(o->property("test").toBool(), true);
5451
5452     delete o;
5453     }
5454 }
5455
5456 // Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
5457 void tst_qdeclarativeecmascript::aliasWritesOverrideBindings()
5458 {
5459     {
5460     QDeclarativeComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml"));
5461     QObject *o = component.create();
5462     QVERIFY(o != 0);
5463
5464     QCOMPARE(o->property("test").toBool(), true);
5465
5466     delete o;
5467     }
5468
5469     {
5470     QDeclarativeComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml"));
5471     QObject *o = component.create();
5472     QVERIFY(o != 0);
5473
5474     QCOMPARE(o->property("test").toBool(), true);
5475
5476     delete o;
5477     }
5478
5479     {
5480     QDeclarativeComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml"));
5481     QObject *o = component.create();
5482     QVERIFY(o != 0);
5483
5484     QCOMPARE(o->property("test").toBool(), true);
5485
5486     delete o;
5487     }
5488 }
5489
5490 // Allow an alais to a composite element
5491 // QTBUG-20200
5492 void tst_qdeclarativeecmascript::aliasToCompositeElement()
5493 {
5494     QDeclarativeComponent component(&engine, testFileUrl("aliasToCompositeElement.qml"));
5495
5496     QObject *object = component.create();
5497     QVERIFY(object != 0);
5498
5499     delete object;
5500 }
5501
5502 void tst_qdeclarativeecmascript::qtbug_20344()
5503 {
5504     QDeclarativeComponent component(&engine, testFileUrl("qtbug_20344.qml"));
5505
5506     QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
5507     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5508
5509     QObject *object = component.create();
5510     QVERIFY(object != 0);
5511
5512     delete object;
5513 }
5514
5515 void tst_qdeclarativeecmascript::revisionErrors()
5516 {
5517     {
5518         QDeclarativeComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml"));
5519         QString url = component.url().toString();
5520
5521         QString warning1 = url + ":8: ReferenceError: Can't find variable: prop2";
5522         QString warning2 = url + ":11: ReferenceError: Can't find variable: prop2";
5523         QString warning3 = url + ":13: ReferenceError: Can't find variable: method2";
5524
5525         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
5526         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
5527         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
5528         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5529         QVERIFY(object != 0);
5530         delete object;
5531     }
5532     {
5533         QDeclarativeComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml"));
5534         QString url = component.url().toString();
5535
5536         // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
5537         // method2, prop2 from MyRevisionedClass not available
5538         // method4, prop4 from MyRevisionedSubclass not available
5539         QString warning1 = url + ":8: ReferenceError: Can't find variable: prop2";
5540         QString warning2 = url + ":14: ReferenceError: Can't find variable: prop2";
5541         QString warning3 = url + ":10: ReferenceError: Can't find variable: prop4";
5542         QString warning4 = url + ":16: ReferenceError: Can't find variable: prop4";
5543         QString warning5 = url + ":20: ReferenceError: Can't find variable: method2";
5544
5545         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
5546         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
5547         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
5548         QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData());
5549         QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData());
5550         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5551         QVERIFY(object != 0);
5552         delete object;
5553     }
5554     {
5555         QDeclarativeComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml"));
5556         QString url = component.url().toString();
5557
5558         // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
5559         // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
5560         QString warning1 = url + ":30: ReferenceError: Can't find variable: methodD";
5561         QString warning2 = url + ":10: ReferenceError: Can't find variable: propD";
5562         QString warning3 = url + ":20: ReferenceError: Can't find variable: propD";
5563         QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
5564         QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
5565         QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
5566         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5567         QVERIFY(object != 0);
5568         delete object;
5569     }
5570 }
5571
5572 void tst_qdeclarativeecmascript::revision()
5573 {
5574     {
5575         QDeclarativeComponent component(&engine, testFileUrl("metaobjectRevision.qml"));
5576         QString url = component.url().toString();
5577
5578         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5579         QVERIFY(object != 0);
5580         delete object;
5581     }
5582     {
5583         QDeclarativeComponent component(&engine, testFileUrl("metaobjectRevision2.qml"));
5584         QString url = component.url().toString();
5585
5586         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5587         QVERIFY(object != 0);
5588         delete object;
5589     }
5590     {
5591         QDeclarativeComponent component(&engine, testFileUrl("metaobjectRevision3.qml"));
5592         QString url = component.url().toString();
5593
5594         MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create());
5595         QVERIFY(object != 0);
5596         delete object;
5597     }
5598     // Test that non-root classes can resolve revisioned methods
5599     {
5600         QDeclarativeComponent component(&engine, testFileUrl("metaobjectRevision4.qml"));
5601
5602         QObject *object = component.create();
5603         QVERIFY(object != 0);
5604         QCOMPARE(object->property("test").toReal(), 11.);
5605         delete object;
5606     }
5607 }
5608
5609 void tst_qdeclarativeecmascript::realToInt()
5610 {
5611     QDeclarativeComponent component(&engine, testFileUrl("realToInt.qml"));
5612     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
5613     QVERIFY(object != 0);
5614
5615     QMetaObject::invokeMethod(object, "test1");
5616     QCOMPARE(object->value(), int(4));
5617     QMetaObject::invokeMethod(object, "test2");
5618     QCOMPARE(object->value(), int(8));
5619 }
5620
5621 void tst_qdeclarativeecmascript::urlProperty()
5622 {
5623     {
5624         QDeclarativeComponent component(&engine, testFileUrl("urlProperty.1.qml"));
5625         MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
5626         QVERIFY(object != 0);
5627         object->setStringProperty("http://qt-project.org");
5628         QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
5629         QCOMPARE(object->intProperty(), 123);
5630         QCOMPARE(object->value(), 1);
5631         QCOMPARE(object->property("result").toBool(), true);
5632     }
5633 }
5634
5635 void tst_qdeclarativeecmascript::urlPropertyWithEncoding()
5636 {
5637     {
5638         QDeclarativeComponent component(&engine, testFileUrl("urlProperty.2.qml"));
5639         MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
5640         QVERIFY(object != 0);
5641         object->setStringProperty("http://qt-project.org");
5642         QUrl encoded;
5643         encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
5644         QCOMPARE(object->urlProperty(), encoded);
5645         QCOMPARE(object->value(), 0);   // Interpreting URL as string yields canonicalised version
5646         QCOMPARE(object->property("result").toBool(), true);
5647     }
5648 }
5649
5650 void tst_qdeclarativeecmascript::urlListPropertyWithEncoding()
5651 {
5652     {
5653         QDeclarativeComponent component(&engine, testFileUrl("urlListProperty.qml"));
5654         QObject *object = component.create();
5655         QVERIFY(object != 0);
5656         MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1"));
5657         MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2"));
5658         MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3"));
5659         MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4"));
5660         QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0);
5661         QUrl encoded;
5662         encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
5663         QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded));
5664         QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded));
5665         QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << encoded << encoded));
5666         QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << encoded << encoded));
5667         delete object;
5668     }
5669 }
5670
5671 void tst_qdeclarativeecmascript::dynamicString()
5672 {
5673     QDeclarativeComponent component(&engine, testFileUrl("dynamicString.qml"));
5674     QObject *object = component.create();
5675     QVERIFY(object != 0);
5676     QCOMPARE(object->property("stringProperty").toString(),
5677              QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
5678 }
5679
5680 void tst_qdeclarativeecmascript::automaticSemicolon()
5681 {
5682     QDeclarativeComponent component(&engine, testFileUrl("automaticSemicolon.qml"));
5683     QObject *object = component.create();
5684     QVERIFY(object != 0);
5685 }
5686
5687 void tst_qdeclarativeecmascript::unaryExpression()
5688 {
5689     QDeclarativeComponent component(&engine, testFileUrl("unaryExpression.qml"));
5690     QObject *object = component.create();
5691     QVERIFY(object != 0);
5692 }
5693
5694 // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
5695 void tst_qdeclarativeecmascript::doubleEvaluate()
5696 {
5697     QDeclarativeComponent component(&engine, testFileUrl("doubleEvaluate.qml"));
5698     QObject *object = component.create();
5699     QVERIFY(object != 0);
5700     WriteCounter *wc = qobject_cast<WriteCounter *>(object);
5701     QVERIFY(wc != 0);
5702     QCOMPARE(wc->count(), 1);
5703
5704     wc->setProperty("x", 9);
5705
5706     QCOMPARE(wc->count(), 2);
5707
5708     delete object;
5709 }
5710
5711 static QStringList messages;
5712 static void captureMsgHandler(QtMsgType, const char *msg)
5713 {
5714     messages.append(QLatin1String(msg));
5715 }
5716
5717 void tst_qdeclarativeecmascript::nonNotifyable()
5718 {
5719     QV4Compiler::enableV4(false);
5720     QDeclarativeComponent component(&engine, testFileUrl("nonNotifyable.qml"));
5721     QV4Compiler::enableV4(true);
5722
5723     QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
5724     messages.clear();
5725     QObject *object = component.create();
5726     qInstallMsgHandler(old);
5727
5728     QVERIFY(object != 0);
5729
5730     QString expected1 = QLatin1String("QDeclarativeExpression: Expression ") +
5731                         component.url().toString() +
5732                         QLatin1String(":5 depends on non-NOTIFYable properties:");
5733     QString expected2 = QLatin1String("    ") +
5734                         QLatin1String(object->metaObject()->className()) +
5735                         QLatin1String("::value");
5736
5737     QCOMPARE(messages.length(), 2);
5738     QCOMPARE(messages.at(0), expected1);
5739     QCOMPARE(messages.at(1), expected2);
5740
5741     delete object;
5742 }
5743
5744 void tst_qdeclarativeecmascript::forInLoop()
5745 {
5746     QDeclarativeComponent component(&engine, testFileUrl("forInLoop.qml"));
5747     QObject *object = component.create();
5748     QVERIFY(object != 0);
5749
5750     QMetaObject::invokeMethod(object, "listProperty");
5751
5752     QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
5753     QCOMPARE(r.size(), 3);
5754     QCOMPARE(r[0],QLatin1String("0=obj1"));
5755     QCOMPARE(r[1],QLatin1String("1=obj2"));
5756     QCOMPARE(r[2],QLatin1String("2=obj3"));
5757
5758     //TODO: should test for in loop for other objects (such as QObjects) as well.
5759
5760     delete object;
5761 }
5762
5763 // An object the binding depends on is deleted while the binding is still running
5764 void tst_qdeclarativeecmascript::deleteWhileBindingRunning()
5765 {
5766     QDeclarativeComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml"));
5767     QObject *object = component.create();
5768     QVERIFY(object != 0);
5769     delete object;
5770 }
5771
5772 void tst_qdeclarativeecmascript::qtbug_22679()
5773 {
5774     MyQmlObject object;
5775     object.setStringProperty(QLatin1String("Please work correctly"));
5776     engine.rootContext()->setContextProperty("contextProp", &object);
5777
5778     QDeclarativeComponent component(&engine, testFileUrl("qtbug_22679.qml"));
5779     qRegisterMetaType<QList<QDeclarativeError> >("QList<QDeclarativeError>");
5780     QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QDeclarativeError>)));
5781
5782     QObject *o = component.create();
5783     QVERIFY(o != 0);
5784     QCOMPARE(warningsSpy.count(), 0);
5785     delete o;
5786 }
5787
5788 void tst_qdeclarativeecmascript::qtbug_22843_data()
5789 {
5790     QTest::addColumn<bool>("library");
5791
5792     QTest::newRow("without .pragma library") << false;
5793     QTest::newRow("with .pragma library") << true;
5794 }
5795
5796 void tst_qdeclarativeecmascript::qtbug_22843()
5797 {
5798     QFETCH(bool, library);
5799
5800     QString fileName("qtbug_22843");
5801     if (library)
5802         fileName += QLatin1String(".library");
5803     fileName += QLatin1String(".qml");
5804
5805     QDeclarativeComponent component(&engine, testFileUrl(fileName));
5806     QString url = component.url().toString();
5807     QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )");
5808     QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'");
5809
5810     qRegisterMetaType<QList<QDeclarativeError> >("QList<QDeclarativeError>");
5811     QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QDeclarativeError>)));
5812     for (int x = 0; x < 3; ++x) {
5813         warningsSpy.clear();
5814         // For libraries, only the first import attempt should produce a
5815         // SyntaxError warning; subsequent component creation should not
5816         // attempt to reload the script.
5817         bool expectSyntaxError = !library || (x == 0);
5818         if (expectSyntaxError)
5819             QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
5820         QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
5821         QObject *object = component.create();
5822         QVERIFY(object != 0);
5823         QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
5824         delete object;
5825     }
5826 }
5827
5828
5829 void tst_qdeclarativeecmascript::switchStatement()
5830 {
5831     {
5832         QDeclarativeComponent component(&engine, testFileUrl("switchStatement.1.qml"));
5833         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5834         QVERIFY(object != 0);
5835
5836         // `object->value()' is the number of executed statements
5837
5838         object->setStringProperty("A");
5839         QCOMPARE(object->value(), 5);
5840
5841         object->setStringProperty("S");
5842         QCOMPARE(object->value(), 3);
5843
5844         object->setStringProperty("D");
5845         QCOMPARE(object->value(), 3);
5846
5847         object->setStringProperty("F");
5848         QCOMPARE(object->value(), 4);
5849
5850         object->setStringProperty("something else");
5851         QCOMPARE(object->value(), 1);
5852     }
5853
5854     {
5855         QDeclarativeComponent component(&engine, testFileUrl("switchStatement.2.qml"));
5856         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5857         QVERIFY(object != 0);
5858
5859         // `object->value()' is the number of executed statements
5860
5861         object->setStringProperty("A");
5862         QCOMPARE(object->value(), 5);
5863
5864         object->setStringProperty("S");
5865         QCOMPARE(object->value(), 3);
5866
5867         object->setStringProperty("D");
5868         QCOMPARE(object->value(), 3);
5869
5870         object->setStringProperty("F");
5871         QCOMPARE(object->value(), 3);
5872
5873         object->setStringProperty("something else");
5874         QCOMPARE(object->value(), 4);
5875     }
5876
5877     {
5878         QDeclarativeComponent component(&engine, testFileUrl("switchStatement.3.qml"));
5879         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5880         QVERIFY(object != 0);
5881
5882         // `object->value()' is the number of executed statements
5883
5884         object->setStringProperty("A");
5885         QCOMPARE(object->value(), 5);
5886
5887         object->setStringProperty("S");
5888         QCOMPARE(object->value(), 3);
5889
5890         object->setStringProperty("D");
5891         QCOMPARE(object->value(), 3);
5892
5893         object->setStringProperty("F");
5894         QCOMPARE(object->value(), 3);
5895
5896         object->setStringProperty("something else");
5897         QCOMPARE(object->value(), 6);
5898     }
5899
5900     {
5901         QDeclarativeComponent component(&engine, testFileUrl("switchStatement.4.qml"));
5902
5903         QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
5904         QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5905
5906         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5907         QVERIFY(object != 0);
5908
5909         // `object->value()' is the number of executed statements
5910
5911         object->setStringProperty("A");
5912         QCOMPARE(object->value(), 5);
5913
5914         object->setStringProperty("S");
5915         QCOMPARE(object->value(), 3);
5916
5917         object->setStringProperty("D");
5918         QCOMPARE(object->value(), 3);
5919
5920         object->setStringProperty("F");
5921         QCOMPARE(object->value(), 3);
5922
5923         QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5924
5925         object->setStringProperty("something else");
5926     }
5927
5928     {
5929         QDeclarativeComponent component(&engine, testFileUrl("switchStatement.5.qml"));
5930         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5931         QVERIFY(object != 0);
5932
5933         // `object->value()' is the number of executed statements
5934
5935         object->setStringProperty("A");
5936         QCOMPARE(object->value(), 1);
5937
5938         object->setStringProperty("S");
5939         QCOMPARE(object->value(), 1);
5940
5941         object->setStringProperty("D");
5942         QCOMPARE(object->value(), 1);
5943
5944         object->setStringProperty("F");
5945         QCOMPARE(object->value(), 1);
5946
5947         object->setStringProperty("something else");
5948         QCOMPARE(object->value(), 1);
5949     }
5950
5951     {
5952         QDeclarativeComponent component(&engine, testFileUrl("switchStatement.6.qml"));
5953         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5954         QVERIFY(object != 0);
5955
5956         // `object->value()' is the number of executed statements
5957
5958         object->setStringProperty("A");
5959         QCOMPARE(object->value(), 123);
5960
5961         object->setStringProperty("S");
5962         QCOMPARE(object->value(), 123);
5963
5964         object->setStringProperty("D");
5965         QCOMPARE(object->value(), 321);
5966
5967         object->setStringProperty("F");
5968         QCOMPARE(object->value(), 321);
5969
5970         object->setStringProperty("something else");
5971         QCOMPARE(object->value(), 0);
5972     }
5973 }
5974
5975 void tst_qdeclarativeecmascript::withStatement()
5976 {
5977     {
5978         QDeclarativeComponent component(&engine, testFileUrl("withStatement.1.qml"));
5979         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5980         QVERIFY(object != 0);
5981
5982         QCOMPARE(object->value(), 123);
5983     }
5984 }
5985
5986 void tst_qdeclarativeecmascript::tryStatement()
5987 {
5988     {
5989         QDeclarativeComponent component(&engine, testFileUrl("tryStatement.1.qml"));
5990         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5991         QVERIFY(object != 0);
5992
5993         QCOMPARE(object->value(), 123);
5994     }
5995
5996     {
5997         QDeclarativeComponent component(&engine, testFileUrl("tryStatement.2.qml"));
5998         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
5999         QVERIFY(object != 0);
6000
6001         QCOMPARE(object->value(), 321);
6002     }
6003
6004     {
6005         QDeclarativeComponent component(&engine, testFileUrl("tryStatement.3.qml"));
6006         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6007         QVERIFY(object != 0);
6008
6009         QCOMPARE(object->value(), 1);
6010     }
6011
6012     {
6013         QDeclarativeComponent component(&engine, testFileUrl("tryStatement.4.qml"));
6014         MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
6015         QVERIFY(object != 0);
6016
6017         QCOMPARE(object->value(), 1);
6018     }
6019 }
6020
6021 class CppInvokableWithQObjectDerived : public QObject
6022 {
6023     Q_OBJECT
6024 public:
6025     CppInvokableWithQObjectDerived() {}
6026     ~CppInvokableWithQObjectDerived() {}
6027
6028     Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data)
6029     {
6030         MyQmlObject *obj = new MyQmlObject();
6031         obj->setStringProperty(data);
6032         return obj;
6033     }
6034
6035     Q_INVOKABLE QString getStringProperty(MyQmlObject *obj)
6036     {
6037         return obj->stringProperty();
6038     }
6039 };
6040
6041 void tst_qdeclarativeecmascript::invokableWithQObjectDerived()
6042 {
6043     CppInvokableWithQObjectDerived invokable;
6044
6045     {
6046     QDeclarativeEngine engine;
6047     engine.rootContext()->setContextProperty("invokable", &invokable);
6048
6049     QDeclarativeComponent component(&engine, testFileUrl("qobjectDerivedArgument.qml"));
6050
6051     QObject *object = component.create();
6052
6053     QVERIFY(object != 0);
6054     QVERIFY(object->property("result").value<bool>() == true);
6055
6056     delete object;
6057     }
6058 }
6059
6060 QTEST_MAIN(tst_qdeclarativeecmascript)
6061
6062 #include "tst_qdeclarativeecmascript.moc"