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