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