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