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