X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=tests%2Fauto%2Fdeclarative%2Fqdeclarativeecmascript%2Ftst_qdeclarativeecmascript.cpp;h=9c844057a6935903f2ff9c7596a959bf4e7ce81f;hb=45b14259fc0cf704692df1c00da511527d1fba1d;hp=b732cd8193a9232e29b711526075e15398588771;hpb=d77218522eb480c8d528de18049cd7b604cdeb2a;p=profile%2Fivi%2Fqtdeclarative.git diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp index b732cd8..9c84405 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp +++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp @@ -1,8 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ ** ** This file is part of the test suite of the Qt Toolkit. ** @@ -35,10 +34,11 @@ ** ** ** +** ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include +#include #include #include #include @@ -49,14 +49,11 @@ #include #include #include +#include +#include #include "testtypes.h" #include "testhttpserver.h" -#include "../../../shared/util.h" - -#ifdef Q_OS_SYMBIAN -// In Symbian OS test data is located in applications private dir -#define SRCDIR "." -#endif +#include "../../shared/util.h" /* This test covers evaluation of ECMAScript expressions and bindings from within @@ -64,18 +61,8 @@ QML. This does not include static QML language issues. Static QML language issues are covered in qmllanguage */ -inline QUrl TEST_FILE(const QString &filename) -{ - QFileInfo fileInfo(__FILE__); - return QUrl::fromLocalFile(fileInfo.absoluteDir().filePath("data/" + filename)); -} - -inline QUrl TEST_FILE(const char *filename) -{ - return TEST_FILE(QLatin1String(filename)); -} -class tst_qdeclarativeecmascript : public QObject +class tst_qdeclarativeecmascript : public QDeclarativeDataTest { Q_OBJECT public: @@ -117,6 +104,7 @@ private slots: void objectHasOwnProperty(); void selfDeletingBinding(); void extendedObjectPropertyLookup(); + void extendedObjectPropertyLookup2(); void scriptErrors(); void functionErrors(); void propertyAssignmentErrors(); @@ -131,6 +119,7 @@ private slots: void jsObject(); void undefinedResetsProperty(); void listToVariant(); + void listAssignment(); void multiEngineObject(); void deletedObject(); void attachedPropertyScope(); @@ -145,19 +134,52 @@ private slots: void numberAssignment(); void propertySplicing(); void signalWithUnknownTypes(); + void signalWithJSValueInVariant_data(); + void signalWithJSValueInVariant(); + void signalWithJSValueInVariant_twoEngines_data(); + void signalWithJSValueInVariant_twoEngines(); + void signalWithQJSValue_data(); + void signalWithQJSValue(); void moduleApi_data(); void moduleApi(); + void importScripts_data(); void importScripts(); void scarceResources(); + void scarceResources_data(); + void scarceResources_other(); void propertyChangeSlots(); + void propertyVar_data(); + void propertyVar(); + void propertyVarCpp(); + void propertyVarOwnership(); + void propertyVarImplicitOwnership(); + void propertyVarReparent(); + void propertyVarReparentNullContext(); + void propertyVarCircular(); + void propertyVarCircular2(); + void propertyVarInheritance(); + void propertyVarInheritance2(); void elementAssign(); void objectPassThroughSignals(); + void objectConversion(); void booleanConversion(); void handleReferenceManagement(); + void stringArg(); + void readonlyDeclaration(); + void sequenceConversionRead(); + void sequenceConversionWrite(); + void sequenceConversionArray(); + void sequenceConversionThreads(); + void sequenceConversionBindings(); + void sequenceConversionCopy(); + void assignSequenceTypes(); + void qtbug_22464(); + void qtbug_21580(); void bug1(); void bug2(); void dynamicCreationCrash(); + void dynamicCreationOwnership(); void regExpBug(); void nullObjectBinding(); void deletedEngine(); @@ -176,9 +198,12 @@ private slots: void qtbug_10696(); void qtbug_11606(); void qtbug_11600(); + void qtbug_21864(); + void qobjectConnectionListExceptionHandling(); void nonscriptable(); void deleteLater(); void in(); + void typeOf(); void sharedAttachedObject(); void objectName(); void writeRemovesBinding(); @@ -187,27 +212,47 @@ private slots: void aliasWritesOverrideBindings(); void aliasToCompositeElement(); void realToInt(); + void urlProperty(); + void urlPropertyWithEncoding(); + void urlListPropertyWithEncoding(); void dynamicString(); void include(); void signalHandlers(); - + void doubleEvaluate(); + void forInLoop(); + void nonNotifyable(); + void deleteWhileBindingRunning(); void callQtInvokables(); void invokableObjectArg(); void invokableObjectRet(); - + void qtbug_20344(); + void qtbug_22679(); + void qtbug_22843_data(); + void qtbug_22843(); void revisionErrors(); void revision(); + void automaticSemicolon(); + void unaryExpression(); + void switchStatement(); + void withStatement(); + void tryStatement(); + private: + static void propertyVarWeakRefCallback(v8::Persistent object, void* parameter); QDeclarativeEngine engine; }; -void tst_qdeclarativeecmascript::initTestCase() { registerTypes(); } +void tst_qdeclarativeecmascript::initTestCase() +{ + QDeclarativeDataTest::initTestCase(); + registerTypes(); +} void tst_qdeclarativeecmascript::assignBasicTypes() { { - QDeclarativeComponent component(&engine, TEST_FILE("assignBasicTypes.qml")); + QDeclarativeComponent component(&engine, testFileUrl("assignBasicTypes.qml")); MyTypeObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3); @@ -235,7 +280,7 @@ void tst_qdeclarativeecmascript::assignBasicTypes() delete object; } { - QDeclarativeComponent component(&engine, TEST_FILE("assignBasicTypes.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("assignBasicTypes.2.qml")); MyTypeObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3); @@ -267,7 +312,7 @@ void tst_qdeclarativeecmascript::assignBasicTypes() void tst_qdeclarativeecmascript::idShortcutInvalidates() { { - QDeclarativeComponent component(&engine, TEST_FILE("idShortcutInvalidates.qml")); + QDeclarativeComponent component(&engine, testFileUrl("idShortcutInvalidates.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QVERIFY(object->objectProperty() != 0); @@ -277,7 +322,7 @@ void tst_qdeclarativeecmascript::idShortcutInvalidates() } { - QDeclarativeComponent component(&engine, TEST_FILE("idShortcutInvalidates.1.qml")); + QDeclarativeComponent component(&engine, testFileUrl("idShortcutInvalidates.1.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QVERIFY(object->objectProperty() != 0); @@ -290,14 +335,14 @@ void tst_qdeclarativeecmascript::idShortcutInvalidates() void tst_qdeclarativeecmascript::boolPropertiesEvaluateAsBool() { { - QDeclarativeComponent component(&engine, TEST_FILE("boolPropertiesEvaluateAsBool.1.qml")); + QDeclarativeComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.1.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->stringProperty(), QLatin1String("pass")); delete object; } { - QDeclarativeComponent component(&engine, TEST_FILE("boolPropertiesEvaluateAsBool.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.2.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->stringProperty(), QLatin1String("pass")); @@ -308,7 +353,7 @@ void tst_qdeclarativeecmascript::boolPropertiesEvaluateAsBool() void tst_qdeclarativeecmascript::signalAssignment() { { - QDeclarativeComponent component(&engine, TEST_FILE("signalAssignment.1.qml")); + QDeclarativeComponent component(&engine, testFileUrl("signalAssignment.1.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->string(), QString()); @@ -318,7 +363,7 @@ void tst_qdeclarativeecmascript::signalAssignment() } { - QDeclarativeComponent component(&engine, TEST_FILE("signalAssignment.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("signalAssignment.2.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->string(), QString()); @@ -331,7 +376,7 @@ void tst_qdeclarativeecmascript::signalAssignment() void tst_qdeclarativeecmascript::methods() { { - QDeclarativeComponent component(&engine, TEST_FILE("methods.1.qml")); + QDeclarativeComponent component(&engine, testFileUrl("methods.1.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->methodCalled(), false); @@ -343,7 +388,7 @@ void tst_qdeclarativeecmascript::methods() } { - QDeclarativeComponent component(&engine, TEST_FILE("methods.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("methods.2.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->methodCalled(), false); @@ -355,7 +400,7 @@ void tst_qdeclarativeecmascript::methods() } { - QDeclarativeComponent component(&engine, TEST_FILE("methods.3.qml")); + QDeclarativeComponent component(&engine, testFileUrl("methods.3.qml")); QObject *object = component.create(); QVERIFY(object != 0); QCOMPARE(object->property("test").toInt(), 19); @@ -363,7 +408,7 @@ void tst_qdeclarativeecmascript::methods() } { - QDeclarativeComponent component(&engine, TEST_FILE("methods.4.qml")); + QDeclarativeComponent component(&engine, testFileUrl("methods.4.qml")); QObject *object = component.create(); QVERIFY(object != 0); QCOMPARE(object->property("test").toInt(), 19); @@ -373,7 +418,7 @@ void tst_qdeclarativeecmascript::methods() } { - QDeclarativeComponent component(&engine, TEST_FILE("methods.5.qml")); + QDeclarativeComponent component(&engine, testFileUrl("methods.5.qml")); QObject *object = component.create(); QVERIFY(object != 0); QCOMPARE(object->property("test").toInt(), 9); @@ -383,8 +428,8 @@ void tst_qdeclarativeecmascript::methods() void tst_qdeclarativeecmascript::bindingLoop() { - QDeclarativeComponent component(&engine, TEST_FILE("bindingLoop.qml")); - QString warning = component.url().toString() + ":9:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\""; + QDeclarativeComponent component(&engine, testFileUrl("bindingLoop.qml")); + QString warning = component.url().toString() + ":5:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\""; QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); QObject *object = component.create(); QVERIFY(object != 0); @@ -592,7 +637,7 @@ void tst_qdeclarativeecmascript::objectPropertiesTriggerReeval() void tst_qdeclarativeecmascript::deferredProperties() { - QDeclarativeComponent component(&engine, TEST_FILE("deferredProperties.qml")); + QDeclarativeComponent component(&engine, testFileUrl("deferredProperties.qml")); MyDeferredObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -615,7 +660,7 @@ void tst_qdeclarativeecmascript::deferredProperties() // Check errors on deferred properties are correctly emitted void tst_qdeclarativeecmascript::deferredPropertiesErrors() { - QDeclarativeComponent component(&engine, TEST_FILE("deferredPropertiesErrors.qml")); + QDeclarativeComponent component(&engine, testFileUrl("deferredPropertiesErrors.qml")); MyDeferredObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -623,7 +668,7 @@ void tst_qdeclarativeecmascript::deferredPropertiesErrors() QVERIFY(object->objectProperty() == 0); QVERIFY(object->objectProperty2() == 0); - QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject* objectProperty"; + QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject*"; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); qmlExecuteDeferred(object); @@ -633,7 +678,7 @@ void tst_qdeclarativeecmascript::deferredPropertiesErrors() void tst_qdeclarativeecmascript::extensionObjects() { - QDeclarativeComponent component(&engine, TEST_FILE("extensionObjects.qml")); + QDeclarativeComponent component(&engine, testFileUrl("extensionObjects.qml")); MyExtendedObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -658,7 +703,7 @@ void tst_qdeclarativeecmascript::extensionObjects() void tst_qdeclarativeecmascript::overrideExtensionProperties() { - QDeclarativeComponent component(&engine, TEST_FILE("extensionObjectsPropertyOverride.qml")); + QDeclarativeComponent component(&engine, testFileUrl("extensionObjectsPropertyOverride.qml")); OverrideDefaultPropertyObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -671,7 +716,7 @@ void tst_qdeclarativeecmascript::overrideExtensionProperties() void tst_qdeclarativeecmascript::attachedProperties() { { - QDeclarativeComponent component(&engine, TEST_FILE("attachedProperty.qml")); + QDeclarativeComponent component(&engine, testFileUrl("attachedProperty.qml")); QObject *object = component.create(); QVERIFY(object != 0); QCOMPARE(object->property("a").toInt(), 19); @@ -682,7 +727,7 @@ void tst_qdeclarativeecmascript::attachedProperties() } { - QDeclarativeComponent component(&engine, TEST_FILE("attachedProperty.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("attachedProperty.2.qml")); QObject *object = component.create(); QVERIFY(object != 0); QCOMPARE(object->property("a").toInt(), 26); @@ -694,7 +739,7 @@ void tst_qdeclarativeecmascript::attachedProperties() } { - QDeclarativeComponent component(&engine, TEST_FILE("writeAttachedProperty.qml")); + QDeclarativeComponent component(&engine, testFileUrl("writeAttachedProperty.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -713,7 +758,7 @@ void tst_qdeclarativeecmascript::enums() { // Existent enums { - QDeclarativeComponent component(&engine, TEST_FILE("enums.1.qml")); + QDeclarativeComponent component(&engine, testFileUrl("enums.1.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -732,10 +777,10 @@ void tst_qdeclarativeecmascript::enums() } // Non-existent enums { - QDeclarativeComponent component(&engine, TEST_FILE("enums.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("enums.2.qml")); - QString warning1 = component.url().toString() + ":5: Unable to assign [undefined] to int a"; - QString warning2 = component.url().toString() + ":6: Unable to assign [undefined] to int b"; + QString warning1 = component.url().toString() + ":5: Unable to assign [undefined] to int"; + QString warning2 = component.url().toString() + ":6: Unable to assign [undefined] to int"; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); @@ -750,7 +795,7 @@ void tst_qdeclarativeecmascript::enums() void tst_qdeclarativeecmascript::valueTypeFunctions() { - QDeclarativeComponent component(&engine, TEST_FILE("valueTypeFunctions.qml")); + QDeclarativeComponent component(&engine, testFileUrl("valueTypeFunctions.qml")); MyTypeObject *obj = qobject_cast(component.create()); QVERIFY(obj != 0); QCOMPARE(obj->rectProperty(), QRect(0,0,100,100)); @@ -767,7 +812,7 @@ void tst_qdeclarativeecmascript::constantsOverrideBindings() { // From ECMAScript { - QDeclarativeComponent component(&engine, TEST_FILE("constantsOverrideBindings.1.qml")); + QDeclarativeComponent component(&engine, testFileUrl("constantsOverrideBindings.1.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -786,7 +831,7 @@ void tst_qdeclarativeecmascript::constantsOverrideBindings() // During construction { - QDeclarativeComponent component(&engine, TEST_FILE("constantsOverrideBindings.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("constantsOverrideBindings.2.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -802,7 +847,7 @@ void tst_qdeclarativeecmascript::constantsOverrideBindings() #if 0 // From C++ { - QDeclarativeComponent component(&engine, TEST_FILE("constantsOverrideBindings.3.qml")); + QDeclarativeComponent component(&engine, testFileUrl("constantsOverrideBindings.3.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -822,7 +867,7 @@ void tst_qdeclarativeecmascript::constantsOverrideBindings() // Using an alias { - QDeclarativeComponent component(&engine, TEST_FILE("constantsOverrideBindings.4.qml")); + QDeclarativeComponent component(&engine, testFileUrl("constantsOverrideBindings.4.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -843,7 +888,7 @@ the original binding to be disabled. void tst_qdeclarativeecmascript::outerBindingOverridesInnerBinding() { QDeclarativeComponent component(&engine, - TEST_FILE("outerBindingOverridesInnerBinding.qml")); + testFileUrl("outerBindingOverridesInnerBinding.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -871,9 +916,9 @@ Tests for a regression where this used to crash. */ void tst_qdeclarativeecmascript::nonExistentAttachedObject() { - QDeclarativeComponent component(&engine, TEST_FILE("nonExistentAttachedObject.qml")); + QDeclarativeComponent component(&engine, testFileUrl("nonExistentAttachedObject.qml")); - QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString stringProperty"; + QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString"; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); QObject *object = component.create(); @@ -885,7 +930,7 @@ void tst_qdeclarativeecmascript::nonExistentAttachedObject() void tst_qdeclarativeecmascript::scope() { { - QDeclarativeComponent component(&engine, TEST_FILE("scope.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scope.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -904,7 +949,7 @@ void tst_qdeclarativeecmascript::scope() } { - QDeclarativeComponent component(&engine, TEST_FILE("scope.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scope.2.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -919,7 +964,7 @@ void tst_qdeclarativeecmascript::scope() } { - QDeclarativeComponent component(&engine, TEST_FILE("scope.3.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scope.3.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -932,7 +977,7 @@ void tst_qdeclarativeecmascript::scope() // Signal argument scope { - QDeclarativeComponent component(&engine, TEST_FILE("scope.4.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scope.4.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -948,7 +993,7 @@ void tst_qdeclarativeecmascript::scope() } { - QDeclarativeComponent component(&engine, TEST_FILE("scope.5.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scope.5.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -959,7 +1004,7 @@ void tst_qdeclarativeecmascript::scope() } { - QDeclarativeComponent component(&engine, TEST_FILE("scope.6.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scope.6.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -973,7 +1018,7 @@ void tst_qdeclarativeecmascript::scope() // importing context void tst_qdeclarativeecmascript::importScope() { - QDeclarativeComponent component(&engine, TEST_FILE("importScope.qml")); + QDeclarativeComponent component(&engine, testFileUrl("importScope.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -988,7 +1033,7 @@ is essentially a test of QDeclarativeMetaType::copy() */ void tst_qdeclarativeecmascript::signalParameterTypes() { - QDeclarativeComponent component(&engine, TEST_FILE("signalParameterTypes.qml")); + QDeclarativeComponent component(&engine, testFileUrl("signalParameterTypes.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -1009,7 +1054,7 @@ Test that two JS objects for the same QObject compare as equal. */ void tst_qdeclarativeecmascript::objectsCompareAsEqual() { - QDeclarativeComponent component(&engine, TEST_FILE("objectsCompareAsEqual.qml")); + QDeclarativeComponent component(&engine, testFileUrl("objectsCompareAsEqual.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -1029,7 +1074,7 @@ Tests for a regression where the binding would not reevaluate. */ void tst_qdeclarativeecmascript::aliasPropertyAndBinding() { - QDeclarativeComponent component(&engine, TEST_FILE("aliasPropertyAndBinding.qml")); + QDeclarativeComponent component(&engine, testFileUrl("aliasPropertyAndBinding.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -1053,7 +1098,7 @@ void tst_qdeclarativeecmascript::aliasPropertyReset() QObject *object = 0; // test that a manual write (of undefined) to a resettable aliased property succeeds - QDeclarativeComponent c1(&engine, TEST_FILE("aliasreset/aliasPropertyReset.1.qml")); + QDeclarativeComponent c1(&engine, testFileUrl("aliasreset/aliasPropertyReset.1.qml")); object = c1.create(); QVERIFY(object != 0); QVERIFY(object->property("sourceComponentAlias").value() != 0); @@ -1064,7 +1109,7 @@ void tst_qdeclarativeecmascript::aliasPropertyReset() delete object; // test that a manual write (of undefined) to a resettable alias property succeeds - QDeclarativeComponent c2(&engine, TEST_FILE("aliasreset/aliasPropertyReset.2.qml")); + QDeclarativeComponent c2(&engine, testFileUrl("aliasreset/aliasPropertyReset.2.qml")); object = c2.create(); QVERIFY(object != 0); QVERIFY(object->property("sourceComponentAlias").value() != 0); @@ -1075,7 +1120,7 @@ void tst_qdeclarativeecmascript::aliasPropertyReset() delete object; // test that an alias to a bound property works correctly - QDeclarativeComponent c3(&engine, TEST_FILE("aliasreset/aliasPropertyReset.3.qml")); + QDeclarativeComponent c3(&engine, testFileUrl("aliasreset/aliasPropertyReset.3.qml")); object = c3.create(); QVERIFY(object != 0); QVERIFY(object->property("sourceComponentAlias").value() != 0); @@ -1089,7 +1134,7 @@ void tst_qdeclarativeecmascript::aliasPropertyReset() // test that a manual write (of undefined) to a resettable alias property // whose aliased property's object has been deleted, does not crash. - QDeclarativeComponent c4(&engine, TEST_FILE("aliasreset/aliasPropertyReset.4.qml")); + QDeclarativeComponent c4(&engine, testFileUrl("aliasreset/aliasPropertyReset.4.qml")); object = c4.create(); QVERIFY(object != 0); QVERIFY(object->property("sourceComponentAlias").value() != 0); @@ -1104,14 +1149,14 @@ void tst_qdeclarativeecmascript::aliasPropertyReset() delete object; // test that binding an alias property to an undefined value works correctly - QDeclarativeComponent c5(&engine, TEST_FILE("aliasreset/aliasPropertyReset.5.qml")); + QDeclarativeComponent c5(&engine, testFileUrl("aliasreset/aliasPropertyReset.5.qml")); object = c5.create(); QVERIFY(object != 0); QVERIFY(object->property("sourceComponentAlias").value() == 0); // bound to undefined value. delete object; // test that a manual write (of undefined) to a non-resettable property fails properly - QUrl url = TEST_FILE("aliasreset/aliasPropertyReset.error.1.qml"); + QUrl url = testFileUrl("aliasreset/aliasPropertyReset.error.1.qml"); QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int"); QDeclarativeComponent e1(&engine, url); object = e1.create(); @@ -1144,7 +1189,7 @@ void tst_qdeclarativeecmascript::dynamicCreation() QFETCH(QString, method); QFETCH(QString, createdName); - QDeclarativeComponent component(&engine, TEST_FILE("dynamicCreation.qml")); + QDeclarativeComponent component(&engine, testFileUrl("dynamicCreation.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -1162,7 +1207,7 @@ void tst_qdeclarativeecmascript::dynamicCreation() void tst_qdeclarativeecmascript::dynamicDestruction() { { - QDeclarativeComponent component(&engine, TEST_FILE("dynamicDeletion.qml")); + QDeclarativeComponent component(&engine, testFileUrl("dynamicDeletion.qml")); QDeclarativeGuard object = qobject_cast(component.create()); QVERIFY(object != 0); QDeclarativeGuard createdQmlObject = 0; @@ -1174,12 +1219,15 @@ void tst_qdeclarativeecmascript::dynamicDestruction() QMetaObject::invokeMethod(object, "killOther"); QVERIFY(createdQmlObject); - QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); + + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QVERIFY(createdQmlObject); for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up if (createdQmlObject) { QTest::qWait(100); - QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); } } QVERIFY(!createdQmlObject); @@ -1187,13 +1235,13 @@ void tst_qdeclarativeecmascript::dynamicDestruction() QDeclarativeEngine::setObjectOwnership(object, QDeclarativeEngine::JavaScriptOwnership); QMetaObject::invokeMethod(object, "killMe"); QVERIFY(object); - QTest::qWait(0); - QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QVERIFY(!object); } { - QDeclarativeComponent component(&engine, TEST_FILE("dynamicDeletion.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("dynamicDeletion.2.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -1205,7 +1253,8 @@ void tst_qdeclarativeecmascript::dynamicDestruction() QMetaObject::invokeMethod(o, "destroy"); - QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QVERIFY(qvariant_cast(o->property("objectProperty")) == 0); @@ -1218,7 +1267,7 @@ void tst_qdeclarativeecmascript::dynamicDestruction() */ void tst_qdeclarativeecmascript::objectToString() { - QDeclarativeComponent component(&engine, TEST_FILE("declarativeToString.qml")); + QDeclarativeComponent component(&engine, testFileUrl("declarativeToString.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QMetaObject::invokeMethod(object, "testToString"); @@ -1233,7 +1282,7 @@ void tst_qdeclarativeecmascript::objectToString() */ void tst_qdeclarativeecmascript::objectHasOwnProperty() { - QUrl url = TEST_FILE("declarativeHasOwnProperty.qml"); + QUrl url = testFileUrl("declarativeHasOwnProperty.qml"); QString warning1 = url.toString() + ":59: TypeError: Cannot call method 'hasOwnProperty' of undefined"; QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined"; QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined"; @@ -1285,7 +1334,7 @@ This test is best run under valgrind to ensure no invalid memory access occur. void tst_qdeclarativeecmascript::selfDeletingBinding() { { - QDeclarativeComponent component(&engine, TEST_FILE("selfDeletingBinding.qml")); + QDeclarativeComponent component(&engine, testFileUrl("selfDeletingBinding.qml")); QObject *object = component.create(); QVERIFY(object != 0); object->setProperty("triggerDelete", true); @@ -1293,7 +1342,7 @@ void tst_qdeclarativeecmascript::selfDeletingBinding() } { - QDeclarativeComponent component(&engine, TEST_FILE("selfDeletingBinding.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("selfDeletingBinding.2.qml")); QObject *object = component.create(); QVERIFY(object != 0); object->setProperty("triggerDelete", true); @@ -1310,28 +1359,43 @@ and no synthesiszed properties). */ void tst_qdeclarativeecmascript::extendedObjectPropertyLookup() { - QDeclarativeComponent component(&engine, TEST_FILE("extendedObjectPropertyLookup.qml")); + QDeclarativeComponent component(&engine, testFileUrl("extendedObjectPropertyLookup.qml")); QObject *object = component.create(); QVERIFY(object != 0); delete object; } /* +Test that extended object properties can be accessed correctly. +*/ +void tst_qdeclarativeecmascript::extendedObjectPropertyLookup2() +{ + QDeclarativeComponent component(&engine, testFileUrl("extendedObjectPropertyLookup2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QVariant returnValue; + QVERIFY(QMetaObject::invokeMethod(object, "getValue", Q_RETURN_ARG(QVariant, returnValue))); + QCOMPARE(returnValue.toInt(), 42); + + delete object; +} +/* Test file/lineNumbers for binding/Script errors. */ void tst_qdeclarativeecmascript::scriptErrors() { - QDeclarativeComponent component(&engine, TEST_FILE("scriptErrors.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scriptErrors.qml")); QString url = component.url().toString(); QString warning1 = url.left(url.length() - 3) + "js:2: Error: Invalid write to global property \"a\""; QString warning2 = url + ":5: ReferenceError: Can't find variable: a"; QString warning3 = url.left(url.length() - 3) + "js:4: Error: Invalid write to global property \"a\""; - QString warning4 = url + ":10: ReferenceError: Can't find variable: a"; - QString warning5 = url + ":8: ReferenceError: Can't find variable: a"; - QString warning6 = url + ":7: Unable to assign [undefined] to int x"; - QString warning7 = url + ":12: Error: Cannot assign to read-only property \"trueProperty\""; - QString warning8 = url + ":13: Error: Cannot assign to non-existent property \"fakeProperty\""; + QString warning4 = url + ":13: ReferenceError: Can't find variable: a"; + QString warning5 = url + ":11: ReferenceError: Can't find variable: a"; + QString warning6 = url + ":10: Unable to assign [undefined] to int"; + QString warning7 = url + ":15: Error: Cannot assign to read-only property \"trueProperty\""; + QString warning8 = url + ":16: Error: Cannot assign to non-existent property \"fakeProperty\""; QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData()); QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData()); @@ -1358,7 +1422,7 @@ Test file/lineNumbers for inline functions. */ void tst_qdeclarativeecmascript::functionErrors() { - QDeclarativeComponent component(&engine, TEST_FILE("functionErrors.qml")); + QDeclarativeComponent component(&engine, testFileUrl("functionErrors.qml")); QString url = component.url().toString(); QString warning = url + ":5: Error: Invalid write to global property \"a\""; @@ -1370,15 +1434,15 @@ void tst_qdeclarativeecmascript::functionErrors() delete object; // test that if an exception occurs while invoking js function from cpp, it is reported as expected. - QDeclarativeComponent componentTwo(&engine, TEST_FILE("scarceresources/scarceResourceFunctionFail.qml")); + QDeclarativeComponent componentTwo(&engine, testFileUrl("scarceResourceFunctionFail.var.qml")); url = componentTwo.url().toString(); object = componentTwo.create(); QVERIFY(object != 0); QString srpname = object->property("srp_name").toString(); - warning = url + QLatin1String(":17: TypeError: Property 'scarceResource' of object ") + srpname + - QLatin1String(" is not a function"); + warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srpname + + QLatin1String(" is not a function"); QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed. QMetaObject::invokeMethod(object, "retrieveScarceResource"); delete object; @@ -1389,7 +1453,7 @@ Test various errors that can occur when assigning a property from script */ void tst_qdeclarativeecmascript::propertyAssignmentErrors() { - QDeclarativeComponent component(&engine, TEST_FILE("propertyAssignmentErrors.qml")); + QDeclarativeComponent component(&engine, testFileUrl("propertyAssignmentErrors.qml")); QString url = component.url().toString(); @@ -1408,7 +1472,7 @@ a signal script. */ void tst_qdeclarativeecmascript::signalTriggeredBindings() { - QDeclarativeComponent component(&engine, TEST_FILE("signalTriggeredBindings.qml")); + QDeclarativeComponent component(&engine, testFileUrl("signalTriggeredBindings.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -1436,7 +1500,7 @@ Test that list properties can be iterated from ECMAScript */ void tst_qdeclarativeecmascript::listProperties() { - QDeclarativeComponent component(&engine, TEST_FILE("listProperties.qml")); + QDeclarativeComponent component(&engine, testFileUrl("listProperties.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -1450,7 +1514,7 @@ void tst_qdeclarativeecmascript::listProperties() void tst_qdeclarativeecmascript::exceptionClearsOnReeval() { - QDeclarativeComponent component(&engine, TEST_FILE("exceptionClearsOnReeval.qml")); + QDeclarativeComponent component(&engine, testFileUrl("exceptionClearsOnReeval.qml")); QString url = component.url().toString(); QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null"; @@ -1473,7 +1537,7 @@ void tst_qdeclarativeecmascript::exceptionClearsOnReeval() void tst_qdeclarativeecmascript::exceptionSlotProducesWarning() { - QDeclarativeComponent component(&engine, TEST_FILE("exceptionProducesWarning.qml")); + QDeclarativeComponent component(&engine, testFileUrl("exceptionProducesWarning.qml")); QString url = component.url().toString(); QString warning = component.url().toString() + ":6: Error: JS exception"; @@ -1486,7 +1550,7 @@ void tst_qdeclarativeecmascript::exceptionSlotProducesWarning() void tst_qdeclarativeecmascript::exceptionBindingProducesWarning() { - QDeclarativeComponent component(&engine, TEST_FILE("exceptionProducesWarning2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("exceptionProducesWarning2.qml")); QString url = component.url().toString(); QString warning = component.url().toString() + ":5: Error: JS exception"; @@ -1507,7 +1571,7 @@ static void transientErrorsMsgHandler(QtMsgType, const char *) void tst_qdeclarativeecmascript::transientErrors() { { - QDeclarativeComponent component(&engine, TEST_FILE("transientErrors.qml")); + QDeclarativeComponent component(&engine, testFileUrl("transientErrors.qml")); transientErrorsMsgCount = 0; QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); @@ -1524,7 +1588,7 @@ void tst_qdeclarativeecmascript::transientErrors() // One binding erroring multiple times, but then resolving { - QDeclarativeComponent component(&engine, TEST_FILE("transientErrors.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("transientErrors.2.qml")); transientErrorsMsgCount = 0; QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); @@ -1543,7 +1607,7 @@ void tst_qdeclarativeecmascript::transientErrors() // Check that errors during shutdown are minimized void tst_qdeclarativeecmascript::shutdownErrors() { - QDeclarativeComponent component(&engine, TEST_FILE("shutdownErrors.qml")); + QDeclarativeComponent component(&engine, testFileUrl("shutdownErrors.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -1558,7 +1622,8 @@ void tst_qdeclarativeecmascript::shutdownErrors() void tst_qdeclarativeecmascript::compositePropertyType() { - QDeclarativeComponent component(&engine, TEST_FILE("compositePropertyType.qml")); + QDeclarativeComponent component(&engine, testFileUrl("compositePropertyType.qml")); + QTest::ignoreMessage(QtDebugMsg, "hello world"); QObject *object = qobject_cast(component.create()); delete object; @@ -1567,7 +1632,7 @@ void tst_qdeclarativeecmascript::compositePropertyType() // QTBUG-5759 void tst_qdeclarativeecmascript::jsObject() { - QDeclarativeComponent component(&engine, TEST_FILE("jsObject.qml")); + QDeclarativeComponent component(&engine, testFileUrl("jsObject.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -1579,7 +1644,7 @@ void tst_qdeclarativeecmascript::jsObject() void tst_qdeclarativeecmascript::undefinedResetsProperty() { { - QDeclarativeComponent component(&engine, TEST_FILE("undefinedResetsProperty.qml")); + QDeclarativeComponent component(&engine, testFileUrl("undefinedResetsProperty.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -1596,7 +1661,7 @@ void tst_qdeclarativeecmascript::undefinedResetsProperty() delete object; } { - QDeclarativeComponent component(&engine, TEST_FILE("undefinedResetsProperty.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("undefinedResetsProperty.2.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -1610,10 +1675,34 @@ void tst_qdeclarativeecmascript::undefinedResetsProperty() } } +// Aliases to variant properties should work +void tst_qdeclarativeecmascript::qtbug_22464() +{ + QDeclarativeComponent component(&engine, testFileUrl("qtbug_22464.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +void tst_qdeclarativeecmascript::qtbug_21580() +{ + QDeclarativeComponent component(&engine, testFileUrl("qtbug_21580.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + // QTBUG-6781 void tst_qdeclarativeecmascript::bug1() { - QDeclarativeComponent component(&engine, TEST_FILE("bug.1.qml")); + QDeclarativeComponent component(&engine, testFileUrl("bug.1.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -1644,7 +1733,7 @@ void tst_qdeclarativeecmascript::bug2() // Don't crash in createObject when the component has errors. void tst_qdeclarativeecmascript::dynamicCreationCrash() { - QDeclarativeComponent component(&engine, TEST_FILE("dynamicCreation.qml")); + QDeclarativeComponent component(&engine, testFileUrl("dynamicCreation.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -1656,14 +1745,62 @@ void tst_qdeclarativeecmascript::dynamicCreationCrash() delete object; } -//QTBUG-9367 +// ownership transferred to JS, ensure that GC runs the dtor +void tst_qdeclarativeecmascript::dynamicCreationOwnership() +{ + int dtorCount = 0; + int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too. + + // allow the engine to go out of scope too. + { + QDeclarativeEngine dcoEngine; + QDeclarativeComponent component(&dcoEngine, testFileUrl("dynamicCreationOwnership.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + MyDynamicCreationDestructionObject *mdcdo = object->findChild("mdcdo"); + QVERIFY(mdcdo != 0); + mdcdo->setDtorCount(&dtorCount); + + for (int i = 1; i < 105; ++i, ++expectedDtorCount) { + QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject"); + if (i % 90 == 0) { + // we do this once manually, but it should be done automatically + // when the engine goes out of scope (since it should gc in dtor) + QMetaObject::invokeMethod(object, "performGc"); + } + if (i % 10 == 0) { + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + } + } + + delete object; + } + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + QCOMPARE(dtorCount, expectedDtorCount); +} + void tst_qdeclarativeecmascript::regExpBug() { - QDeclarativeComponent component(&engine, TEST_FILE("regExp.qml")); - MyQmlObject *object = qobject_cast(component.create()); - QVERIFY(object != 0); - QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]")); - delete object; + //QTBUG-9367 + { + QDeclarativeComponent component(&engine, testFileUrl("regExp.qml")); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]")); + delete object; + } + + //QTBUG-23068 + { + QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString()); + QDeclarativeComponent component(&engine, testFileUrl("regExp.2.qml")); + QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready"); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(!object); + QCOMPARE(component.errorString(), err); + } } static inline bool evaluate_error(QV8Engine *engine, v8::Handle o, const char *source) @@ -2191,7 +2328,7 @@ void tst_qdeclarativeecmascript::callQtInvokables() // QTBUG-13047 (check that you can pass registered object types as args) void tst_qdeclarativeecmascript::invokableObjectArg() { - QDeclarativeComponent component(&engine, TEST_FILE("invokableObjectArg.qml")); + QDeclarativeComponent component(&engine, testFileUrl("invokableObjectArg.qml")); QObject *o = component.create(); QVERIFY(o); @@ -2205,7 +2342,7 @@ void tst_qdeclarativeecmascript::invokableObjectArg() // QTBUG-13047 (check that you can return registered object types from methods) void tst_qdeclarativeecmascript::invokableObjectRet() { - QDeclarativeComponent component(&engine, TEST_FILE("invokableObjectRet.qml")); + QDeclarativeComponent component(&engine, testFileUrl("invokableObjectRet.qml")); QObject *o = component.create(); QVERIFY(o); @@ -2216,7 +2353,7 @@ void tst_qdeclarativeecmascript::invokableObjectRet() // QTBUG-5675 void tst_qdeclarativeecmascript::listToVariant() { - QDeclarativeComponent component(&engine, TEST_FILE("listToVariant.qml")); + QDeclarativeComponent component(&engine, testFileUrl("listToVariant.qml")); MyQmlContainer container; @@ -2233,6 +2370,21 @@ void tst_qdeclarativeecmascript::listToVariant() delete object; } +// QTBUG-16316 +Q_DECLARE_METATYPE(QDeclarativeListProperty) +void tst_qdeclarativeecmascript::listAssignment() +{ + QDeclarativeComponent component(&engine, testFileUrl("listAssignment.qml")); + QObject *obj = component.create(); + QCOMPARE(obj->property("list1length").toInt(), 2); + QDeclarativeListProperty list1 = obj->property("list1").value >(); + QDeclarativeListProperty list2 = obj->property("list2").value >(); + QCOMPARE(list1.count(&list1), list2.count(&list2)); + QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0)); + QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1)); + delete obj; +} + // QTBUG-7957 void tst_qdeclarativeecmascript::multiEngineObject() { @@ -2241,11 +2393,11 @@ void tst_qdeclarativeecmascript::multiEngineObject() QDeclarativeEngine e1; e1.rootContext()->setContextProperty("thing", &obj); - QDeclarativeComponent c1(&e1, TEST_FILE("multiEngineObject.qml")); + QDeclarativeComponent c1(&e1, testFileUrl("multiEngineObject.qml")); QDeclarativeEngine e2; e2.rootContext()->setContextProperty("thing", &obj); - QDeclarativeComponent c2(&e2, TEST_FILE("multiEngineObject.qml")); + QDeclarativeComponent c2(&e2, testFileUrl("multiEngineObject.qml")); QObject *o1 = c1.create(); QObject *o2 = c2.create(); @@ -2260,7 +2412,7 @@ void tst_qdeclarativeecmascript::multiEngineObject() // Test that references to QObjects are cleanup when the object is destroyed void tst_qdeclarativeecmascript::deletedObject() { - QDeclarativeComponent component(&engine, TEST_FILE("deletedObject.qml")); + QDeclarativeComponent component(&engine, testFileUrl("deletedObject.qml")); QObject *object = component.create(); @@ -2274,7 +2426,7 @@ void tst_qdeclarativeecmascript::deletedObject() void tst_qdeclarativeecmascript::attachedPropertyScope() { - QDeclarativeComponent component(&engine, TEST_FILE("attachedPropertyScope.qml")); + QDeclarativeComponent component(&engine, testFileUrl("attachedPropertyScope.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -2295,7 +2447,7 @@ void tst_qdeclarativeecmascript::attachedPropertyScope() void tst_qdeclarativeecmascript::scriptConnect() { { - QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.1.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scriptConnect.1.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -2308,7 +2460,7 @@ void tst_qdeclarativeecmascript::scriptConnect() } { - QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scriptConnect.2.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -2321,7 +2473,7 @@ void tst_qdeclarativeecmascript::scriptConnect() } { - QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.3.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scriptConnect.3.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -2334,7 +2486,7 @@ void tst_qdeclarativeecmascript::scriptConnect() } { - QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.4.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scriptConnect.4.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -2347,7 +2499,7 @@ void tst_qdeclarativeecmascript::scriptConnect() } { - QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.5.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scriptConnect.5.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -2360,7 +2512,7 @@ void tst_qdeclarativeecmascript::scriptConnect() } { - QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.6.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scriptConnect.6.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -2376,7 +2528,7 @@ void tst_qdeclarativeecmascript::scriptConnect() void tst_qdeclarativeecmascript::scriptDisconnect() { { - QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.1.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scriptDisconnect.1.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -2395,7 +2547,7 @@ void tst_qdeclarativeecmascript::scriptDisconnect() } { - QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scriptDisconnect.2.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -2414,7 +2566,7 @@ void tst_qdeclarativeecmascript::scriptDisconnect() } { - QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.3.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scriptDisconnect.3.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -2432,7 +2584,7 @@ void tst_qdeclarativeecmascript::scriptDisconnect() delete object; } { - QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.4.qml")); + QDeclarativeComponent component(&engine, testFileUrl("scriptDisconnect.4.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -2470,7 +2622,7 @@ void tst_qdeclarativeecmascript::ownership() context->setContextObject(&own); { - QDeclarativeComponent component(&engine, TEST_FILE("ownership.qml")); + QDeclarativeComponent component(&engine, testFileUrl("ownership.qml")); QVERIFY(own.object != 0); @@ -2478,7 +2630,8 @@ void tst_qdeclarativeecmascript::ownership() engine.collectGarbage(); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QVERIFY(own.object == 0); @@ -2488,7 +2641,7 @@ void tst_qdeclarativeecmascript::ownership() own.object = new QObject(&own); { - QDeclarativeComponent component(&engine, TEST_FILE("ownership.qml")); + QDeclarativeComponent component(&engine, testFileUrl("ownership.qml")); QVERIFY(own.object != 0); @@ -2496,7 +2649,8 @@ void tst_qdeclarativeecmascript::ownership() engine.collectGarbage(); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QVERIFY(own.object != 0); @@ -2551,7 +2705,8 @@ void tst_qdeclarativeecmascript::cppOwnershipReturnValue() delete object; } - QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QVERIFY(source.value != 0); } @@ -2579,7 +2734,8 @@ void tst_qdeclarativeecmascript::ownershipCustomReturnValue() } engine.collectGarbage(); - QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QVERIFY(source.value == 0); } @@ -2611,7 +2767,7 @@ void tst_qdeclarativeecmascript::qlistqobjectMethods() QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext()); context->setContextObject(&obj); - QDeclarativeComponent component(&engine, TEST_FILE("qlistqobjectMethods.qml")); + QDeclarativeComponent component(&engine, testFileUrl("qlistqobjectMethods.qml")); QObject *object = component.create(context); @@ -2625,7 +2781,7 @@ void tst_qdeclarativeecmascript::qlistqobjectMethods() // QTBUG-9205 void tst_qdeclarativeecmascript::strictlyEquals() { - QDeclarativeComponent component(&engine, TEST_FILE("strictlyEquals.qml")); + QDeclarativeComponent component(&engine, testFileUrl("strictlyEquals.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -2644,7 +2800,7 @@ void tst_qdeclarativeecmascript::strictlyEquals() void tst_qdeclarativeecmascript::compiled() { - QDeclarativeComponent component(&engine, TEST_FILE("compiled.qml")); + QDeclarativeComponent component(&engine, testFileUrl("compiled.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -2684,7 +2840,7 @@ void tst_qdeclarativeecmascript::compiled() // Test that numbers assigned in bindings as strings work consistently void tst_qdeclarativeecmascript::numberAssignment() { - QDeclarativeComponent component(&engine, TEST_FILE("numberAssignment.qml")); + QDeclarativeComponent component(&engine, testFileUrl("numberAssignment.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -2710,7 +2866,7 @@ void tst_qdeclarativeecmascript::numberAssignment() void tst_qdeclarativeecmascript::propertySplicing() { - QDeclarativeComponent component(&engine, TEST_FILE("propertySplicing.qml")); + QDeclarativeComponent component(&engine, testFileUrl("propertySplicing.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -2723,7 +2879,7 @@ void tst_qdeclarativeecmascript::propertySplicing() // QTBUG-16683 void tst_qdeclarativeecmascript::signalWithUnknownTypes() { - QDeclarativeComponent component(&engine, TEST_FILE("signalWithUnknownTypes.qml")); + QDeclarativeComponent component(&engine, testFileUrl("signalWithUnknownTypes.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -2740,6 +2896,103 @@ void tst_qdeclarativeecmascript::signalWithUnknownTypes() delete object; } +void tst_qdeclarativeecmascript::signalWithJSValueInVariant_data() +{ + QTest::addColumn("expression"); + QTest::addColumn("compare"); + + QString compareStrict("(function(a, b) { return a === b; })"); + QTest::newRow("true") << "true" << compareStrict; + QTest::newRow("undefined") << "undefined" << compareStrict; + QTest::newRow("null") << "null" << compareStrict; + QTest::newRow("123") << "123" << compareStrict; + QTest::newRow("'ciao'") << "'ciao'" << compareStrict; + + QString comparePropertiesStrict( + "(function(a, b) {" + " if (typeof b != 'object')" + " return a === b;" + " var props = Object.getOwnPropertyNames(b);" + " for (var i = 0; i < props.length; ++i) {" + " var p = props[i];" + " return arguments.callee(a[p], b[p]);" + " }" + "})"); + QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })" << comparePropertiesStrict; + QTest::newRow("[10,20,30]") << "[10,20,30]" << comparePropertiesStrict; +} + +void tst_qdeclarativeecmascript::signalWithJSValueInVariant() +{ + QFETCH(QString, expression); + QFETCH(QString, compare); + + QDeclarativeComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml")); + QScopedPointer object(qobject_cast(component.create())); + QVERIFY(object != 0); + + QJSValue value = engine.evaluate(expression); + QVERIFY(!engine.hasUncaughtException()); + object->setProperty("expression", expression); + object->setProperty("compare", compare); + object->setProperty("pass", false); + + emit object->signalWithVariant(QVariant::fromValue(value)); + QVERIFY(object->property("pass").toBool()); +} + +void tst_qdeclarativeecmascript::signalWithJSValueInVariant_twoEngines_data() +{ + signalWithJSValueInVariant_data(); +} + +void tst_qdeclarativeecmascript::signalWithJSValueInVariant_twoEngines() +{ + QFETCH(QString, expression); + QFETCH(QString, compare); + + QDeclarativeComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml")); + QScopedPointer object(qobject_cast(component.create())); + QVERIFY(object != 0); + + QJSEngine engine2; + QJSValue value = engine2.evaluate(expression); + QVERIFY(!engine2.hasUncaughtException()); + object->setProperty("expression", expression); + object->setProperty("compare", compare); + object->setProperty("pass", false); + + QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine."); + emit object->signalWithVariant(QVariant::fromValue(value)); + QVERIFY(!object->property("pass").toBool()); +} + +void tst_qdeclarativeecmascript::signalWithQJSValue_data() +{ + signalWithJSValueInVariant_data(); +} + +void tst_qdeclarativeecmascript::signalWithQJSValue() +{ + QFETCH(QString, expression); + QFETCH(QString, compare); + + QDeclarativeComponent component(&engine, testFileUrl("signalWithQJSValue.qml")); + QScopedPointer object(qobject_cast(component.create())); + QVERIFY(object != 0); + + QJSValue value = engine.evaluate(expression); + QVERIFY(!engine.hasUncaughtException()); + object->setProperty("expression", expression); + object->setProperty("compare", compare); + object->setProperty("pass", false); + + emit object->signalWithQJSValue(value); + + QVERIFY(object->property("pass").toBool()); + QVERIFY(object->qjsvalue().strictlyEquals(value)); +} + void tst_qdeclarativeecmascript::moduleApi_data() { QTest::addColumn("testfile"); @@ -2753,7 +3006,7 @@ void tst_qdeclarativeecmascript::moduleApi_data() QTest::addColumn("readBackExpectedValues"); QTest::newRow("qobject, register + read + method") - << TEST_FILE("moduleapi/qobjectModuleApi.qml") + << testFileUrl("moduleapi/qobjectModuleApi.qml") << QString() << QStringList() << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest" @@ -2765,7 +3018,7 @@ void tst_qdeclarativeecmascript::moduleApi_data() << QVariantList(); QTest::newRow("script, register + read") - << TEST_FILE("moduleapi/scriptModuleApi.qml") + << testFileUrl("moduleapi/scriptModuleApi.qml") << QString() << QStringList() << (QStringList() << "scriptTest") @@ -2776,7 +3029,7 @@ void tst_qdeclarativeecmascript::moduleApi_data() << QVariantList(); QTest::newRow("qobject, caching + read") - << TEST_FILE("moduleapi/qobjectModuleApiCaching.qml") + << testFileUrl("moduleapi/qobjectModuleApiCaching.qml") << QString() << QStringList() << (QStringList() << "existingUriTest" << "qobjectParentedTest") @@ -2787,7 +3040,7 @@ void tst_qdeclarativeecmascript::moduleApi_data() << QVariantList(); QTest::newRow("script, caching + read") - << TEST_FILE("moduleapi/scriptModuleApiCaching.qml") + << testFileUrl("moduleapi/scriptModuleApiCaching.qml") << QString() << QStringList() << (QStringList() << "scriptTest") @@ -2798,9 +3051,9 @@ void tst_qdeclarativeecmascript::moduleApi_data() << QVariantList(); QTest::newRow("qobject, writing + readonly constraints") - << TEST_FILE("moduleapi/qobjectModuleApiWriting.qml") + << testFileUrl("moduleapi/qobjectModuleApiWriting.qml") << QString() - << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("moduleapi/qobjectModuleApiWriting.qml").toLocalFile() + QLatin1String(":14: Error: Cannot assign to read-only property \"qobjectTestProperty\""))) + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("moduleapi/qobjectModuleApiWriting.qml").toLocalFile() + QLatin1String(":14: Error: Cannot assign to read-only property \"qobjectTestProperty\""))) << (QStringList() << "readOnlyProperty" << "writableProperty") << (QVariantList() << 20 << 50) << (QStringList() << "firstProperty" << "writableProperty") @@ -2809,9 +3062,9 @@ void tst_qdeclarativeecmascript::moduleApi_data() << (QVariantList() << 20 << 30); QTest::newRow("script, writing + readonly constraints") - << TEST_FILE("moduleapi/scriptModuleApiWriting.qml") + << testFileUrl("moduleapi/scriptModuleApiWriting.qml") << QString() - << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("moduleapi/scriptModuleApiWriting.qml").toLocalFile() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\""))) + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("moduleapi/scriptModuleApiWriting.qml").toLocalFile() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\""))) << (QStringList() << "readBack" << "unchanged") << (QVariantList() << 13 << 42) << (QStringList() << "firstProperty" << "secondProperty") @@ -2820,7 +3073,7 @@ void tst_qdeclarativeecmascript::moduleApi_data() << (QVariantList() << 30 << 42); QTest::newRow("qobject module API enum values in JS") - << TEST_FILE("moduleapi/qobjectModuleApiEnums.qml") + << testFileUrl("moduleapi/qobjectModuleApiEnums.qml") << QString() << QStringList() << (QStringList() << "enumValue" << "enumMethod") @@ -2831,7 +3084,7 @@ void tst_qdeclarativeecmascript::moduleApi_data() << QVariantList(); QTest::newRow("qobject, invalid major version fail") - << TEST_FILE("moduleapi/moduleApiMajorVersionFail.qml") + << testFileUrl("moduleapi/moduleApiMajorVersionFail.qml") << QString("QDeclarativeComponent: Component is not ready") << QStringList() << QStringList() @@ -2842,7 +3095,7 @@ void tst_qdeclarativeecmascript::moduleApi_data() << QVariantList(); QTest::newRow("qobject, invalid minor version fail") - << TEST_FILE("moduleapi/moduleApiMinorVersionFail.qml") + << testFileUrl("moduleapi/moduleApiMinorVersionFail.qml") << QString("QDeclarativeComponent: Component is not ready") << QStringList() << QStringList() @@ -2889,184 +3142,219 @@ void tst_qdeclarativeecmascript::moduleApi() } } -void tst_qdeclarativeecmascript::importScripts() +void tst_qdeclarativeecmascript::importScripts_data() { - QObject *object = 0; + QTest::addColumn("testfile"); + QTest::addColumn("errorMessage"); + QTest::addColumn("warningMessages"); + QTest::addColumn("propertyNames"); + QTest::addColumn("propertyValues"); - // first, ensure that the required behaviour works. - QDeclarativeComponent component(&engine, TEST_FILE("jsimport/testImport.qml")); - object = component.create(); - QVERIFY(object != 0); - QCOMPARE(object->property("importedScriptStringValue"), QVariant(QString(QLatin1String("Hello, World!")))); - QCOMPARE(object->property("importedScriptFunctionValue"), QVariant(20)); - QCOMPARE(object->property("importedModuleAttachedPropertyValue"), QVariant(19)); - QCOMPARE(object->property("importedModuleEnumValue"), QVariant(2)); - delete object; + QTest::newRow("basic functionality") + << testFileUrl("jsimport/testImport.qml") + << QString() + << QStringList() + << (QStringList() << QLatin1String("importedScriptStringValue") + << QLatin1String("importedScriptFunctionValue") + << QLatin1String("importedModuleAttachedPropertyValue") + << QLatin1String("importedModuleEnumValue")) + << (QVariantList() << QVariant(QLatin1String("Hello, World!")) + << QVariant(20) + << QVariant(19) + << QVariant(2)); + + QTest::newRow("import scoping") + << testFileUrl("jsimport/testImportScoping.qml") + << QString() + << QStringList() + << (QStringList() << QLatin1String("componentError")) + << (QVariantList() << QVariant(5)); - QDeclarativeComponent componentTwo(&engine, TEST_FILE("jsimport/testImportScoping.qml")); - object = componentTwo.create(); - QVERIFY(object != 0); - QCOMPARE(object->property("componentError"), QVariant(5)); - delete object; + QTest::newRow("parent scope shouldn't be inherited by import with imports") + << testFileUrl("jsimportfail/failOne.qml") + << QString() + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/failOne.qml").toLocalFile() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined"))) + << (QStringList() << QLatin1String("importScriptFunctionValue")) + << (QVariantList() << QVariant(QString())); - // then, ensure that unintended behaviour does not work. - QDeclarativeComponent failOneComponent(&engine, TEST_FILE("jsimportfail/failOne.qml")); - QString expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/failOne.qml").toLocalFile() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined"); - QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData()); - object = failOneComponent.create(); - QVERIFY(object != 0); - QVERIFY(object->property("importScriptFunctionValue").toString().isEmpty()); - delete object; - QDeclarativeComponent failTwoComponent(&engine, TEST_FILE("jsimportfail/failTwo.qml")); - expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/failTwo.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: ImportOneJs"); - QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData()); - object = failTwoComponent.create(); - QVERIFY(object != 0); - QVERIFY(object->property("importScriptFunctionValue").toString().isEmpty()); - delete object; - QDeclarativeComponent failThreeComponent(&engine, TEST_FILE("jsimportfail/failThree.qml")); - expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/failThree.qml").toLocalFile() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined"); - QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData()); - object = failThreeComponent.create(); - QVERIFY(object != 0); - QCOMPARE(object->property("importedModuleAttachedPropertyValue"), QVariant(false)); - delete object; - QDeclarativeComponent failFourComponent(&engine, TEST_FILE("jsimportfail/failFour.qml")); - expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/failFour.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: JsQtTest"); - QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData()); - object = failFourComponent.create(); - QVERIFY(object != 0); - QCOMPARE(object->property("importedModuleEnumValue"), QVariant(0)); - delete object; - QDeclarativeComponent failFiveComponent(&engine, TEST_FILE("jsimportfail/failFive.qml")); - expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/importWithImports.js").toLocalFile() + QLatin1String(":8: ReferenceError: Can't find variable: Component"); - QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData()); - expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/importPragmaLibrary.js").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: Component"); - QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData()); - object = failFiveComponent.create(); - QVERIFY(object != 0); - QCOMPARE(object->property("componentError"), QVariant(0)); - delete object; + QTest::newRow("javascript imports in an import should be private to the import scope") + << testFileUrl("jsimportfail/failTwo.qml") + << QString() + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/failTwo.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: ImportOneJs"))) + << (QStringList() << QLatin1String("importScriptFunctionValue")) + << (QVariantList() << QVariant(QString())); - // also, test that importing scripts with .pragma library works as required - QDeclarativeComponent pragmaLibraryComponent(&engine, TEST_FILE("jsimport/testImportPragmaLibrary.qml")); - object = pragmaLibraryComponent.create(); - QVERIFY(object != 0); - QCOMPARE(object->property("testValue"), QVariant(31)); - delete object; + QTest::newRow("module imports in an import should be private to the import scope") + << testFileUrl("jsimportfail/failThree.qml") + << QString() + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/failThree.qml").toLocalFile() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined"))) + << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue")) + << (QVariantList() << QVariant(false)); - // and that .pragma library scripts don't inherit imports from any .qml file - QDeclarativeComponent pragmaLibraryComponentTwo(&engine, TEST_FILE("jsimportfail/testImportPragmaLibrary.qml")); - object = pragmaLibraryComponentTwo.create(); - QVERIFY(object != 0); - QCOMPARE(object->property("testValue"), QVariant(0)); - delete object; + QTest::newRow("typenames in an import should be private to the import scope") + << testFileUrl("jsimportfail/failFour.qml") + << QString() + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/failFour.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: JsQtTest"))) + << (QStringList() << QLatin1String("importedModuleEnumValue")) + << (QVariantList() << QVariant(0)); + + QTest::newRow("import with imports has it's own activation scope") + << testFileUrl("jsimportfail/failFive.qml") + << QString() + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/importWithImports.js").toLocalFile() + QLatin1String(":8: ReferenceError: Can't find variable: Component")) + << QString(QLatin1String("file://") + testFileUrl("jsimportfail/importPragmaLibrary.js").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: Component"))) + << (QStringList() << QLatin1String("componentError")) + << (QVariantList() << QVariant(0)); + + QTest::newRow("import pragma library script") + << testFileUrl("jsimport/testImportPragmaLibrary.qml") + << QString() + << QStringList() + << (QStringList() << QLatin1String("testValue")) + << (QVariantList() << QVariant(31)); + + QTest::newRow("pragma library imports shouldn't inherit parent imports or scope") + << testFileUrl("jsimportfail/testImportPragmaLibrary.qml") + << QString() + << QStringList() + << (QStringList() << QLatin1String("testValue")) + << (QVariantList() << QVariant(0)); + + QTest::newRow("import pragma library script which has an import") + << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml") + << QString() + << QStringList() + << (QStringList() << QLatin1String("testValue")) + << (QVariantList() << QVariant(55)); + + QTest::newRow("import pragma library script which has a pragma library import") + << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml") + << QString() + << QStringList() + << (QStringList() << QLatin1String("testValue")) + << (QVariantList() << QVariant(18)); } -void tst_qdeclarativeecmascript::scarceResources() +void tst_qdeclarativeecmascript::importScripts() +{ + QFETCH(QUrl, testfile); + QFETCH(QString, errorMessage); + QFETCH(QStringList, warningMessages); + QFETCH(QStringList, propertyNames); + QFETCH(QVariantList, propertyValues); + + QDeclarativeComponent component(&engine, testfile); + + if (!errorMessage.isEmpty()) + QTest::ignoreMessage(QtWarningMsg, errorMessage.toAscii().constData()); + + if (warningMessages.size()) + foreach (const QString &warning, warningMessages) + QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData()); + + QObject *object = component.create(); + if (!errorMessage.isEmpty()) { + QVERIFY(object == 0); + } else { + QVERIFY(object != 0); + for (int i = 0; i < propertyNames.size(); ++i) + QCOMPARE(object->property(propertyNames.at(i).toAscii().constData()), propertyValues.at(i)); + delete object; + } +} + +void tst_qdeclarativeecmascript::scarceResources_other() { + /* These tests require knowledge of state, since we test values after + performing signal or function invocation. */ + QPixmap origPixmap(100, 100); origPixmap.fill(Qt::blue); - + QString srp_name, expectedWarning; QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&engine); ScarceResourceObject *eo = 0; + QObject *srsc = 0; QObject *object = 0; - // in the following three cases, the instance created from the component - // has a property which is a copy of the scarce resource; hence, the - // resource should NOT be detached prior to deletion of the object instance, - // unless the resource is destroyed explicitly. - QDeclarativeComponent component(&engine, TEST_FILE("scarceresources/scarceResourceCopy.qml")); - object = component.create(); - QVERIFY(object != 0); - QVERIFY(object->property("scarceResourceCopy").isValid()); - QCOMPARE(object->property("scarceResourceCopy").value(), origPixmap); - QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point. + /* property var semantics */ + + // test that scarce resources are handled properly in signal invocation + QDeclarativeComponent varComponentTen(&engine, testFileUrl("scarceResourceSignal.var.qml")); + object = varComponentTen.create(); + srsc = object->findChild("srsc"); + QVERIFY(srsc); + QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet. + QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5. + eo = qobject_cast(QDeclarativeProperty::read(object, "a").value()); + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. + QMetaObject::invokeMethod(srsc, "testSignal"); + QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated + QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10. + eo = qobject_cast(QDeclarativeProperty::read(object, "a").value()); + QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage. + QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap. + QVERIFY(srsc->property("scarceResourceCopy").isValid()); + QCOMPARE(srsc->property("scarceResourceCopy").value(), origPixmap); eo = qobject_cast(QDeclarativeProperty::read(object, "a").value()); - QVERIFY(!eo->scarceResourceIsDetached()); // there are two copies of it in existence: the property of object, and the property of eo. + QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now. + QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point. delete object; - QDeclarativeComponent componentTwo(&engine, TEST_FILE("scarceresources/scarceResourceCopyFromJs.qml")); - object = componentTwo.create(); + // test that scarce resources are handled properly from js functions in qml files + QDeclarativeComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml")); + object = varComponentEleven.create(); QVERIFY(object != 0); - QVERIFY(object->property("scarceResourceCopy").isValid()); + QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid + eo = qobject_cast(QDeclarativeProperty::read(object, "a").value()); + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. + QMetaObject::invokeMethod(object, "retrieveScarceResource"); + QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid. QCOMPARE(object->property("scarceResourceCopy").value(), origPixmap); - QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point. eo = qobject_cast(QDeclarativeProperty::read(object, "a").value()); - QVERIFY(!eo->scarceResourceIsDetached()); // there are two copies of it in existence: the property of object, and the property of eo. - delete object; - - QDeclarativeComponent componentThree(&engine, TEST_FILE("scarceresources/scarceResourceDestroyedCopy.qml")); - object = componentThree.create(); - QVERIFY(object != 0); - QVERIFY(!(object->property("scarceResourceCopy").isValid())); // was manually released prior to being returned. - QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point. + QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage. + QMetaObject::invokeMethod(object, "releaseScarceResource"); + QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid eo = qobject_cast(QDeclarativeProperty::read(object, "a").value()); - QVERIFY(eo->scarceResourceIsDetached()); // should have explicitly been released during the evaluation of the binding. - delete object; - - // in the following three cases, no other copy should exist in memory, - // and so it should be detached (unless explicitly preserved). - QDeclarativeComponent componentFour(&engine, TEST_FILE("scarceresources/scarceResourceTest.qml")); - object = componentFour.create(); - QVERIFY(object != 0); - QVERIFY(object->property("scarceResourceTest").isValid()); - QCOMPARE(object->property("scarceResourceTest").toInt(), 100); + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point. - eo = qobject_cast(QDeclarativeProperty::read(object, "a").value()); - QVERIFY(eo->scarceResourceIsDetached()); // the resource should have been released after the binding was evaluated. delete object; - QDeclarativeComponent componentFive(&engine, TEST_FILE("scarceresources/scarceResourceTestPreserve.qml")); - object = componentFive.create(); + // test that if an exception occurs while invoking js function from cpp, that the resources are released. + QDeclarativeComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml")); + object = varComponentTwelve.create(); QVERIFY(object != 0); - QVERIFY(object->property("scarceResourceTest").isValid()); - QCOMPARE(object->property("scarceResourceTest").toInt(), 100); - QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point. + QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid eo = qobject_cast(QDeclarativeProperty::read(object, "a").value()); - QVERIFY(!eo->scarceResourceIsDetached()); // this won't be detached since we explicitly preserved it. - delete object; - - QDeclarativeComponent componentSix(&engine, TEST_FILE("scarceresources/scarceResourceTestMultiple.qml")); - object = componentSix.create(); - QVERIFY(object != 0); - QVERIFY(object->property("scarceResourceTest").isValid()); - QCOMPARE(object->property("scarceResourceTest").toInt(), 100); - QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point. + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. + srp_name = object->property("srp_name").toString(); + expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function"); + QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed. + QMetaObject::invokeMethod(object, "retrieveScarceResource"); + QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred. eo = qobject_cast(QDeclarativeProperty::read(object, "a").value()); - QVERIFY(eo->scarceResourceIsDetached()); // all resources were released manually or automatically released. - delete object; - - // test that scarce resources are handled correctly for imports - QDeclarativeComponent componentSeven(&engine, TEST_FILE("scarceresources/scarceResourceCopyImportNoBinding.qml")); - object = componentSeven.create(); - QVERIFY(object != 0); // the import should have caused the addition of a resource to the ScarceResources list - QVERIFY(ep->scarceResources.isEmpty()); // but they should have been released by this point. - delete object; - - QDeclarativeComponent componentEight(&engine, TEST_FILE("scarceresources/scarceResourceCopyImportFail.qml")); - object = componentEight.create(); - QVERIFY(object != 0); - QVERIFY(!object->property("scarceResourceCopy").isValid()); // wasn't preserved, so shouldn't be valid. + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point. delete object; - QDeclarativeComponent componentNine(&engine, TEST_FILE("scarceresources/scarceResourceCopyImport.qml")); - object = componentNine.create(); + // test that if an Item which has JS ownership but has a scarce resource property is garbage collected, + // that the scarce resource is removed from the engine's list of scarce resources to clean up. + QDeclarativeComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml")); + object = varComponentThirteen.create(); QVERIFY(object != 0); - QVERIFY(object->property("scarceResourceCopy").isValid()); // preserved, so should be valid. - QCOMPARE(object->property("scarceResourceCopy").value(), origPixmap); - QVERIFY(object->property("scarceResourceAssignedCopyOne").isValid()); // assigned before destroy(), so should be valid. - QCOMPARE(object->property("scarceResourceAssignedCopyOne").value(), origPixmap); - QVERIFY(!object->property("scarceResourceAssignedCopyTwo").isValid()); // assigned after destroy(), so should be invalid. - QVERIFY(ep->scarceResources.isEmpty()); // this will still be zero, because "preserve()" REMOVES it from this list. + QVERIFY(!object->property("varProperty").isValid()); // not assigned yet + QMetaObject::invokeMethod(object, "assignVarProperty"); + QVERIFY(ep->scarceResources.isEmpty()); // the scarce resource is a VME property. + QMetaObject::invokeMethod(object, "deassignVarProperty"); + QVERIFY(ep->scarceResources.isEmpty()); // should still be empty; the resource should have been released on gc. delete object; + /* property variant semantics */ + // test that scarce resources are handled properly in signal invocation - QDeclarativeComponent componentTen(&engine, TEST_FILE("scarceresources/scarceResourceSignal.qml")); - object = componentTen.create(); + QDeclarativeComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml")); + object = variantComponentTen.create(); QVERIFY(object != 0); - QObject *srsc = object->findChild("srsc"); + srsc = object->findChild("srsc"); QVERIFY(srsc); QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet. QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5. @@ -3086,8 +3374,8 @@ void tst_qdeclarativeecmascript::scarceResources() delete object; // test that scarce resources are handled properly from js functions in qml files - QDeclarativeComponent componentEleven(&engine, TEST_FILE("scarceresources/scarceResourceFunction.qml")); - object = componentEleven.create(); + QDeclarativeComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml")); + object = variantComponentEleven.create(); QVERIFY(object != 0); QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid eo = qobject_cast(QDeclarativeProperty::read(object, "a").value()); @@ -3105,14 +3393,14 @@ void tst_qdeclarativeecmascript::scarceResources() delete object; // test that if an exception occurs while invoking js function from cpp, that the resources are released. - QDeclarativeComponent componentTwelve(&engine, TEST_FILE("scarceresources/scarceResourceFunctionFail.qml")); - object = componentTwelve.create(); + QDeclarativeComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml")); + object = variantComponentTwelve.create(); QVERIFY(object != 0); QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid eo = qobject_cast(QDeclarativeProperty::read(object, "a").value()); QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. - QString srp_name = object->property("srp_name").toString(); - QString expectedWarning = componentTwelve.url().toString() + QLatin1String(":17: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function"); + srp_name = object->property("srp_name").toString(); + expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function"); QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed. QMetaObject::invokeMethod(object, "retrieveScarceResource"); QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred. @@ -3122,25 +3410,285 @@ void tst_qdeclarativeecmascript::scarceResources() delete object; } -void tst_qdeclarativeecmascript::propertyChangeSlots() +void tst_qdeclarativeecmascript::scarceResources_data() { - // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly. - QDeclarativeComponent component(&engine, TEST_FILE("changeslots/propertyChangeSlots.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); - delete object; + QTest::addColumn("qmlFile"); + QTest::addColumn("readDetachStatus"); + QTest::addColumn("expectedDetachStatus"); + QTest::addColumn("propertyNames"); + QTest::addColumn("expectedValidity"); + QTest::addColumn("expectedValues"); + QTest::addColumn("expectedErrors"); - // ensure that invalid property names fail properly. - QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready"); - QDeclarativeComponent e1(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.1.qml")); - QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\""); - QCOMPARE(e1.errors().at(0).toString(), expectedErrorString); - object = e1.create(); - QVERIFY(object == 0); - delete object; + QPixmap origPixmap(100, 100); + origPixmap.fill(Qt::blue); + + /* property var semantics */ + + // in the following three cases, the instance created from the component + // has a property which is a copy of the scarce resource; hence, the + // resource should NOT be detached prior to deletion of the object instance, + // unless the resource is destroyed explicitly. + QTest::newRow("var: import scarce resource copy directly") + << testFileUrl("scarceResourceCopy.var.qml") + << true + << false // won't be detached, because assigned to property and not explicitly released + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList() << true) + << (QList() << origPixmap) + << QStringList(); + + QTest::newRow("var: import scarce resource copy from JS") + << testFileUrl("scarceResourceCopyFromJs.var.qml") + << true + << false // won't be detached, because assigned to property and not explicitly released + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList() << true) + << (QList() << origPixmap) + << QStringList(); + + QTest::newRow("var: import released scarce resource copy from JS") + << testFileUrl("scarceResourceDestroyedCopy.var.qml") + << true + << true // explicitly released, so it will be detached + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList() << false) + << (QList() << QVariant()) + << QStringList(); + + // in the following three cases, no other copy should exist in memory, + // and so it should be detached (unless explicitly preserved). + QTest::newRow("var: import auto-release SR from JS in binding side-effect") + << testFileUrl("scarceResourceTest.var.qml") + << true + << true // auto released, so it will be detached + << (QStringList() << QLatin1String("scarceResourceTest")) + << (QList() << true) + << (QList() << QVariant(100)) + << QStringList(); + QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect") + << testFileUrl("scarceResourceTestPreserve.var.qml") + << true + << false // won't be detached because we explicitly preserve it + << (QStringList() << QLatin1String("scarceResourceTest")) + << (QList() << true) + << (QList() << QVariant(100)) + << QStringList(); + QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect") + << testFileUrl("scarceResourceTestMultiple.var.qml") + << true + << true // will be detached because all resources were released manually or automatically. + << (QStringList() << QLatin1String("scarceResourceTest")) + << (QList() << true) + << (QList() << QVariant(100)) + << QStringList(); + + // In the following three cases, test that scarce resources are handled + // correctly for imports. + QTest::newRow("var: import with no binding") + << testFileUrl("scarceResourceCopyImportNoBinding.var.qml") + << false // cannot check detach status. + << false + << QStringList() + << QList() + << QList() + << QStringList(); + QTest::newRow("var: import with binding without explicit preserve") + << testFileUrl("scarceResourceCopyImportNoBinding.var.qml") + << false + << false + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList() << false) // will have been released prior to evaluation of binding. + << (QList() << QVariant()) + << QStringList(); + QTest::newRow("var: import with explicit release after binding evaluation") + << testFileUrl("scarceResourceCopyImport.var.qml") + << false + << false + << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual")) + << (QList() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated. + << (QList() << QVariant() << QVariant() << QVariant() << QVariant(true)) + << QStringList(); + QTest::newRow("var: import with different js objects") + << testFileUrl("scarceResourceCopyImportDifferent.var.qml") + << false + << false + << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual")) + << (QList() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object. + << (QList() << QVariant() << QVariant(origPixmap) << QVariant(false)) + << QStringList(); + QTest::newRow("var: import with different js objects and explicit release") + << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml") + << false + << false + << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo")) + << (QList() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object. + << (QList() << QVariant(origPixmap) << QVariant()) + << QStringList(); + QTest::newRow("var: import with same js objects and explicit release") + << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml") + << false + << false + << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo")) + << (QList() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object. + << (QList() << QVariant() << QVariant()) + << QStringList(); + QTest::newRow("var: binding with same js objects and explicit release") + << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml") + << false + << false + << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo")) + << (QList() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object. + << (QList() << QVariant() << QVariant()) + << QStringList(); + + + /* property variant semantics */ + + // in the following three cases, the instance created from the component + // has a property which is a copy of the scarce resource; hence, the + // resource should NOT be detached prior to deletion of the object instance, + // unless the resource is destroyed explicitly. + QTest::newRow("variant: import scarce resource copy directly") + << testFileUrl("scarceResourceCopy.variant.qml") + << true + << false // won't be detached, because assigned to property and not explicitly released + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList() << true) + << (QList() << origPixmap) + << QStringList(); + + QTest::newRow("variant: import scarce resource copy from JS") + << testFileUrl("scarceResourceCopyFromJs.variant.qml") + << true + << false // won't be detached, because assigned to property and not explicitly released + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList() << true) + << (QList() << origPixmap) + << QStringList(); + + QTest::newRow("variant: import released scarce resource copy from JS") + << testFileUrl("scarceResourceDestroyedCopy.variant.qml") + << true + << true // explicitly released, so it will be detached + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList() << false) + << (QList() << QVariant()) + << QStringList(); + + // in the following three cases, no other copy should exist in memory, + // and so it should be detached (unless explicitly preserved). + QTest::newRow("variant: import auto-release SR from JS in binding side-effect") + << testFileUrl("scarceResourceTest.variant.qml") + << true + << true // auto released, so it will be detached + << (QStringList() << QLatin1String("scarceResourceTest")) + << (QList() << true) + << (QList() << QVariant(100)) + << QStringList(); + QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect") + << testFileUrl("scarceResourceTestPreserve.variant.qml") + << true + << false // won't be detached because we explicitly preserve it + << (QStringList() << QLatin1String("scarceResourceTest")) + << (QList() << true) + << (QList() << QVariant(100)) + << QStringList(); + QTest::newRow("variant: import multiple scarce resources") + << testFileUrl("scarceResourceTestMultiple.variant.qml") + << true + << true // will be detached because all resources were released manually or automatically. + << (QStringList() << QLatin1String("scarceResourceTest")) + << (QList() << true) + << (QList() << QVariant(100)) + << QStringList(); + + // In the following three cases, test that scarce resources are handled + // correctly for imports. + QTest::newRow("variant: import with no binding") + << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml") + << false // cannot check detach status. + << false + << QStringList() + << QList() + << QList() + << QStringList(); + QTest::newRow("variant: import with binding without explicit preserve") + << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml") + << false + << false + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList() << false) // will have been released prior to evaluation of binding. + << (QList() << QVariant()) + << QStringList(); + QTest::newRow("variant: import with explicit release after binding evaluation") + << testFileUrl("scarceResourceCopyImport.variant.qml") + << false + << false + << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo")) + << (QList() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies. + << (QList() << origPixmap << origPixmap << QVariant()) + << QStringList(); +} + +void tst_qdeclarativeecmascript::scarceResources() +{ + QFETCH(QUrl, qmlFile); + QFETCH(bool, readDetachStatus); + QFETCH(bool, expectedDetachStatus); + QFETCH(QStringList, propertyNames); + QFETCH(QVariantList, expectedValidity); + QFETCH(QVariantList, expectedValues); + QFETCH(QStringList, expectedErrors); + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&engine); + ScarceResourceObject *eo = 0; + QObject *object = 0; + + QDeclarativeComponent c(&engine, qmlFile); + object = c.create(); + QVERIFY(object != 0); + for (int i = 0; i < propertyNames.size(); ++i) { + QString prop = propertyNames.at(i); + bool validity = expectedValidity.at(i).toBool(); + QVariant value = expectedValues.at(i); + + QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity); + if (value.type() == QVariant::Int) { + QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt()); + } else if (value.type() == QVariant::Pixmap) { + QCOMPARE(object->property(prop.toLatin1().constData()).value(), value.value()); + } + } + + if (readDetachStatus) { + eo = qobject_cast(QDeclarativeProperty::read(object, "a").value()); + QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus); + } + + QVERIFY(ep->scarceResources.isEmpty()); + delete object; +} + +void tst_qdeclarativeecmascript::propertyChangeSlots() +{ + // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly. + QDeclarativeComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + delete object; + // ensure that invalid property names fail properly. QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready"); - QDeclarativeComponent e2(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.2.qml")); + QDeclarativeComponent e1(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.1.qml")); + QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\""); + QCOMPARE(e1.errors().at(0).toString(), expectedErrorString); + object = e1.create(); + QVERIFY(object == 0); + delete object; + + QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready"); + QDeclarativeComponent e2(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.2.qml")); expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\""); QCOMPARE(e2.errors().at(0).toString(), expectedErrorString); object = e2.create(); @@ -3148,7 +3696,7 @@ void tst_qdeclarativeecmascript::propertyChangeSlots() delete object; QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready"); - QDeclarativeComponent e3(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.3.qml")); + QDeclarativeComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml")); expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\""); QCOMPARE(e3.errors().at(0).toString(), expectedErrorString); object = e3.create(); @@ -3156,7 +3704,7 @@ void tst_qdeclarativeecmascript::propertyChangeSlots() delete object; QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready"); - QDeclarativeComponent e4(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.4.qml")); + QDeclarativeComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml")); expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\""); QCOMPARE(e4.errors().at(0).toString(), expectedErrorString); object = e4.create(); @@ -3164,10 +3712,394 @@ void tst_qdeclarativeecmascript::propertyChangeSlots() delete object; } +void tst_qdeclarativeecmascript::propertyVar_data() +{ + QTest::addColumn("qmlFile"); + + // valid + QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyVar.1.qml"); + QTest::newRow("non-bindable object changed") << testFileUrl("propertyVar.2.qml"); + QTest::newRow("primitive changed") << testFileUrl("propertyVar.3.qml"); + QTest::newRow("javascript array modification") << testFileUrl("propertyVar.4.qml"); + QTest::newRow("javascript map modification") << testFileUrl("propertyVar.5.qml"); + QTest::newRow("javascript array assignment") << testFileUrl("propertyVar.6.qml"); + QTest::newRow("javascript map assignment") << testFileUrl("propertyVar.7.qml"); + QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml"); + QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml"); + QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml"); +} + +void tst_qdeclarativeecmascript::propertyVar() +{ + QFETCH(QUrl, qmlFile); + + QDeclarativeComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +// Tests that we can write QVariant values to var properties from C++ +void tst_qdeclarativeecmascript::propertyVarCpp() +{ + QObject *object = 0; + + // ensure that writing to and reading from a var property from cpp works as required. + // Literal values stored in var properties can be read and written as QVariants + // of a specific type, whereas object values are read as QVariantMaps. + QDeclarativeComponent component(&engine, testFileUrl("propertyVarCpp.qml")); + object = component.create(); + QVERIFY(object != 0); + // assign int to property var that currently has int assigned + QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10))); + QCOMPARE(object->property("varBound"), QVariant(15)); + QCOMPARE(object->property("intBound"), QVariant(15)); + QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int); + QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int); + // assign string to property var that current has bool assigned + QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool); + QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString")))); + QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString"))); + QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String); + // now enforce behaviour when accessing JavaScript objects from cpp. + QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map); + delete object; +} + +static void gc(QDeclarativeEngine &engine) +{ + engine.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); +} + +void tst_qdeclarativeecmascript::propertyVarOwnership() +{ + // Referenced JS objects are not collected + { + QDeclarativeComponent component(&engine, testFileUrl("propertyVarOwnership.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("test").toBool(), false); + QMetaObject::invokeMethod(object, "runTest"); + QCOMPARE(object->property("test").toBool(), true); + delete object; + } + // Referenced JS objects are not collected + { + QDeclarativeComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("test").toBool(), false); + QMetaObject::invokeMethod(object, "runTest"); + QCOMPARE(object->property("test").toBool(), true); + delete object; + } + // Qt objects are not collected until they've been dereferenced + { + QDeclarativeComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test2").toBool(), false); + QCOMPARE(object->property("test2").toBool(), false); + + QMetaObject::invokeMethod(object, "runTest"); + QCOMPARE(object->property("test1").toBool(), true); + + QPointer referencedObject = object->property("object").value(); + QVERIFY(!referencedObject.isNull()); + gc(engine); + QVERIFY(!referencedObject.isNull()); + + QMetaObject::invokeMethod(object, "runTest2"); + QCOMPARE(object->property("test2").toBool(), true); + gc(engine); + QVERIFY(referencedObject.isNull()); + + delete object; + } + // Self reference does not prevent Qt object collection + { + QDeclarativeComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + QPointer referencedObject = object->property("object").value(); + QVERIFY(!referencedObject.isNull()); + gc(engine); + QVERIFY(!referencedObject.isNull()); + + QMetaObject::invokeMethod(object, "runTest"); + gc(engine); + QVERIFY(referencedObject.isNull()); + + delete object; + } +} + +void tst_qdeclarativeecmascript::propertyVarImplicitOwnership() +{ + // The childObject has a reference to a different QObject. We want to ensure + // that the different item will not be cleaned up until required. IE, the childObject + // has implicit ownership of the constructed QObject. + QDeclarativeComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "assignCircular"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QObject *rootObject = object->property("vp").value(); + QVERIFY(rootObject != 0); + QObject *childObject = rootObject->findChild("text"); + QVERIFY(childObject != 0); + QCOMPARE(rootObject->property("rectCanary").toInt(), 5); + QCOMPARE(childObject->property("textCanary").toInt(), 10); + QMetaObject::invokeMethod(childObject, "constructQObject"); // creates a reference to a constructed QObject. + QWeakPointer qobjectGuard(childObject->property("vp").value()); // get the pointer prior to processing deleteLater events. + QVERIFY(!qobjectGuard.isNull()); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QVERIFY(!qobjectGuard.isNull()); + QMetaObject::invokeMethod(object, "deassignCircular"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QVERIFY(qobjectGuard.isNull()); // should have been collected now. + delete object; +} + +void tst_qdeclarativeecmascript::propertyVarReparent() +{ + // ensure that nothing breaks if we re-parent objects + QDeclarativeComponent component(&engine, testFileUrl("propertyVar.reparent.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "assignVarProp"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QObject *rect = object->property("vp").value(); + QObject *text = rect->findChild("textOne"); + QObject *text2 = rect->findChild("textTwo"); + QWeakPointer rectGuard(rect); + QWeakPointer textGuard(text); + QWeakPointer text2Guard(text2); + QVERIFY(!rectGuard.isNull()); + QVERIFY(!textGuard.isNull()); + QVERIFY(!text2Guard.isNull()); + QCOMPARE(text->property("textCanary").toInt(), 11); + QCOMPARE(text2->property("textCanary").toInt(), 12); + // now construct an image which we will reparent. + QMetaObject::invokeMethod(text2, "constructQObject"); + QObject *image = text2->property("vp").value(); + QWeakPointer imageGuard(image); + QVERIFY(!imageGuard.isNull()); + QCOMPARE(image->property("imageCanary").toInt(), 13); + // now reparent the "Image" object (currently, it has JS ownership) + image->setParent(text); // shouldn't be collected after deassignVp now, since has a parent. + QMetaObject::invokeMethod(text2, "deassignVp"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QCOMPARE(text->property("textCanary").toInt(), 11); + QCOMPARE(text2->property("textCanary").toInt(), 22); + QVERIFY(!imageGuard.isNull()); // should still be alive. + QCOMPARE(image->property("imageCanary").toInt(), 13); // still able to access var properties + QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2 + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QVERIFY(imageGuard.isNull()); // should now have been deleted, due to parent being deleted. + delete object; +} + +void tst_qdeclarativeecmascript::propertyVarReparentNullContext() +{ + // sometimes reparenting can cause problems + // (eg, if the ctxt is collected, varproperties are no longer available) + // this test ensures that no crash occurs in that situation. + QDeclarativeComponent component(&engine, testFileUrl("propertyVar.reparent.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "assignVarProp"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QObject *rect = object->property("vp").value(); + QObject *text = rect->findChild("textOne"); + QObject *text2 = rect->findChild("textTwo"); + QWeakPointer rectGuard(rect); + QWeakPointer textGuard(text); + QWeakPointer text2Guard(text2); + QVERIFY(!rectGuard.isNull()); + QVERIFY(!textGuard.isNull()); + QVERIFY(!text2Guard.isNull()); + QCOMPARE(text->property("textCanary").toInt(), 11); + QCOMPARE(text2->property("textCanary").toInt(), 12); + // now construct an image which we will reparent. + QMetaObject::invokeMethod(text2, "constructQObject"); + QObject *image = text2->property("vp").value(); + QWeakPointer imageGuard(image); + QVERIFY(!imageGuard.isNull()); + QCOMPARE(image->property("imageCanary").toInt(), 13); + // now reparent the "Image" object (currently, it has JS ownership) + image->setParent(object); // reparented to base object. after deassignVarProp, the ctxt will be invalid. + QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2 + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QVERIFY(!imageGuard.isNull()); // should still be alive. + QVERIFY(!image->property("imageCanary").isValid()); // but varProperties won't be available (null context). + delete object; + QVERIFY(imageGuard.isNull()); // should now be dead. +} + +void tst_qdeclarativeecmascript::propertyVarCircular() +{ + // enforce behaviour regarding circular references - ensure qdvmemo deletion. + QDeclarativeComponent component(&engine, testFileUrl("propertyVar.circular.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QCOMPARE(object->property("canaryInt"), QVariant(5)); + QVariant canaryResourceVariant = object->property("canaryResource"); + QVERIFY(canaryResourceVariant.isValid()); + QPixmap canaryResourcePixmap = canaryResourceVariant.value(); + canaryResourceVariant = QVariant(); // invalidate it to remove one copy of the pixmap from memory. + QMetaObject::invokeMethod(object, "deassignCanaryResource"); // remove one copy of the pixmap from memory + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QVERIFY(!canaryResourcePixmap.isDetached()); // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog. + QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QCOMPARE(object->property("canaryInt"), QVariant(2)); + QCOMPARE(object->property("canaryResource"), QVariant(1)); + QVERIFY(canaryResourcePixmap.isDetached()); // now detached, since orig copy was member of qdvmemo which was deleted. + delete object; +} + +void tst_qdeclarativeecmascript::propertyVarCircular2() +{ + // track deletion of JS-owned parent item with Cpp-owned child + // where the child has a var property referencing its parent. + QDeclarativeComponent component(&engine, testFileUrl("propertyVar.circular.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "assignCircular"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QObject *rootObject = object->property("vp").value(); + QVERIFY(rootObject != 0); + QObject *childObject = rootObject->findChild("text"); + QVERIFY(childObject != 0); + QWeakPointer rootObjectTracker(rootObject); + QVERIFY(!rootObjectTracker.isNull()); + QWeakPointer childObjectTracker(childObject); + QVERIFY(!childObjectTracker.isNull()); + gc(engine); + QCOMPARE(rootObject->property("rectCanary").toInt(), 5); + QCOMPARE(childObject->property("textCanary").toInt(), 10); + QMetaObject::invokeMethod(object, "deassignCircular"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QVERIFY(rootObjectTracker.isNull()); // should have been collected + QVERIFY(childObjectTracker.isNull()); // should have been collected + delete object; +} + +void tst_qdeclarativeecmascript::propertyVarWeakRefCallback(v8::Persistent object, void* parameter) +{ + *(int*)(parameter) += 1; + qPersistentDispose(object); +} + +void tst_qdeclarativeecmascript::propertyVarInheritance() +{ + int propertyVarWeakRefCallbackCount = 0; + + // enforce behaviour regarding element inheritance - ensure handle disposal. + // The particular component under test here has a chain of references. + QDeclarativeComponent component(&engine, testFileUrl("propertyVar.inherit.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + // we want to be able to track when the varProperties array of the last metaobject is disposed + QObject *cco5 = object->property("varProperty").value()->property("vp").value()->property("vp").value()->property("vp").value()->property("vp").value(); + QObject *ico5 = object->property("varProperty").value()->property("inheritanceVarProperty").value()->property("vp").value()->property("vp").value()->property("vp").value()->property("vp").value(); + QDeclarativeVMEMetaObject *icovmemo = ((QDeclarativeVMEMetaObject *)(ico5->metaObject())); + QDeclarativeVMEMetaObject *ccovmemo = ((QDeclarativeVMEMetaObject *)(cco5->metaObject())); + v8::Persistent icoCanaryHandle; + v8::Persistent ccoCanaryHandle; + { + v8::HandleScope hs; + // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only + // public function which can return us a handle to something in the varProperties array. + icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ"))); + ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ"))); + // we make them weak and invoke the gc, but we should not hit the weak-callback yet + // as the varproperties array of each vmemo still references the resource. + icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback); + ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback); + gc(engine); + QVERIFY(propertyVarWeakRefCallbackCount == 0); + } + // now we deassign the var prop, which should trigger collection of item subtrees. + QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + // ensure that there are only weak handles to the underlying varProperties array remaining. + gc(engine); + QCOMPARE(propertyVarWeakRefCallbackCount, 2); // should have been called for both, since all refs should be weak. + delete object; + // since there are no parent vmemo's to keep implicit references alive, and the only handles + // to what remains are weak, all varProperties arrays must have been collected. +} + +void tst_qdeclarativeecmascript::propertyVarInheritance2() +{ + int propertyVarWeakRefCallbackCount = 0; + + // The particular component under test here does NOT have a chain of references; the + // only link between rootObject and childObject is that rootObject is the parent of childObject. + QDeclarativeComponent component(&engine, testFileUrl("propertyVar.circular.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "assignCircular"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QObject *rootObject = object->property("vp").value(); + QVERIFY(rootObject != 0); + QObject *childObject = rootObject->findChild("text"); + QVERIFY(childObject != 0); + QCOMPARE(rootObject->property("rectCanary").toInt(), 5); + QCOMPARE(childObject->property("textCanary").toInt(), 10); + v8::Persistent childObjectVarArrayValueHandle; + { + v8::HandleScope hs; + propertyVarWeakRefCallbackCount = 0; // reset callback count. + childObjectVarArrayValueHandle = qPersistentNew(((QDeclarativeVMEMetaObject *)(childObject->metaObject()))->vmeProperty(childObject->metaObject()->indexOfProperty("vp"))); + childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback); + gc(engine); + QVERIFY(propertyVarWeakRefCallbackCount == 0); // should not have been collected yet. + QCOMPARE(childObject->property("vp").value(), rootObject); + QCOMPARE(childObject->property("textCanary").toInt(), 10); + } + QMetaObject::invokeMethod(object, "deassignCircular"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QVERIFY(propertyVarWeakRefCallbackCount == 1); // should have been collected now. + delete object; +} + // Ensure that QObject type conversion works on binding assignment void tst_qdeclarativeecmascript::elementAssign() { - QDeclarativeComponent component(&engine, TEST_FILE("elementAssign.qml")); + QDeclarativeComponent component(&engine, testFileUrl("elementAssign.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -3180,7 +4112,7 @@ void tst_qdeclarativeecmascript::elementAssign() // QTBUG-12457 void tst_qdeclarativeecmascript::objectPassThroughSignals() { - QDeclarativeComponent component(&engine, TEST_FILE("objectsPassThroughSignals.qml")); + QDeclarativeComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -3190,10 +4122,25 @@ void tst_qdeclarativeecmascript::objectPassThroughSignals() delete object; } +// QTBUG-21626 +void tst_qdeclarativeecmascript::objectConversion() +{ + QDeclarativeComponent component(&engine, testFileUrl("objectConversion.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + QVariant retn; + QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn)); + QCOMPARE(retn.value().value("test"), QVariant(100)); + + delete object; +} + + // QTBUG-20242 void tst_qdeclarativeecmascript::booleanConversion() { - QDeclarativeComponent component(&engine, TEST_FILE("booleanConversion.qml")); + QDeclarativeComponent component(&engine, testFileUrl("booleanConversion.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -3218,18 +4165,19 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() { // Linear QObject reference QDeclarativeEngine hrmEngine; - QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.object.1.qml")); + QDeclarativeComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml")); QObject *object = component.create(); QVERIFY(object != 0); CircularReferenceObject *cro = object->findChild("cro"); + cro->setEngine(&hrmEngine); cro->setDtorCount(&dtorCount); QMetaObject::invokeMethod(object, "createReference"); - QMetaObject::invokeMethod(object, "performGc"); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + gc(engine); QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference delete object; hrmEngine.collectGarbage(); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QCOMPARE(dtorCount, 3); } @@ -3237,18 +4185,19 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() { // Circular QObject reference QDeclarativeEngine hrmEngine; - QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.object.2.qml")); + QDeclarativeComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml")); QObject *object = component.create(); QVERIFY(object != 0); CircularReferenceObject *cro = object->findChild("cro"); + cro->setEngine(&hrmEngine); cro->setDtorCount(&dtorCount); QMetaObject::invokeMethod(object, "circularReference"); - QMetaObject::invokeMethod(object, "performGc"); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + gc(engine); QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive. delete object; hrmEngine.collectGarbage(); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QCOMPARE(dtorCount, 3); } @@ -3256,11 +4205,12 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() { // Linear handle reference QDeclarativeEngine hrmEngine; - QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.handle.1.qml")); + QDeclarativeComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.1.qml")); QObject *object = component.create(); QVERIFY(object != 0); CircularReferenceHandle *crh = object->findChild("crh"); QVERIFY(crh != 0); + crh->setEngine(&hrmEngine); crh->setDtorCount(&dtorCount); QMetaObject::invokeMethod(object, "createReference"); CircularReferenceHandle *first = object->property("first").value(); @@ -3271,12 +4221,12 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() // now we have to reparent second and make second owned by JS. second->setParent(0); QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership); - QMetaObject::invokeMethod(object, "performGc"); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + gc(engine); QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected. delete object; hrmEngine.collectGarbage(); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QCOMPARE(dtorCount, 3); } @@ -3284,11 +4234,12 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() { // Circular handle reference QDeclarativeEngine hrmEngine; - QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.handle.2.qml")); + QDeclarativeComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.2.qml")); QObject *object = component.create(); QVERIFY(object != 0); CircularReferenceHandle *crh = object->findChild("crh"); QVERIFY(crh != 0); + crh->setEngine(&hrmEngine); crh->setDtorCount(&dtorCount); QMetaObject::invokeMethod(object, "circularReference"); CircularReferenceHandle *first = object->property("first").value(); @@ -3302,12 +4253,12 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() second->setParent(0); QDeclarativeEngine::setObjectOwnership(first, QDeclarativeEngine::JavaScriptOwnership); QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership); - QMetaObject::invokeMethod(object, "performGc"); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + gc(engine); QCOMPARE(dtorCount, 2); // despite circular references, both will be collected. delete object; hrmEngine.collectGarbage(); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QCOMPARE(dtorCount, 3); } @@ -3316,8 +4267,8 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() // multiple engine interaction - linear reference QDeclarativeEngine hrmEngine1; QDeclarativeEngine hrmEngine2; - QDeclarativeComponent component1(&hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml")); - QDeclarativeComponent component2(&hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml")); + QDeclarativeComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml")); + QDeclarativeComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml")); QObject *object1 = component1.create(); QObject *object2 = component2.create(); QVERIFY(object1 != 0); @@ -3326,6 +4277,8 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() CircularReferenceHandle *crh2 = object2->findChild("crh"); QVERIFY(crh1 != 0); QVERIFY(crh2 != 0); + crh1->setEngine(&hrmEngine1); + crh2->setEngine(&hrmEngine2); crh1->setDtorCount(&dtorCount); crh2->setDtorCount(&dtorCount); QMetaObject::invokeMethod(object1, "createReference"); @@ -3342,15 +4295,16 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() // now we have to reparent second2 and make second2 owned by JS. second2->setParent(0); QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership); - QMetaObject::invokeMethod(object1, "performGc"); - QMetaObject::invokeMethod(object2, "performGc"); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + gc(engine); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected. delete object1; delete object2; hrmEngine1.collectGarbage(); hrmEngine2.collectGarbage(); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QCOMPARE(dtorCount, 6); } @@ -3359,8 +4313,8 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() // multiple engine interaction - circular reference QDeclarativeEngine hrmEngine1; QDeclarativeEngine hrmEngine2; - QDeclarativeComponent component1(&hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml")); - QDeclarativeComponent component2(&hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml")); + QDeclarativeComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml")); + QDeclarativeComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml")); QObject *object1 = component1.create(); QObject *object2 = component2.create(); QVERIFY(object1 != 0); @@ -3369,6 +4323,8 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() CircularReferenceHandle *crh2 = object2->findChild("crh"); QVERIFY(crh1 != 0); QVERIFY(crh2 != 0); + crh1->setEngine(&hrmEngine1); + crh2->setEngine(&hrmEngine2); crh1->setDtorCount(&dtorCount); crh2->setDtorCount(&dtorCount); QMetaObject::invokeMethod(object1, "createReference"); @@ -3394,15 +4350,16 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership); QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership); QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership); - QMetaObject::invokeMethod(object1, "performGc"); - QMetaObject::invokeMethod(object2, "performGc"); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + gc(engine); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive. delete object1; delete object2; hrmEngine1.collectGarbage(); hrmEngine2.collectGarbage(); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QCOMPARE(dtorCount, 6); } @@ -3411,8 +4368,8 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() // multiple engine interaction - linear reference with engine deletion QDeclarativeEngine *hrmEngine1 = new QDeclarativeEngine; QDeclarativeEngine *hrmEngine2 = new QDeclarativeEngine; - QDeclarativeComponent component1(hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml")); - QDeclarativeComponent component2(hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml")); + QDeclarativeComponent component1(hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml")); + QDeclarativeComponent component2(hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml")); QObject *object1 = component1.create(); QObject *object2 = component2.create(); QVERIFY(object1 != 0); @@ -3421,6 +4378,8 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() CircularReferenceHandle *crh2 = object2->findChild("crh"); QVERIFY(crh1 != 0); QVERIFY(crh2 != 0); + crh1->setEngine(hrmEngine1); + crh2->setEngine(hrmEngine2); crh1->setDtorCount(&dtorCount); crh2->setDtorCount(&dtorCount); QMetaObject::invokeMethod(object1, "createReference"); @@ -3444,28 +4403,386 @@ void tst_qdeclarativeecmascript::handleReferenceManagement() QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership); QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership); QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership); - QMetaObject::invokeMethod(object1, "performGc"); - QMetaObject::invokeMethod(object2, "performGc"); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + gc(engine); QCOMPARE(dtorCount, 0); delete hrmEngine2; - QMetaObject::invokeMethod(object1, "performGc"); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + gc(engine); QCOMPARE(dtorCount, 0); delete object1; delete object2; hrmEngine1->collectGarbage(); - QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); QCOMPARE(dtorCount, 6); delete hrmEngine1; } } +void tst_qdeclarativeecmascript::stringArg() +{ + QDeclarativeComponent component(&engine, testFileUrl("stringArg.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "success"); + QVERIFY(object->property("returnValue").toBool()); + + QString w1 = testFileUrl("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments"); + QTest::ignoreMessage(QtWarningMsg, w1.toAscii().constData()); + QMetaObject::invokeMethod(object, "failure"); + QVERIFY(object->property("returnValue").toBool()); + + delete object; +} + +void tst_qdeclarativeecmascript::readonlyDeclaration() +{ + QDeclarativeComponent component(&engine, testFileUrl("readonlyDeclaration.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QList) +void tst_qdeclarativeecmascript::sequenceConversionRead() +{ + { + QUrl qmlFile = testFileUrl("sequenceConversion.read.qml"); + QDeclarativeComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + MySequenceConversionObject *seq = object->findChild("msco"); + QVERIFY(seq != 0); + + QMetaObject::invokeMethod(object, "readSequences"); + QList intList; intList << 1 << 2 << 3 << 4; + QCOMPARE(object->property("intListLength").toInt(), intList.length()); + QCOMPARE(object->property("intList").value >(), intList); + QList qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4; + QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length()); + QCOMPARE(object->property("qrealList").value >(), qrealList); + QList boolList; boolList << true << false << true << false; + QCOMPARE(object->property("boolListLength").toInt(), boolList.length()); + QCOMPARE(object->property("boolList").value >(), boolList); + QList stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth"); + QCOMPARE(object->property("stringListLength").toInt(), stringList.length()); + QCOMPARE(object->property("stringList").value >(), stringList); + QList urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com"); + QCOMPARE(object->property("urlListLength").toInt(), urlList.length()); + QCOMPARE(object->property("urlList").value >(), urlList); + QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth"); + QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length()); + QCOMPARE(object->property("qstringList").value(), qstringList); + + QMetaObject::invokeMethod(object, "readSequenceElements"); + QCOMPARE(object->property("intVal").toInt(), 2); + QCOMPARE(object->property("qrealVal").toReal(), 2.2); + QCOMPARE(object->property("boolVal").toBool(), false); + QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second"))); + QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com")); + QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second"))); + + QMetaObject::invokeMethod(object, "enumerateSequenceElements"); + QCOMPARE(object->property("enumerationMatches").toBool(), true); + + intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test. + QDeclarativeProperty seqProp(seq, "intListProperty"); + QCOMPARE(seqProp.read().value >(), intList); + QDeclarativeProperty seqProp2(seq, "intListProperty", &engine); + QCOMPARE(seqProp2.read().value >(), intList); + + QMetaObject::invokeMethod(object, "testReferenceDeletion"); + QCOMPARE(object->property("referenceDeletion").toBool(), true); + + delete object; + } + + { + QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml"); + QDeclarativeComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + MySequenceConversionObject *seq = object->findChild("msco"); + QVERIFY(seq != 0); + + // we haven't registered QList as a sequence type. + QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList' for property 'MySequenceConversionObject::pointListProperty'"); + QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined"); + QTest::ignoreMessage(QtWarningMsg, warningOne.toAscii().constData()); + QTest::ignoreMessage(QtWarningMsg, warningTwo.toAscii().constData()); + + QMetaObject::invokeMethod(object, "performTest"); + + // QList has not been registered as a sequence type. + QCOMPARE(object->property("pointListLength").toInt(), 0); + QVERIFY(!object->property("pointList").isValid()); + QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList' for property 'MySequenceConversionObject::pointListProperty'"); + QDeclarativeProperty seqProp(seq, "pointListProperty", &engine); + QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type + + delete object; + } +} + +void tst_qdeclarativeecmascript::sequenceConversionWrite() +{ + { + QUrl qmlFile = testFileUrl("sequenceConversion.write.qml"); + QDeclarativeComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + MySequenceConversionObject *seq = object->findChild("msco"); + QVERIFY(seq != 0); + + QMetaObject::invokeMethod(object, "writeSequences"); + QCOMPARE(object->property("success").toBool(), true); + + QMetaObject::invokeMethod(object, "writeSequenceElements"); + QCOMPARE(object->property("success").toBool(), true); + + QMetaObject::invokeMethod(object, "writeOtherElements"); + QCOMPARE(object->property("success").toBool(), true); + + QMetaObject::invokeMethod(object, "testReferenceDeletion"); + QCOMPARE(object->property("referenceDeletion").toBool(), true); + + delete object; + } + + { + QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml"); + QDeclarativeComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + MySequenceConversionObject *seq = object->findChild("msco"); + QVERIFY(seq != 0); + + // we haven't registered QList as a sequence type, so writing shouldn't work. + QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to void"); + QTest::ignoreMessage(QtWarningMsg, warningOne.toAscii().constData()); + + QMetaObject::invokeMethod(object, "performTest"); + + QList pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed + QCOMPARE(seq->pointListProperty(), pointList); + + delete object; + } +} + +void tst_qdeclarativeecmascript::sequenceConversionArray() +{ + // ensure that in JS the returned sequences act just like normal JS Arrays. + QUrl qmlFile = testFileUrl("sequenceConversion.array.qml"); + QDeclarativeComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "indexedAccess"); + QVERIFY(object->property("success").toBool()); + QMetaObject::invokeMethod(object, "arrayOperations"); + QVERIFY(object->property("success").toBool()); + QMetaObject::invokeMethod(object, "testEqualitySemantics"); + QVERIFY(object->property("success").toBool()); + QMetaObject::invokeMethod(object, "testReferenceDeletion"); + QCOMPARE(object->property("referenceDeletion").toBool(), true); + delete object; +} + +void tst_qdeclarativeecmascript::sequenceConversionThreads() +{ + // ensure that sequence conversion operations work correctly in a worker thread + // and that serialisation between the main and worker thread succeeds. + QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml"); + QDeclarativeComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + + QMetaObject::invokeMethod(object, "testIntSequence"); + QTRY_VERIFY(object->property("finished").toBool()); + QVERIFY(object->property("success").toBool()); + + QMetaObject::invokeMethod(object, "testQrealSequence"); + QTRY_VERIFY(object->property("finished").toBool()); + QVERIFY(object->property("success").toBool()); + + QMetaObject::invokeMethod(object, "testBoolSequence"); + QTRY_VERIFY(object->property("finished").toBool()); + QVERIFY(object->property("success").toBool()); + + QMetaObject::invokeMethod(object, "testStringSequence"); + QTRY_VERIFY(object->property("finished").toBool()); + QVERIFY(object->property("success").toBool()); + + QMetaObject::invokeMethod(object, "testQStringSequence"); + QTRY_VERIFY(object->property("finished").toBool()); + QVERIFY(object->property("success").toBool()); + + QMetaObject::invokeMethod(object, "testUrlSequence"); + QTRY_VERIFY(object->property("finished").toBool()); + QVERIFY(object->property("success").toBool()); + + QMetaObject::invokeMethod(object, "testVariantSequence"); + QTRY_VERIFY(object->property("finished").toBool()); + QVERIFY(object->property("success").toBool()); + + delete object; +} + +void tst_qdeclarativeecmascript::sequenceConversionBindings() +{ + { + QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml"); + QDeclarativeComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + QList intList; intList << 1 << 2 << 3 << 12 << 7; + QCOMPARE(object->property("boundSequence").value >(), intList); + QCOMPARE(object->property("boundElement").toInt(), intList.at(3)); + QList intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14; + QCOMPARE(object->property("boundSequenceTwo").value >(), intListTwo); + delete object; + } + + { + QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml"); + QString warning = QString(QLatin1String("%1:17: Unable to assign QList to QList")).arg(qmlFile.toString()); + QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData()); + QDeclarativeComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + delete object; + } +} + +void tst_qdeclarativeecmascript::sequenceConversionCopy() +{ + QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml"); + QDeclarativeComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "testCopySequences"); + QCOMPARE(object->property("success").toBool(), true); + QMetaObject::invokeMethod(object, "readSequenceCopyElements"); + QCOMPARE(object->property("success").toBool(), true); + QMetaObject::invokeMethod(object, "testEqualitySemantics"); + QCOMPARE(object->property("success").toBool(), true); + delete object; +} + +void tst_qdeclarativeecmascript::assignSequenceTypes() +{ + // test binding array to sequence type property + { + QDeclarativeComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml")); + MySequenceConversionObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->intListProperty(), (QList() << 1 << 2)); + QCOMPARE(object->qrealListProperty(), (QList() << 1.1 << 2.2)); + QCOMPARE(object->boolListProperty(), (QList() << false << true)); + QCOMPARE(object->urlListProperty(), (QList() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com"))); + QCOMPARE(object->stringListProperty(), (QList() << QLatin1String("one") << QLatin1String("two"))); + QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two"))); + delete object; + } + + // test binding literal to sequence type property + { + QDeclarativeComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml")); + MySequenceConversionObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->intListProperty(), (QList() << 1)); + QCOMPARE(object->qrealListProperty(), (QList() << 1.1)); + QCOMPARE(object->boolListProperty(), (QList() << false)); + QCOMPARE(object->urlListProperty(), (QList() << QUrl("http://www.example1.com"))); + QCOMPARE(object->stringListProperty(), (QList() << QLatin1String("one"))); + QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two"))); + delete object; + } + + // test binding single value to sequence type property + { + QDeclarativeComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml")); + MySequenceConversionObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->intListProperty(), (QList() << 1)); + QCOMPARE(object->qrealListProperty(), (QList() << 1.1)); + QCOMPARE(object->boolListProperty(), (QList() << false)); + QCOMPARE(object->urlListProperty(), (QList() << QUrl(testFileUrl("example.html")))); + delete object; + } + + // test assigning array to sequence type property in js function + { + QDeclarativeComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml")); + MySequenceConversionObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->intListProperty(), (QList() << 1 << 2)); + QCOMPARE(object->qrealListProperty(), (QList() << 1.1 << 2.2)); + QCOMPARE(object->boolListProperty(), (QList() << false << true)); + QCOMPARE(object->urlListProperty(), (QList() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com"))); + QCOMPARE(object->stringListProperty(), (QList() << QLatin1String("one") << QLatin1String("two"))); + QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two"))); + delete object; + } + + // test assigning literal to sequence type property in js function + { + QDeclarativeComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml")); + MySequenceConversionObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->intListProperty(), (QList() << 1)); + QCOMPARE(object->qrealListProperty(), (QList() << 1.1)); + QCOMPARE(object->boolListProperty(), (QList() << false)); + QCOMPARE(object->urlListProperty(), (QList() << QUrl("http://www.example1.com"))); + QCOMPARE(object->stringListProperty(), (QList() << QLatin1String("one"))); + QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two"))); + delete object; + } + + // test assigning single value to sequence type property in js function + { + QDeclarativeComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml")); + MySequenceConversionObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->intListProperty(), (QList() << 1)); + QCOMPARE(object->qrealListProperty(), (QList() << 1.1)); + QCOMPARE(object->boolListProperty(), (QList() << false)); + QCOMPARE(object->urlListProperty(), (QList() << QUrl(testFileUrl("example.html")))); + delete object; + } + + // test QList literal assignment and binding assignment causes url resolution when required + { + QDeclarativeComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + MySequenceConversionObject *msco1 = object->findChild(QLatin1String("msco1")); + MySequenceConversionObject *msco2 = object->findChild(QLatin1String("msco2")); + MySequenceConversionObject *msco3 = object->findChild(QLatin1String("msco3")); + MySequenceConversionObject *msco4 = object->findChild(QLatin1String("msco4")); + MySequenceConversionObject *msco5 = object->findChild(QLatin1String("msco5")); + QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0); + QCOMPARE(msco1->urlListProperty(), (QList() << QUrl(testFileUrl("example.html")))); + QCOMPARE(msco2->urlListProperty(), (QList() << QUrl(testFileUrl("example.html")))); + QCOMPARE(msco3->urlListProperty(), (QList() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html")))); + QCOMPARE(msco4->urlListProperty(), (QList() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html")))); + QCOMPARE(msco5->urlListProperty(), (QList() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html")))); + delete object; + } +} + // Test that assigning a null object works // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4 void tst_qdeclarativeecmascript::nullObjectBinding() { - QDeclarativeComponent component(&engine, TEST_FILE("nullObjectBinding.qml")); + QDeclarativeComponent component(&engine, testFileUrl("nullObjectBinding.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -3479,7 +4796,7 @@ void tst_qdeclarativeecmascript::nullObjectBinding() void tst_qdeclarativeecmascript::deletedEngine() { QDeclarativeEngine *engine = new QDeclarativeEngine; - QDeclarativeComponent component(engine, TEST_FILE("deletedEngine.qml")); + QDeclarativeComponent component(engine, testFileUrl("deletedEngine.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -3500,7 +4817,7 @@ void tst_qdeclarativeecmascript::deletedEngine() // Test the crashing part of QTBUG-9705 void tst_qdeclarativeecmascript::libraryScriptAssert() { - QDeclarativeComponent component(&engine, TEST_FILE("libraryScriptAssert.qml")); + QDeclarativeComponent component(&engine, testFileUrl("libraryScriptAssert.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -3510,7 +4827,7 @@ void tst_qdeclarativeecmascript::libraryScriptAssert() void tst_qdeclarativeecmascript::variantsAssignedUndefined() { - QDeclarativeComponent component(&engine, TEST_FILE("variantsAssignedUndefined.qml")); + QDeclarativeComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -3529,7 +4846,7 @@ void tst_qdeclarativeecmascript::variantsAssignedUndefined() void tst_qdeclarativeecmascript::qtbug_9792() { - QDeclarativeComponent component(&engine, TEST_FILE("qtbug_9792.qml")); + QDeclarativeComponent component(&engine, testFileUrl("qtbug_9792.qml")); QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext()); @@ -3556,7 +4873,7 @@ void tst_qdeclarativeecmascript::qtbug_9792() // Verifies that QDeclarativeGuard<>s used in the vmemetaobject are cleaned correctly void tst_qdeclarativeecmascript::qtcreatorbug_1289() { - QDeclarativeComponent component(&engine, TEST_FILE("qtcreatorbug_1289.qml")); + QDeclarativeComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -3578,7 +4895,7 @@ void tst_qdeclarativeecmascript::qtcreatorbug_1289() void tst_qdeclarativeecmascript::noSpuriousWarningsAtShutdown() { { - QDeclarativeComponent component(&engine, TEST_FILE("noSpuriousWarningsAtShutdown.qml")); + QDeclarativeComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml")); QObject *o = component.create(); @@ -3594,7 +4911,7 @@ void tst_qdeclarativeecmascript::noSpuriousWarningsAtShutdown() { - QDeclarativeComponent component(&engine, TEST_FILE("noSpuriousWarningsAtShutdown.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml")); QObject *o = component.create(); @@ -3612,7 +4929,7 @@ void tst_qdeclarativeecmascript::noSpuriousWarningsAtShutdown() void tst_qdeclarativeecmascript::canAssignNullToQObject() { { - QDeclarativeComponent component(&engine, TEST_FILE("canAssignNullToQObject.1.qml")); + QDeclarativeComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml")); MyQmlObject *o = qobject_cast(component.create()); QVERIFY(o != 0); @@ -3627,7 +4944,7 @@ void tst_qdeclarativeecmascript::canAssignNullToQObject() } { - QDeclarativeComponent component(&engine, TEST_FILE("canAssignNullToQObject.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml")); MyQmlObject *o = qobject_cast(component.create()); QVERIFY(o != 0); @@ -3640,7 +4957,7 @@ void tst_qdeclarativeecmascript::canAssignNullToQObject() void tst_qdeclarativeecmascript::functionAssignment_fromBinding() { - QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.1.qml")); + QDeclarativeComponent component(&engine, testFileUrl("functionAssignment.1.qml")); QString url = component.url().toString(); QString warning = url + ":4: Unable to assign a function to a property."; @@ -3658,7 +4975,7 @@ void tst_qdeclarativeecmascript::functionAssignment_fromJS() { QFETCH(QString, triggerProperty); - QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("functionAssignment.2.qml")); QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString())); MyQmlObject *o = qobject_cast(component.create()); @@ -3690,7 +5007,7 @@ void tst_qdeclarativeecmascript::functionAssignment_fromJS_data() void tst_qdeclarativeecmascript::functionAssignmentfromJS_invalid() { - QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("functionAssignment.2.qml")); QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString())); MyQmlObject *o = qobject_cast(component.create()); @@ -3714,7 +5031,7 @@ void tst_qdeclarativeecmascript::functionAssignmentfromJS_invalid() void tst_qdeclarativeecmascript::eval() { - QDeclarativeComponent component(&engine, TEST_FILE("eval.qml")); + QDeclarativeComponent component(&engine, testFileUrl("eval.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -3730,7 +5047,7 @@ void tst_qdeclarativeecmascript::eval() void tst_qdeclarativeecmascript::function() { - QDeclarativeComponent component(&engine, TEST_FILE("function.qml")); + QDeclarativeComponent component(&engine, testFileUrl("function.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -3747,7 +5064,7 @@ void tst_qdeclarativeecmascript::include() { // Non-library relative include { - QDeclarativeComponent component(&engine, TEST_FILE("include.qml")); + QDeclarativeComponent component(&engine, testFileUrl("include.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -3763,7 +5080,7 @@ void tst_qdeclarativeecmascript::include() // Library relative include { - QDeclarativeComponent component(&engine, TEST_FILE("include_shared.qml")); + QDeclarativeComponent component(&engine, testFileUrl("include_shared.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -3779,7 +5096,7 @@ void tst_qdeclarativeecmascript::include() // Callback { - QDeclarativeComponent component(&engine, TEST_FILE("include_callback.qml")); + QDeclarativeComponent component(&engine, testFileUrl("include_callback.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -3795,7 +5112,7 @@ void tst_qdeclarativeecmascript::include() // Including file with ".pragma library" { - QDeclarativeComponent component(&engine, TEST_FILE("include_pragma.qml")); + QDeclarativeComponent component(&engine, testFileUrl("include_pragma.qml")); QObject *o = component.create(); QVERIFY(o != 0); QCOMPARE(o->property("test1").toInt(), 100); @@ -3807,9 +5124,9 @@ void tst_qdeclarativeecmascript::include() { TestHTTPServer server(8111); QVERIFY(server.isValid()); - server.serveDirectory(SRCDIR "/data"); + server.serveDirectory(dataDirectory()); - QDeclarativeComponent component(&engine, TEST_FILE("include_remote.qml")); + QDeclarativeComponent component(&engine, testFileUrl("include_remote.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -3835,9 +5152,9 @@ void tst_qdeclarativeecmascript::include() { TestHTTPServer server(8111); QVERIFY(server.isValid()); - server.serveDirectory(SRCDIR "/data"); + server.serveDirectory(dataDirectory()); - QDeclarativeComponent component(&engine, TEST_FILE("include_remote_missing.qml")); + QDeclarativeComponent component(&engine, testFileUrl("include_remote_missing.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -3853,7 +5170,7 @@ void tst_qdeclarativeecmascript::include() void tst_qdeclarativeecmascript::signalHandlers() { - QDeclarativeComponent component(&engine, TEST_FILE("signalHandlers.qml")); + QDeclarativeComponent component(&engine, testFileUrl("signalHandlers.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -3883,7 +5200,7 @@ void tst_qdeclarativeecmascript::signalHandlers() void tst_qdeclarativeecmascript::qtbug_10696() { - QDeclarativeComponent component(&engine, TEST_FILE("qtbug_10696.qml")); + QDeclarativeComponent component(&engine, testFileUrl("qtbug_10696.qml")); QObject *o = component.create(); QVERIFY(o != 0); delete o; @@ -3891,7 +5208,7 @@ void tst_qdeclarativeecmascript::qtbug_10696() void tst_qdeclarativeecmascript::qtbug_11606() { - QDeclarativeComponent component(&engine, TEST_FILE("qtbug_11606.qml")); + QDeclarativeComponent component(&engine, testFileUrl("qtbug_11606.qml")); QObject *o = component.create(); QVERIFY(o != 0); QCOMPARE(o->property("test").toBool(), true); @@ -3900,7 +5217,30 @@ void tst_qdeclarativeecmascript::qtbug_11606() void tst_qdeclarativeecmascript::qtbug_11600() { - QDeclarativeComponent component(&engine, TEST_FILE("qtbug_11600.qml")); + QDeclarativeComponent component(&engine, testFileUrl("qtbug_11600.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test").toBool(), true); + delete o; +} + +void tst_qdeclarativeecmascript::qtbug_21864() +{ + QDeclarativeComponent component(&engine, testFileUrl("qtbug_21864.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test").toBool(), true); + delete o; +} + +void tst_qdeclarativeecmascript::qobjectConnectionListExceptionHandling() +{ + // QTBUG-23375 + QDeclarativeComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml")); + QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined"); + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); QObject *o = component.create(); QVERIFY(o != 0); QCOMPARE(o->property("test").toBool(), true); @@ -3910,7 +5250,7 @@ void tst_qdeclarativeecmascript::qtbug_11600() // Reading and writing non-scriptable properties should fail void tst_qdeclarativeecmascript::nonscriptable() { - QDeclarativeComponent component(&engine, TEST_FILE("nonscriptable.qml")); + QDeclarativeComponent component(&engine, testFileUrl("nonscriptable.qml")); QObject *o = component.create(); QVERIFY(o != 0); QCOMPARE(o->property("readOk").toBool(), true); @@ -3921,7 +5261,7 @@ void tst_qdeclarativeecmascript::nonscriptable() // deleteLater() should not be callable from QML void tst_qdeclarativeecmascript::deleteLater() { - QDeclarativeComponent component(&engine, TEST_FILE("deleteLater.qml")); + QDeclarativeComponent component(&engine, testFileUrl("deleteLater.qml")); QObject *o = component.create(); QVERIFY(o != 0); QCOMPARE(o->property("test").toBool(), true); @@ -3930,7 +5270,7 @@ void tst_qdeclarativeecmascript::deleteLater() void tst_qdeclarativeecmascript::in() { - QDeclarativeComponent component(&engine, TEST_FILE("in.qml")); + QDeclarativeComponent component(&engine, testFileUrl("in.qml")); QObject *o = component.create(); QVERIFY(o != 0); QCOMPARE(o->property("test1").toBool(), true); @@ -3938,9 +5278,37 @@ void tst_qdeclarativeecmascript::in() delete o; } +void tst_qdeclarativeecmascript::typeOf() +{ + QDeclarativeComponent component(&engine, testFileUrl("typeOf.qml")); + + // These warnings should not happen once QTBUG-21864 is fixed + QString warning1 = component.url().toString() + QLatin1String(":16: Error: Cannot assign [undefined] to QString"); + QString warning2 = component.url().resolved(QUrl("typeOf.js")).toString() + QLatin1String(":1: ReferenceError: Can't find variable: a"); + + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); + + QObject *o = component.create(); + QVERIFY(o != 0); + + QEXPECT_FAIL("", "QTBUG-21864", Abort); + QCOMPARE(o->property("test1").toString(), QLatin1String("undefined")); + QCOMPARE(o->property("test2").toString(), QLatin1String("object")); + QCOMPARE(o->property("test3").toString(), QLatin1String("number")); + QCOMPARE(o->property("test4").toString(), QLatin1String("string")); + QCOMPARE(o->property("test5").toString(), QLatin1String("function")); + QCOMPARE(o->property("test6").toString(), QLatin1String("object")); + QCOMPARE(o->property("test7").toString(), QLatin1String("undefined")); + QCOMPARE(o->property("test8").toString(), QLatin1String("boolean")); + QCOMPARE(o->property("test9").toString(), QLatin1String("object")); + + delete o; +} + void tst_qdeclarativeecmascript::sharedAttachedObject() { - QDeclarativeComponent component(&engine, TEST_FILE("sharedAttachedObject.qml")); + QDeclarativeComponent component(&engine, testFileUrl("sharedAttachedObject.qml")); QObject *o = component.create(); QVERIFY(o != 0); QCOMPARE(o->property("test1").toBool(), true); @@ -3951,7 +5319,7 @@ void tst_qdeclarativeecmascript::sharedAttachedObject() // QTBUG-13999 void tst_qdeclarativeecmascript::objectName() { - QDeclarativeComponent component(&engine, TEST_FILE("objectName.qml")); + QDeclarativeComponent component(&engine, testFileUrl("objectName.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -3968,7 +5336,7 @@ void tst_qdeclarativeecmascript::objectName() void tst_qdeclarativeecmascript::writeRemovesBinding() { - QDeclarativeComponent component(&engine, TEST_FILE("writeRemovesBinding.qml")); + QDeclarativeComponent component(&engine, testFileUrl("writeRemovesBinding.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -3980,7 +5348,7 @@ void tst_qdeclarativeecmascript::writeRemovesBinding() // Test bindings assigned to alias properties actually assign to the alias' target void tst_qdeclarativeecmascript::aliasBindingsAssignCorrectly() { - QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsAssignCorrectly.qml")); + QDeclarativeComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -3993,7 +5361,7 @@ void tst_qdeclarativeecmascript::aliasBindingsAssignCorrectly() void tst_qdeclarativeecmascript::aliasBindingsOverrideTarget() { { - QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.qml")); + QDeclarativeComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -4003,7 +5371,7 @@ void tst_qdeclarativeecmascript::aliasBindingsOverrideTarget() } { - QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -4013,7 +5381,7 @@ void tst_qdeclarativeecmascript::aliasBindingsOverrideTarget() } { - QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.3.qml")); + QDeclarativeComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -4027,7 +5395,7 @@ void tst_qdeclarativeecmascript::aliasBindingsOverrideTarget() void tst_qdeclarativeecmascript::aliasWritesOverrideBindings() { { - QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.qml")); + QDeclarativeComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -4037,7 +5405,7 @@ void tst_qdeclarativeecmascript::aliasWritesOverrideBindings() } { - QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -4047,7 +5415,7 @@ void tst_qdeclarativeecmascript::aliasWritesOverrideBindings() } { - QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.3.qml")); + QDeclarativeComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml")); QObject *o = component.create(); QVERIFY(o != 0); @@ -4061,7 +5429,20 @@ void tst_qdeclarativeecmascript::aliasWritesOverrideBindings() // QTBUG-20200 void tst_qdeclarativeecmascript::aliasToCompositeElement() { - QDeclarativeComponent component(&engine, TEST_FILE("aliasToCompositeElement.qml")); + QDeclarativeComponent component(&engine, testFileUrl("aliasToCompositeElement.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + delete object; +} + +void tst_qdeclarativeecmascript::qtbug_20344() +{ + QDeclarativeComponent component(&engine, testFileUrl("qtbug_20344.qml")); + + QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot"; + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); QObject *object = component.create(); QVERIFY(object != 0); @@ -4072,7 +5453,7 @@ void tst_qdeclarativeecmascript::aliasToCompositeElement() void tst_qdeclarativeecmascript::revisionErrors() { { - QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors.qml")); + QDeclarativeComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml")); QString url = component.url().toString(); QString warning1 = url + ":8: ReferenceError: Can't find variable: prop2"; @@ -4087,7 +5468,7 @@ void tst_qdeclarativeecmascript::revisionErrors() delete object; } { - QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml")); QString url = component.url().toString(); // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0 @@ -4109,7 +5490,7 @@ void tst_qdeclarativeecmascript::revisionErrors() delete object; } { - QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors3.qml")); + QDeclarativeComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml")); QString url = component.url().toString(); // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1 @@ -4129,7 +5510,7 @@ void tst_qdeclarativeecmascript::revisionErrors() void tst_qdeclarativeecmascript::revision() { { - QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision.qml")); + QDeclarativeComponent component(&engine, testFileUrl("metaobjectRevision.qml")); QString url = component.url().toString(); MyRevisionedClass *object = qobject_cast(component.create()); @@ -4137,7 +5518,7 @@ void tst_qdeclarativeecmascript::revision() delete object; } { - QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision2.qml")); + QDeclarativeComponent component(&engine, testFileUrl("metaobjectRevision2.qml")); QString url = component.url().toString(); MyRevisionedClass *object = qobject_cast(component.create()); @@ -4145,7 +5526,7 @@ void tst_qdeclarativeecmascript::revision() delete object; } { - QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision3.qml")); + QDeclarativeComponent component(&engine, testFileUrl("metaobjectRevision3.qml")); QString url = component.url().toString(); MyRevisionedClass *object = qobject_cast(component.create()); @@ -4154,7 +5535,7 @@ void tst_qdeclarativeecmascript::revision() } // Test that non-root classes can resolve revisioned methods { - QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision4.qml")); + QDeclarativeComponent component(&engine, testFileUrl("metaobjectRevision4.qml")); QObject *object = component.create(); QVERIFY(object != 0); @@ -4165,7 +5546,7 @@ void tst_qdeclarativeecmascript::revision() void tst_qdeclarativeecmascript::realToInt() { - QDeclarativeComponent component(&engine, TEST_FILE("realToInt.qml")); + QDeclarativeComponent component(&engine, testFileUrl("realToInt.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); @@ -4174,15 +5555,407 @@ void tst_qdeclarativeecmascript::realToInt() QMetaObject::invokeMethod(object, "test2"); QCOMPARE(object->value(), int(8)); } + +void tst_qdeclarativeecmascript::urlProperty() +{ + { + QDeclarativeComponent component(&engine, testFileUrl("urlProperty.1.qml")); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + object->setStringProperty("http://qt-project.org"); + QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html")); + QCOMPARE(object->intProperty(), 123); + QCOMPARE(object->value(), 1); + QCOMPARE(object->property("result").toBool(), true); + } +} + +void tst_qdeclarativeecmascript::urlPropertyWithEncoding() +{ + { + QDeclarativeComponent component(&engine, testFileUrl("urlProperty.2.qml")); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + object->setStringProperty("http://qt-project.org"); + QUrl encoded; + encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode); + QCOMPARE(object->urlProperty(), encoded); + QCOMPARE(object->value(), 0); // Interpreting URL as string yields canonicalised version + QCOMPARE(object->property("result").toBool(), true); + } +} + +void tst_qdeclarativeecmascript::urlListPropertyWithEncoding() +{ + { + QDeclarativeComponent component(&engine, testFileUrl("urlListProperty.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + MySequenceConversionObject *msco1 = object->findChild(QLatin1String("msco1")); + MySequenceConversionObject *msco2 = object->findChild(QLatin1String("msco2")); + MySequenceConversionObject *msco3 = object->findChild(QLatin1String("msco3")); + MySequenceConversionObject *msco4 = object->findChild(QLatin1String("msco4")); + QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0); + QUrl encoded; + encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode); + QCOMPARE(msco1->urlListProperty(), (QList() << encoded)); + QCOMPARE(msco2->urlListProperty(), (QList() << encoded)); + QCOMPARE(msco3->urlListProperty(), (QList() << encoded << encoded)); + QCOMPARE(msco4->urlListProperty(), (QList() << encoded << encoded)); + delete object; + } +} + void tst_qdeclarativeecmascript::dynamicString() { - QDeclarativeComponent component(&engine, TEST_FILE("dynamicString.qml")); + QDeclarativeComponent component(&engine, testFileUrl("dynamicString.qml")); QObject *object = component.create(); QVERIFY(object != 0); QCOMPARE(object->property("stringProperty").toString(), QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!")); } +void tst_qdeclarativeecmascript::automaticSemicolon() +{ + QDeclarativeComponent component(&engine, testFileUrl("automaticSemicolon.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); +} + +void tst_qdeclarativeecmascript::unaryExpression() +{ + QDeclarativeComponent component(&engine, testFileUrl("unaryExpression.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); +} + +// Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice +void tst_qdeclarativeecmascript::doubleEvaluate() +{ + QDeclarativeComponent component(&engine, testFileUrl("doubleEvaluate.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + WriteCounter *wc = qobject_cast(object); + QVERIFY(wc != 0); + QCOMPARE(wc->count(), 1); + + wc->setProperty("x", 9); + + QCOMPARE(wc->count(), 2); + + delete object; +} + +static QStringList messages; +static void captureMsgHandler(QtMsgType, const char *msg) +{ + messages.append(QLatin1String(msg)); +} + +void tst_qdeclarativeecmascript::nonNotifyable() +{ + QV4Compiler::enableV4(false); + QDeclarativeComponent component(&engine, testFileUrl("nonNotifyable.qml")); + QV4Compiler::enableV4(true); + + QtMsgHandler old = qInstallMsgHandler(captureMsgHandler); + messages.clear(); + QObject *object = component.create(); + qInstallMsgHandler(old); + + QVERIFY(object != 0); + + QString expected1 = QLatin1String("QDeclarativeExpression: Expression ") + + component.url().toString() + + QLatin1String(":5 depends on non-NOTIFYable properties:"); + QString expected2 = QLatin1String(" ") + + QLatin1String(object->metaObject()->className()) + + QLatin1String("::value"); + + QCOMPARE(messages.length(), 2); + QCOMPARE(messages.at(0), expected1); + QCOMPARE(messages.at(1), expected2); + + delete object; +} + +void tst_qdeclarativeecmascript::forInLoop() +{ + QDeclarativeComponent component(&engine, testFileUrl("forInLoop.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QMetaObject::invokeMethod(object, "listProperty"); + + QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts); + QCOMPARE(r.size(), 3); + QCOMPARE(r[0],QLatin1String("0=obj1")); + QCOMPARE(r[1],QLatin1String("1=obj2")); + QCOMPARE(r[2],QLatin1String("2=obj3")); + + //TODO: should test for in loop for other objects (such as QObjects) as well. + + delete object; +} + +// An object the binding depends on is deleted while the binding is still running +void tst_qdeclarativeecmascript::deleteWhileBindingRunning() +{ + QDeclarativeComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + delete object; +} + +void tst_qdeclarativeecmascript::qtbug_22679() +{ + MyQmlObject object; + object.setStringProperty(QLatin1String("Please work correctly")); + engine.rootContext()->setContextProperty("contextProp", &object); + + QDeclarativeComponent component(&engine, testFileUrl("qtbug_22679.qml")); + qRegisterMetaType >("QList"); + QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList))); + + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(warningsSpy.count(), 0); + delete o; +} + +void tst_qdeclarativeecmascript::qtbug_22843_data() +{ + QTest::addColumn("library"); + + QTest::newRow("without .pragma library") << false; + QTest::newRow("with .pragma library") << true; +} + +void tst_qdeclarativeecmascript::qtbug_22843() +{ + QFETCH(bool, library); + + QString fileName("qtbug_22843"); + if (library) + fileName += QLatin1String(".library"); + fileName += QLatin1String(".qml"); + + QDeclarativeComponent component(&engine, testFileUrl(fileName)); + QString url = component.url().toString(); + QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )"); + QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'"); + + qRegisterMetaType >("QList"); + QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList))); + for (int x = 0; x < 3; ++x) { + warningsSpy.clear(); + // For libraries, only the first import attempt should produce a + // SyntaxError warning; subsequent component creation should not + // attempt to reload the script. + bool expectSyntaxError = !library || (x == 0); + if (expectSyntaxError) + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0)); + delete object; + } +} + + +void tst_qdeclarativeecmascript::switchStatement() +{ + { + QDeclarativeComponent component(&engine, testFileUrl("switchStatement.1.qml")); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + // `object->value()' is the number of executed statements + + object->setStringProperty("A"); + QCOMPARE(object->value(), 5); + + object->setStringProperty("S"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("D"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("F"); + QCOMPARE(object->value(), 4); + + object->setStringProperty("something else"); + QCOMPARE(object->value(), 1); + } + + { + QDeclarativeComponent component(&engine, testFileUrl("switchStatement.2.qml")); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + // `object->value()' is the number of executed statements + + object->setStringProperty("A"); + QCOMPARE(object->value(), 5); + + object->setStringProperty("S"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("D"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("F"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("something else"); + QCOMPARE(object->value(), 4); + } + + { + QDeclarativeComponent component(&engine, testFileUrl("switchStatement.3.qml")); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + // `object->value()' is the number of executed statements + + object->setStringProperty("A"); + QCOMPARE(object->value(), 5); + + object->setStringProperty("S"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("D"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("F"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("something else"); + QCOMPARE(object->value(), 6); + } + + { + QDeclarativeComponent component(&engine, testFileUrl("switchStatement.4.qml")); + + QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int"; + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + // `object->value()' is the number of executed statements + + object->setStringProperty("A"); + QCOMPARE(object->value(), 5); + + object->setStringProperty("S"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("D"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("F"); + QCOMPARE(object->value(), 3); + + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + + object->setStringProperty("something else"); + } + + { + QDeclarativeComponent component(&engine, testFileUrl("switchStatement.5.qml")); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + // `object->value()' is the number of executed statements + + object->setStringProperty("A"); + QCOMPARE(object->value(), 1); + + object->setStringProperty("S"); + QCOMPARE(object->value(), 1); + + object->setStringProperty("D"); + QCOMPARE(object->value(), 1); + + object->setStringProperty("F"); + QCOMPARE(object->value(), 1); + + object->setStringProperty("something else"); + QCOMPARE(object->value(), 1); + } + + { + QDeclarativeComponent component(&engine, testFileUrl("switchStatement.6.qml")); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + // `object->value()' is the number of executed statements + + object->setStringProperty("A"); + QCOMPARE(object->value(), 123); + + object->setStringProperty("S"); + QCOMPARE(object->value(), 123); + + object->setStringProperty("D"); + QCOMPARE(object->value(), 321); + + object->setStringProperty("F"); + QCOMPARE(object->value(), 321); + + object->setStringProperty("something else"); + QCOMPARE(object->value(), 0); + } +} + +void tst_qdeclarativeecmascript::withStatement() +{ + { + QDeclarativeComponent component(&engine, testFileUrl("withStatement.1.qml")); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->value(), 123); + } +} + +void tst_qdeclarativeecmascript::tryStatement() +{ + { + QDeclarativeComponent component(&engine, testFileUrl("tryStatement.1.qml")); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->value(), 123); + } + + { + QDeclarativeComponent component(&engine, testFileUrl("tryStatement.2.qml")); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->value(), 321); + } + + { + QDeclarativeComponent component(&engine, testFileUrl("tryStatement.3.qml")); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->value(), 1); + } + + { + QDeclarativeComponent component(&engine, testFileUrl("tryStatement.4.qml")); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->value(), 1); + } +} + QTEST_MAIN(tst_qdeclarativeecmascript) #include "tst_qdeclarativeecmascript.moc"