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