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