diff options
Diffstat (limited to 'tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp')
-rw-r--r-- | tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp | 3105 |
1 files changed, 3105 insertions, 0 deletions
diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp new file mode 100644 index 00000000..f5b2061d --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp @@ -0,0 +1,3105 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qtest.h> +#include <QtDeclarative/qdeclarativecomponent.h> +#include <QtDeclarative/qdeclarativeengine.h> +#include <QtDeclarative/qdeclarativeexpression.h> +#include <QtDeclarative/qdeclarativecontext.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qdebug.h> +#include <QtDeclarative/private/qdeclarativeguard_p.h> +#include <QtCore/qdir.h> +#include <QtCore/qnumeric.h> +#include <private/qdeclarativeengine_p.h> +#include <private/qdeclarativeglobalscriptclass_p.h> +#include <private/qscriptdeclarativeclass_p.h> +#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 + +/* +This test covers evaluation of ECMAScript expressions and bindings from within +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 +{ + Q_OBJECT +public: + tst_qdeclarativeecmascript() {} + +private slots: + void initTestCase(); + void assignBasicTypes(); + void idShortcutInvalidates(); + void boolPropertiesEvaluateAsBool(); + void methods(); + void signalAssignment(); + void bindingLoop(); + void basicExpressions(); + void basicExpressions_data(); + void arrayExpressions(); + void contextPropertiesTriggerReeval(); + void objectPropertiesTriggerReeval(); + void deferredProperties(); + void deferredPropertiesErrors(); + void extensionObjects(); + void overrideExtensionProperties(); + void attachedProperties(); + void enums(); + void valueTypeFunctions(); + void constantsOverrideBindings(); + void outerBindingOverridesInnerBinding(); + void aliasPropertyAndBinding(); + void nonExistentAttachedObject(); + void scope(); + void signalParameterTypes(); + void objectsCompareAsEqual(); + void dynamicCreation_data(); + void dynamicCreation(); + void dynamicDestruction(); + void objectToString(); + void selfDeletingBinding(); + void extendedObjectPropertyLookup(); + void scriptErrors(); + void functionErrors(); + void propertyAssignmentErrors(); + void signalTriggeredBindings(); + void listProperties(); + void exceptionClearsOnReeval(); + void exceptionSlotProducesWarning(); + void exceptionBindingProducesWarning(); + void transientErrors(); + void shutdownErrors(); + void compositePropertyType(); + void jsObject(); + void undefinedResetsProperty(); + void listToVariant(); + void multiEngineObject(); + void deletedObject(); + void attachedPropertyScope(); + void scriptConnect(); + void scriptDisconnect(); + void ownership(); + void cppOwnershipReturnValue(); + void ownershipCustomReturnValue(); + void qlistqobjectMethods(); + void strictlyEquals(); + void compiled(); + void numberAssignment(); + void propertySplicing(); + void signalWithUnknownTypes(); + + void bug1(); + void bug2(); + void dynamicCreationCrash(); + void regExpBug(); + void nullObjectBinding(); + void deletedEngine(); + void libraryScriptAssert(); + void variantsAssignedUndefined(); + void qtbug_9792(); + void qtcreatorbug_1289(); + void noSpuriousWarningsAtShutdown(); + void canAssignNullToQObject(); + void functionAssignment_fromBinding(); + void functionAssignment_fromJS(); + void functionAssignment_fromJS_data(); + void functionAssignmentfromJS_invalid(); + void eval(); + void function(); + void qtbug_10696(); + void qtbug_11606(); + void qtbug_11600(); + void nonscriptable(); + void deleteLater(); + void in(); + void sharedAttachedObject(); + void objectName(); + void writeRemovesBinding(); + void aliasBindingsAssignCorrectly(); + void aliasBindingsOverrideTarget(); + void aliasWritesOverrideBindings(); + void pushCleanContext(); + void realToInt(); + void qtbug_20648(); + + void include(); + + void callQtInvokables(); + void invokableObjectArg(); + void invokableObjectRet(); + + void revisionErrors(); + void revision(); +private: + QDeclarativeEngine engine; +}; + +void tst_qdeclarativeecmascript::initTestCase() { registerTypes(); } + +void tst_qdeclarativeecmascript::assignBasicTypes() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("assignBasicTypes.qml")); + MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3); + QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2); + QCOMPARE(object->stringProperty(), QString("Hello World!")); + QCOMPARE(object->uintProperty(), uint(10)); + QCOMPARE(object->intProperty(), -19); + QCOMPARE((float)object->realProperty(), float(23.2)); + QCOMPARE((float)object->doubleProperty(), float(-19.75)); + QCOMPARE((float)object->floatProperty(), float(8.5)); + QCOMPARE(object->colorProperty(), QColor("red")); + QCOMPARE(object->dateProperty(), QDate(1982, 11, 25)); + QCOMPARE(object->timeProperty(), QTime(11, 11, 32)); + QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1))); + QCOMPARE(object->pointProperty(), QPoint(99,13)); + QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3)); + QCOMPARE(object->sizeProperty(), QSize(99, 13)); + QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2)); + QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200)); + QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99)); + QCOMPARE(object->boolProperty(), true); + QCOMPARE(object->variantProperty(), QVariant("Hello World!")); + QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2)); + QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml"))); + delete object; + } + { + QDeclarativeComponent component(&engine, TEST_FILE("assignBasicTypes.2.qml")); + MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3); + QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2); + QCOMPARE(object->stringProperty(), QString("Hello World!")); + QCOMPARE(object->uintProperty(), uint(10)); + QCOMPARE(object->intProperty(), -19); + QCOMPARE((float)object->realProperty(), float(23.2)); + QCOMPARE((float)object->doubleProperty(), float(-19.75)); + QCOMPARE((float)object->floatProperty(), float(8.5)); + QCOMPARE(object->colorProperty(), QColor("red")); + QCOMPARE(object->dateProperty(), QDate(1982, 11, 25)); + QCOMPARE(object->timeProperty(), QTime(11, 11, 32)); + QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1))); + QCOMPARE(object->pointProperty(), QPoint(99,13)); + QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3)); + QCOMPARE(object->sizeProperty(), QSize(99, 13)); + QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2)); + QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200)); + QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99)); + QCOMPARE(object->boolProperty(), true); + QCOMPARE(object->variantProperty(), QVariant("Hello World!")); + QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2)); + QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml"))); + delete object; + } +} + +void tst_qdeclarativeecmascript::idShortcutInvalidates() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("idShortcutInvalidates.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QVERIFY(object->objectProperty() != 0); + delete object->objectProperty(); + QVERIFY(object->objectProperty() == 0); + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("idShortcutInvalidates.1.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QVERIFY(object->objectProperty() != 0); + delete object->objectProperty(); + QVERIFY(object->objectProperty() == 0); + } +} + +void tst_qdeclarativeecmascript::boolPropertiesEvaluateAsBool() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("boolPropertiesEvaluateAsBool.1.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->stringProperty(), QLatin1String("pass")); + } + { + QDeclarativeComponent component(&engine, TEST_FILE("boolPropertiesEvaluateAsBool.2.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->stringProperty(), QLatin1String("pass")); + } +} + +void tst_qdeclarativeecmascript::signalAssignment() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("signalAssignment.1.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->string(), QString()); + emit object->basicSignal(); + QCOMPARE(object->string(), QString("pass")); + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("signalAssignment.2.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->string(), QString()); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2")); + } +} + +void tst_qdeclarativeecmascript::methods() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("methods.1.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->methodCalled(), false); + QCOMPARE(object->methodIntCalled(), false); + emit object->basicSignal(); + QCOMPARE(object->methodCalled(), true); + QCOMPARE(object->methodIntCalled(), false); + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("methods.2.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->methodCalled(), false); + QCOMPARE(object->methodIntCalled(), false); + emit object->basicSignal(); + QCOMPARE(object->methodCalled(), false); + QCOMPARE(object->methodIntCalled(), true); + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("methods.3.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("test").toInt(), 19); + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("methods.4.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("test").toInt(), 19); + QCOMPARE(object->property("test2").toInt(), 17); + QCOMPARE(object->property("test3").toInt(), 16); + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("methods.5.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("test").toInt(), 9); + } +} + +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\""; + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + QObject *object = component.create(); + QVERIFY(object != 0); +} + +void tst_qdeclarativeecmascript::basicExpressions_data() +{ + QTest::addColumn<QString>("expression"); + QTest::addColumn<QVariant>("result"); + QTest::addColumn<bool>("nest"); + + QTest::newRow("Syntax error (self test)") << "{console.log({'a':1'}.a)}" << QVariant() << false; + QTest::newRow("Context property") << "a" << QVariant(1944) << false; + QTest::newRow("Context property") << "a" << QVariant(1944) << true; + QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << false; + QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << true; + QTest::newRow("Overridden context property") << "b" << QVariant("Milk") << false; + QTest::newRow("Overridden context property") << "b" << QVariant("Cow") << true; + QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << false; + QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << true; + QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object2") << false; + QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object3") << true; + QTest::newRow("Default object property") << "horseLegs" << QVariant(4) << false; + QTest::newRow("Default object property") << "antLegs" << QVariant(6) << false; + QTest::newRow("Default object property") << "emuLegs" << QVariant(2) << false; + QTest::newRow("Nested default object property") << "horseLegs" << QVariant(4) << true; + QTest::newRow("Nested default object property") << "antLegs" << QVariant(7) << true; + QTest::newRow("Nested default object property") << "emuLegs" << QVariant(2) << true; + QTest::newRow("Nested default object property") << "humanLegs" << QVariant(2) << true; + QTest::newRow("Context property override default object property") << "millipedeLegs" << QVariant(100) << true; +} + +void tst_qdeclarativeecmascript::basicExpressions() +{ + QFETCH(QString, expression); + QFETCH(QVariant, result); + QFETCH(bool, nest); + + MyQmlObject object1; + MyQmlObject object2; + MyQmlObject object3; + MyDefaultObject1 default1; + MyDefaultObject3 default3; + object1.setStringProperty("Object1"); + object2.setStringProperty("Object2"); + object3.setStringProperty("Object3"); + + QDeclarativeContext context(engine.rootContext()); + QDeclarativeContext nestedContext(&context); + + context.setContextObject(&default1); + context.setContextProperty("a", QVariant(1944)); + context.setContextProperty("b", QVariant("Milk")); + context.setContextProperty("object", &object1); + context.setContextProperty("objectOverride", &object2); + nestedContext.setContextObject(&default3); + nestedContext.setContextProperty("b", QVariant("Cow")); + nestedContext.setContextProperty("objectOverride", &object3); + nestedContext.setContextProperty("millipedeLegs", QVariant(100)); + + MyExpression expr(nest?&nestedContext:&context, expression); + QCOMPARE(expr.evaluate(), result); +} + +void tst_qdeclarativeecmascript::arrayExpressions() +{ + QObject obj1; + QObject obj2; + QObject obj3; + + QDeclarativeContext context(engine.rootContext()); + context.setContextProperty("a", &obj1); + context.setContextProperty("b", &obj2); + context.setContextProperty("c", &obj3); + + MyExpression expr(&context, "[a, b, c, 10]"); + QVariant result = expr.evaluate(); + QCOMPARE(result.userType(), qMetaTypeId<QList<QObject *> >()); + QList<QObject *> list = qvariant_cast<QList<QObject *> >(result); + QCOMPARE(list.count(), 4); + QCOMPARE(list.at(0), &obj1); + QCOMPARE(list.at(1), &obj2); + QCOMPARE(list.at(2), &obj3); + QCOMPARE(list.at(3), (QObject *)0); +} + +// Tests that modifying a context property will reevaluate expressions +void tst_qdeclarativeecmascript::contextPropertiesTriggerReeval() +{ + QDeclarativeContext context(engine.rootContext()); + MyQmlObject object1; + MyQmlObject object2; + MyQmlObject *object3 = new MyQmlObject; + + object1.setStringProperty("Hello"); + object2.setStringProperty("World"); + + context.setContextProperty("testProp", QVariant(1)); + context.setContextProperty("testObj", &object1); + context.setContextProperty("testObj2", object3); + + { + MyExpression expr(&context, "testProp + 1"); + QCOMPARE(expr.changed, false); + QCOMPARE(expr.evaluate(), QVariant(2)); + + context.setContextProperty("testProp", QVariant(2)); + QCOMPARE(expr.changed, true); + QCOMPARE(expr.evaluate(), QVariant(3)); + } + + { + MyExpression expr(&context, "testProp + testProp + testProp"); + QCOMPARE(expr.changed, false); + QCOMPARE(expr.evaluate(), QVariant(6)); + + context.setContextProperty("testProp", QVariant(4)); + QCOMPARE(expr.changed, true); + QCOMPARE(expr.evaluate(), QVariant(12)); + } + + { + MyExpression expr(&context, "testObj.stringProperty"); + QCOMPARE(expr.changed, false); + QCOMPARE(expr.evaluate(), QVariant("Hello")); + + context.setContextProperty("testObj", &object2); + QCOMPARE(expr.changed, true); + QCOMPARE(expr.evaluate(), QVariant("World")); + } + + { + MyExpression expr(&context, "testObj.stringProperty /**/"); + QCOMPARE(expr.changed, false); + QCOMPARE(expr.evaluate(), QVariant("World")); + + context.setContextProperty("testObj", &object1); + QCOMPARE(expr.changed, true); + QCOMPARE(expr.evaluate(), QVariant("Hello")); + } + + { + MyExpression expr(&context, "testObj2"); + QCOMPARE(expr.changed, false); + QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3)); + } + +} + +void tst_qdeclarativeecmascript::objectPropertiesTriggerReeval() +{ + QDeclarativeContext context(engine.rootContext()); + MyQmlObject object1; + MyQmlObject object2; + MyQmlObject object3; + context.setContextProperty("testObj", &object1); + + object1.setStringProperty(QLatin1String("Hello")); + object2.setStringProperty(QLatin1String("Dog")); + object3.setStringProperty(QLatin1String("Cat")); + + { + MyExpression expr(&context, "testObj.stringProperty"); + QCOMPARE(expr.changed, false); + QCOMPARE(expr.evaluate(), QVariant("Hello")); + + object1.setStringProperty(QLatin1String("World")); + QCOMPARE(expr.changed, true); + QCOMPARE(expr.evaluate(), QVariant("World")); + } + + { + MyExpression expr(&context, "testObj.objectProperty.stringProperty"); + QCOMPARE(expr.changed, false); + QCOMPARE(expr.evaluate(), QVariant()); + + object1.setObjectProperty(&object2); + QCOMPARE(expr.changed, true); + expr.changed = false; + QCOMPARE(expr.evaluate(), QVariant("Dog")); + + object1.setObjectProperty(&object3); + QCOMPARE(expr.changed, true); + expr.changed = false; + QCOMPARE(expr.evaluate(), QVariant("Cat")); + + object1.setObjectProperty(0); + QCOMPARE(expr.changed, true); + expr.changed = false; + QCOMPARE(expr.evaluate(), QVariant()); + + object1.setObjectProperty(&object3); + QCOMPARE(expr.changed, true); + expr.changed = false; + QCOMPARE(expr.evaluate(), QVariant("Cat")); + + object3.setStringProperty("Donkey"); + QCOMPARE(expr.changed, true); + expr.changed = false; + QCOMPARE(expr.evaluate(), QVariant("Donkey")); + } +} + +void tst_qdeclarativeecmascript::deferredProperties() +{ + QDeclarativeComponent component(&engine, TEST_FILE("deferredProperties.qml")); + MyDeferredObject *object = + qobject_cast<MyDeferredObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->value(), 0); + QVERIFY(object->objectProperty() == 0); + QVERIFY(object->objectProperty2() != 0); + qmlExecuteDeferred(object); + QCOMPARE(object->value(), 10); + QVERIFY(object->objectProperty() != 0); + MyQmlObject *qmlObject = + qobject_cast<MyQmlObject *>(object->objectProperty()); + QVERIFY(qmlObject != 0); + QCOMPARE(qmlObject->value(), 10); + object->setValue(19); + QCOMPARE(qmlObject->value(), 19); +} + +// Check errors on deferred properties are correctly emitted +void tst_qdeclarativeecmascript::deferredPropertiesErrors() +{ + QDeclarativeComponent component(&engine, TEST_FILE("deferredPropertiesErrors.qml")); + MyDeferredObject *object = + qobject_cast<MyDeferredObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->value(), 0); + QVERIFY(object->objectProperty() == 0); + QVERIFY(object->objectProperty2() == 0); + + QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject* objectProperty"; + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + + qmlExecuteDeferred(object); + + delete object; +} + +void tst_qdeclarativeecmascript::extensionObjects() +{ + QDeclarativeComponent component(&engine, TEST_FILE("extensionObjects.qml")); + MyExtendedObject *object = + qobject_cast<MyExtendedObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->baseProperty(), 13); + QCOMPARE(object->coreProperty(), 9); + object->setProperty("extendedProperty", QVariant(11)); + object->setProperty("baseExtendedProperty", QVariant(92)); + QCOMPARE(object->coreProperty(), 11); + QCOMPARE(object->baseProperty(), 92); + + MyExtendedObject *nested = qobject_cast<MyExtendedObject*>(qvariant_cast<QObject *>(object->property("nested"))); + QVERIFY(nested); + QCOMPARE(nested->baseProperty(), 13); + QCOMPARE(nested->coreProperty(), 9); + nested->setProperty("extendedProperty", QVariant(11)); + nested->setProperty("baseExtendedProperty", QVariant(92)); + QCOMPARE(nested->coreProperty(), 11); + QCOMPARE(nested->baseProperty(), 92); + +} + +void tst_qdeclarativeecmascript::overrideExtensionProperties() +{ + QDeclarativeComponent component(&engine, TEST_FILE("extensionObjectsPropertyOverride.qml")); + OverrideDefaultPropertyObject *object = + qobject_cast<OverrideDefaultPropertyObject *>(component.create()); + QVERIFY(object != 0); + QVERIFY(object->secondProperty() != 0); + QVERIFY(object->firstProperty() == 0); +} + +void tst_qdeclarativeecmascript::attachedProperties() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("attachedProperty.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("a").toInt(), 19); + QCOMPARE(object->property("b").toInt(), 19); + QCOMPARE(object->property("c").toInt(), 19); + QCOMPARE(object->property("d").toInt(), 19); + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("attachedProperty.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("a").toInt(), 26); + QCOMPARE(object->property("b").toInt(), 26); + QCOMPARE(object->property("c").toInt(), 26); + QCOMPARE(object->property("d").toInt(), 26); + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("writeAttachedProperty.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QMetaObject::invokeMethod(object, "writeValue2"); + + MyQmlAttachedObject *attached = + qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object)); + QVERIFY(attached != 0); + + QCOMPARE(attached->value2(), 9); + } +} + +void tst_qdeclarativeecmascript::enums() +{ + // Existent enums + { + QDeclarativeComponent component(&engine, TEST_FILE("enums.1.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("a").toInt(), 0); + QCOMPARE(object->property("b").toInt(), 1); + QCOMPARE(object->property("c").toInt(), 2); + QCOMPARE(object->property("d").toInt(), 3); + QCOMPARE(object->property("e").toInt(), 0); + QCOMPARE(object->property("f").toInt(), 1); + QCOMPARE(object->property("g").toInt(), 2); + QCOMPARE(object->property("h").toInt(), 3); + QCOMPARE(object->property("i").toInt(), 19); + QCOMPARE(object->property("j").toInt(), 19); + } + // Non-existent enums + { + QDeclarativeComponent component(&engine, TEST_FILE("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"; + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); + + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("a").toInt(), 0); + QCOMPARE(object->property("b").toInt(), 0); + } +} + +void tst_qdeclarativeecmascript::valueTypeFunctions() +{ + QDeclarativeComponent component(&engine, TEST_FILE("valueTypeFunctions.qml")); + MyTypeObject *obj = qobject_cast<MyTypeObject*>(component.create()); + QVERIFY(obj != 0); + QCOMPARE(obj->rectProperty(), QRect(0,0,100,100)); + QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5)); +} + +/* +Tests that writing a constant to a property with a binding on it disables the +binding. +*/ +void tst_qdeclarativeecmascript::constantsOverrideBindings() +{ + // From ECMAScript + { + QDeclarativeComponent component(&engine, TEST_FILE("constantsOverrideBindings.1.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("c2").toInt(), 0); + object->setProperty("c1", QVariant(9)); + QCOMPARE(object->property("c2").toInt(), 9); + + emit object->basicSignal(); + + QCOMPARE(object->property("c2").toInt(), 13); + object->setProperty("c1", QVariant(8)); + QCOMPARE(object->property("c2").toInt(), 13); + } + + // During construction + { + QDeclarativeComponent component(&engine, TEST_FILE("constantsOverrideBindings.2.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("c1").toInt(), 0); + QCOMPARE(object->property("c2").toInt(), 10); + object->setProperty("c1", QVariant(9)); + QCOMPARE(object->property("c1").toInt(), 9); + QCOMPARE(object->property("c2").toInt(), 10); + } + +#if 0 + // From C++ + { + QDeclarativeComponent component(&engine, TEST_FILE("constantsOverrideBindings.3.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("c2").toInt(), 0); + object->setProperty("c1", QVariant(9)); + QCOMPARE(object->property("c2").toInt(), 9); + + object->setProperty("c2", QVariant(13)); + QCOMPARE(object->property("c2").toInt(), 13); + object->setProperty("c1", QVariant(7)); + QCOMPARE(object->property("c1").toInt(), 7); + QCOMPARE(object->property("c2").toInt(), 13); + } +#endif + + // Using an alias + { + QDeclarativeComponent component(&engine, TEST_FILE("constantsOverrideBindings.4.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("c1").toInt(), 0); + QCOMPARE(object->property("c3").toInt(), 10); + object->setProperty("c1", QVariant(9)); + QCOMPARE(object->property("c1").toInt(), 9); + QCOMPARE(object->property("c3").toInt(), 10); + } +} + +/* +Tests that assigning a binding to a property that already has a binding causes +the original binding to be disabled. +*/ +void tst_qdeclarativeecmascript::outerBindingOverridesInnerBinding() +{ + QDeclarativeComponent component(&engine, + TEST_FILE("outerBindingOverridesInnerBinding.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("c1").toInt(), 0); + QCOMPARE(object->property("c2").toInt(), 0); + QCOMPARE(object->property("c3").toInt(), 0); + + object->setProperty("c1", QVariant(9)); + QCOMPARE(object->property("c1").toInt(), 9); + QCOMPARE(object->property("c2").toInt(), 0); + QCOMPARE(object->property("c3").toInt(), 0); + + object->setProperty("c3", QVariant(8)); + QCOMPARE(object->property("c1").toInt(), 9); + QCOMPARE(object->property("c2").toInt(), 8); + QCOMPARE(object->property("c3").toInt(), 8); +} + +/* +Access a non-existent attached object. + +Tests for a regression where this used to crash. +*/ +void tst_qdeclarativeecmascript::nonExistentAttachedObject() +{ + QDeclarativeComponent component(&engine, TEST_FILE("nonExistentAttachedObject.qml")); + + QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString stringProperty"; + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + + QObject *object = component.create(); + QVERIFY(object != 0); +} + +void tst_qdeclarativeecmascript::scope() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("scope.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toInt(), 1); + QCOMPARE(object->property("test2").toInt(), 2); + QCOMPARE(object->property("test3").toString(), QString("1Test")); + QCOMPARE(object->property("test4").toString(), QString("2Test")); + QCOMPARE(object->property("test5").toInt(), 1); + QCOMPARE(object->property("test6").toInt(), 1); + QCOMPARE(object->property("test7").toInt(), 2); + QCOMPARE(object->property("test8").toInt(), 2); + QCOMPARE(object->property("test9").toInt(), 1); + QCOMPARE(object->property("test10").toInt(), 3); + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("scope.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toInt(), 19); + QCOMPARE(object->property("test2").toInt(), 19); + QCOMPARE(object->property("test3").toInt(), 14); + QCOMPARE(object->property("test4").toInt(), 14); + QCOMPARE(object->property("test5").toInt(), 24); + QCOMPARE(object->property("test6").toInt(), 24); + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("scope.3.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toBool(), true); + QCOMPARE(object->property("test2").toBool(), true); + QCOMPARE(object->property("test3").toBool(), true); + } + + // Signal argument scope + { + QDeclarativeComponent component(&engine, TEST_FILE("scope.4.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 0); + QCOMPARE(object->property("test2").toString(), QString()); + + emit object->argumentSignal(13, "Argument Scope", 9, MyQmlObject::EnumValue4, Qt::RightButton); + + QCOMPARE(object->property("test").toInt(), 13); + QCOMPARE(object->property("test2").toString(), QString("Argument Scope")); + + delete object; + } +} + +/* +Tests that "any" type passes through a synthesized signal parameter. This +is essentially a test of QDeclarativeMetaType::copy() +*/ +void tst_qdeclarativeecmascript::signalParameterTypes() +{ + QDeclarativeComponent component(&engine, TEST_FILE("signalParameterTypes.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + emit object->basicSignal(); + + QCOMPARE(object->property("intProperty").toInt(), 10); + QCOMPARE(object->property("realProperty").toReal(), 19.2); + QVERIFY(object->property("colorProperty").value<QColor>() == QColor(255, 255, 0, 255)); + QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255))); + QVERIFY(object->property("enumProperty") == MyQmlObject::EnumValue3); + QVERIFY(object->property("qtEnumProperty") == Qt::LeftButton); +} + +/* +Test that two JS objects for the same QObject compare as equal. +*/ +void tst_qdeclarativeecmascript::objectsCompareAsEqual() +{ + QDeclarativeComponent component(&engine, TEST_FILE("objectsCompareAsEqual.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toBool(), true); + QCOMPARE(object->property("test2").toBool(), true); + QCOMPARE(object->property("test3").toBool(), true); + QCOMPARE(object->property("test4").toBool(), true); + QCOMPARE(object->property("test5").toBool(), true); +} + +/* +Confirm bindings and alias properties can coexist. + +Tests for a regression where the binding would not reevaluate. +*/ +void tst_qdeclarativeecmascript::aliasPropertyAndBinding() +{ + QDeclarativeComponent component(&engine, TEST_FILE("aliasPropertyAndBinding.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("c2").toInt(), 3); + QCOMPARE(object->property("c3").toInt(), 3); + + object->setProperty("c2", QVariant(19)); + + QCOMPARE(object->property("c2").toInt(), 19); + QCOMPARE(object->property("c3").toInt(), 19); +} + +void tst_qdeclarativeecmascript::dynamicCreation_data() +{ + QTest::addColumn<QString>("method"); + QTest::addColumn<QString>("createdName"); + + QTest::newRow("One") << "createOne" << "objectOne"; + QTest::newRow("Two") << "createTwo" << "objectTwo"; + QTest::newRow("Three") << "createThree" << "objectThree"; +} + +/* +Test using createQmlObject to dynamically generate an item +Also using createComponent is tested. +*/ +void tst_qdeclarativeecmascript::dynamicCreation() +{ + QFETCH(QString, method); + QFETCH(QString, createdName); + + QDeclarativeComponent component(&engine, TEST_FILE("dynamicCreation.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + + QMetaObject::invokeMethod(object, method.toUtf8()); + QObject *created = object->objectProperty(); + QVERIFY(created); + QCOMPARE(created->objectName(), createdName); +} + +/* + Tests the destroy function +*/ +void tst_qdeclarativeecmascript::dynamicDestruction() +{ + QDeclarativeComponent component(&engine, TEST_FILE("dynamicDeletion.qml")); + QDeclarativeGuard<MyQmlObject> object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + QDeclarativeGuard<QObject> createdQmlObject = 0; + + QMetaObject::invokeMethod(object, "create"); + createdQmlObject = object->objectProperty(); + QVERIFY(createdQmlObject); + QCOMPARE(createdQmlObject->objectName(), QString("emptyObject")); + + QMetaObject::invokeMethod(object, "killOther"); + QVERIFY(createdQmlObject); + QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); + 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); + } + } + QVERIFY(!createdQmlObject); + + QDeclarativeEngine::setObjectOwnership(object, QDeclarativeEngine::JavaScriptOwnership); + QMetaObject::invokeMethod(object, "killMe"); + QVERIFY(object); + QTest::qWait(0); + QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); + QVERIFY(!object); +} + +/* + tests that id.toString() works +*/ +void tst_qdeclarativeecmascript::objectToString() +{ + QDeclarativeComponent component(&engine, TEST_FILE("declarativeToString.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "testToString"); + QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_")); + QVERIFY(object->stringProperty().endsWith(", \"objName\")")); +} + +/* +Tests bindings that indirectly cause their own deletion work. + +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")); + QObject *object = component.create(); + QVERIFY(object != 0); + object->setProperty("triggerDelete", true); + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("selfDeletingBinding.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + object->setProperty("triggerDelete", true); + } +} + +/* +Test that extended object properties can be accessed. + +This test a regression where this used to crash. The issue was specificially +for extended objects that did not include a synthesized meta object (so non-root +and no synthesiszed properties). +*/ +void tst_qdeclarativeecmascript::extendedObjectPropertyLookup() +{ + QDeclarativeComponent component(&engine, TEST_FILE("extendedObjectPropertyLookup.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); +} + +/* +Test file/lineNumbers for binding/Script errors. +*/ +void tst_qdeclarativeecmascript::scriptErrors() +{ + QDeclarativeComponent component(&engine, TEST_FILE("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\""; + + QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning6.toLatin1().constData()); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData()); + emit object->basicSignal(); + + QTest::ignoreMessage(QtWarningMsg, warning7.toLatin1().constData()); + emit object->anotherBasicSignal(); + + QTest::ignoreMessage(QtWarningMsg, warning8.toLatin1().constData()); + emit object->thirdBasicSignal(); +} + +/* +Test file/lineNumbers for inline functions. +*/ +void tst_qdeclarativeecmascript::functionErrors() +{ + QDeclarativeComponent component(&engine, TEST_FILE("functionErrors.qml")); + QString url = component.url().toString(); + + QString warning = url + ":5: Error: Invalid write to global property \"a\""; + + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + + QObject *object = component.create(); + QVERIFY(object != 0); + delete object; +} + +/* +Test various errors that can occur when assigning a property from script +*/ +void tst_qdeclarativeecmascript::propertyAssignmentErrors() +{ + QDeclarativeComponent component(&engine, TEST_FILE("propertyAssignmentErrors.qml")); + + QString url = component.url().toString(); + + QString warning1 = url + ":11:Error: Cannot assign [undefined] to int"; + QString warning2 = url + ":17:Error: Cannot assign QString to int"; + + QTest::ignoreMessage(QtDebugMsg, warning1.toLatin1().constData()); + QTest::ignoreMessage(QtDebugMsg, warning2.toLatin1().constData()); + + QObject *object = component.create(); + QVERIFY(object != 0); + + delete object; +} + +/* +Test bindings still work when the reeval is triggered from within +a signal script. +*/ +void tst_qdeclarativeecmascript::signalTriggeredBindings() +{ + QDeclarativeComponent component(&engine, TEST_FILE("signalTriggeredBindings.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("base").toReal(), 50.); + QCOMPARE(object->property("test1").toReal(), 50.); + QCOMPARE(object->property("test2").toReal(), 50.); + + object->basicSignal(); + + QCOMPARE(object->property("base").toReal(), 200.); + QCOMPARE(object->property("test1").toReal(), 200.); + QCOMPARE(object->property("test2").toReal(), 200.); + + object->argumentSignal(10, QString(), 10, MyQmlObject::EnumValue4, Qt::RightButton); + + QCOMPARE(object->property("base").toReal(), 400.); + QCOMPARE(object->property("test1").toReal(), 400.); + QCOMPARE(object->property("test2").toReal(), 400.); +} + +/* +Test that list properties can be iterated from ECMAScript +*/ +void tst_qdeclarativeecmascript::listProperties() +{ + QDeclarativeComponent component(&engine, TEST_FILE("listProperties.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toInt(), 21); + QCOMPARE(object->property("test2").toInt(), 2); + QCOMPARE(object->property("test3").toBool(), true); + QCOMPARE(object->property("test4").toBool(), true); +} + +void tst_qdeclarativeecmascript::exceptionClearsOnReeval() +{ + QDeclarativeComponent component(&engine, TEST_FILE("exceptionClearsOnReeval.qml")); + QString url = component.url().toString(); + + QString warning = url + ":4: TypeError: Result of expression 'objectProperty' [null] is not an object."; + + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), false); + + MyQmlObject object2; + MyQmlObject object3; + object2.setObjectProperty(&object3); + object->setObjectProperty(&object2); + + QCOMPARE(object->property("test").toBool(), true); +} + +void tst_qdeclarativeecmascript::exceptionSlotProducesWarning() +{ + QDeclarativeComponent component(&engine, TEST_FILE("exceptionProducesWarning.qml")); + QString url = component.url().toString(); + + QString warning = component.url().toString() + ":6: Error: JS exception"; + + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); +} + +void tst_qdeclarativeecmascript::exceptionBindingProducesWarning() +{ + QDeclarativeComponent component(&engine, TEST_FILE("exceptionProducesWarning2.qml")); + QString url = component.url().toString(); + + QString warning = component.url().toString() + ":5: Error: JS exception"; + + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); +} + +static int transientErrorsMsgCount = 0; +static void transientErrorsMsgHandler(QtMsgType, const char *) +{ + ++transientErrorsMsgCount; +} + +// Check that transient binding errors are not displayed +void tst_qdeclarativeecmascript::transientErrors() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("transientErrors.qml")); + + transientErrorsMsgCount = 0; + QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); + + QObject *object = component.create(); + QVERIFY(object != 0); + + qInstallMsgHandler(old); + + QCOMPARE(transientErrorsMsgCount, 0); + } + + // One binding erroring multiple times, but then resolving + { + QDeclarativeComponent component(&engine, TEST_FILE("transientErrors.2.qml")); + + transientErrorsMsgCount = 0; + QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); + + QObject *object = component.create(); + QVERIFY(object != 0); + + qInstallMsgHandler(old); + + QCOMPARE(transientErrorsMsgCount, 0); + } +} + +// Check that errors during shutdown are minimized +void tst_qdeclarativeecmascript::shutdownErrors() +{ + QDeclarativeComponent component(&engine, TEST_FILE("shutdownErrors.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + transientErrorsMsgCount = 0; + QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); + + delete object; + + qInstallMsgHandler(old); + QCOMPARE(transientErrorsMsgCount, 0); +} + +void tst_qdeclarativeecmascript::compositePropertyType() +{ + QDeclarativeComponent component(&engine, TEST_FILE("compositePropertyType.qml")); + QTest::ignoreMessage(QtDebugMsg, "hello world"); + QObject *object = qobject_cast<QObject *>(component.create()); + delete object; +} + +// QTBUG-5759 +void tst_qdeclarativeecmascript::jsObject() +{ + QDeclarativeComponent component(&engine, TEST_FILE("jsObject.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 92); + + delete object; +} + +void tst_qdeclarativeecmascript::undefinedResetsProperty() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("undefinedResetsProperty.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("resettableProperty").toInt(), 92); + + object->setProperty("setUndefined", true); + + QCOMPARE(object->property("resettableProperty").toInt(), 13); + + object->setProperty("setUndefined", false); + + QCOMPARE(object->property("resettableProperty").toInt(), 92); + + delete object; + } + { + QDeclarativeComponent component(&engine, TEST_FILE("undefinedResetsProperty.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("resettableProperty").toInt(), 19); + + QMetaObject::invokeMethod(object, "doReset"); + + QCOMPARE(object->property("resettableProperty").toInt(), 13); + + delete object; + } +} + +// QTBUG-6781 +void tst_qdeclarativeecmascript::bug1() +{ + QDeclarativeComponent component(&engine, TEST_FILE("bug.1.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 14); + + object->setProperty("a", 11); + + QCOMPARE(object->property("test").toInt(), 3); + + object->setProperty("b", true); + + QCOMPARE(object->property("test").toInt(), 9); + + delete object; +} + +void tst_qdeclarativeecmascript::bug2() +{ + QDeclarativeComponent component(&engine); + component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl()); + + QObject *object = component.create(); + QVERIFY(object != 0); + + delete object; +} + +// Don't crash in createObject when the component has errors. +void tst_qdeclarativeecmascript::dynamicCreationCrash() +{ + QDeclarativeComponent component(&engine, TEST_FILE("dynamicCreation.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + + QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready"); + QMetaObject::invokeMethod(object, "dontCrash"); + QObject *created = object->objectProperty(); + QVERIFY(created == 0); +} + +//QTBUG-9367 +void tst_qdeclarativeecmascript::regExpBug() +{ + QDeclarativeComponent component(&engine, TEST_FILE("regExp.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]")); +} + +void tst_qdeclarativeecmascript::callQtInvokables() +{ + MyInvokableObject o; + + QDeclarativeEngine qmlengine; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&qmlengine); + QScriptEngine *engine = &ep->scriptEngine; + + QStringList names; QList<QScriptValue> values; + names << QLatin1String("object"); values << ep->objectClass->newQObject(&o); + names << QLatin1String("undefined"); values << engine->undefinedValue(); + + ep->globalClass->explicitSetProperty(names, values); + + // Non-existent methods + o.reset(); + QCOMPARE(engine->evaluate("object.method_nonexistent()").isError(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), -1); + QCOMPARE(o.actuals().count(), 0); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_nonexistent(10, 11)").isError(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), -1); + QCOMPARE(o.actuals().count(), 0); + + // Insufficient arguments + o.reset(); + QCOMPARE(engine->evaluate("object.method_int()").isError(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), -1); + QCOMPARE(o.actuals().count(), 0); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_intint(10)").isError(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), -1); + QCOMPARE(o.actuals().count(), 0); + + // Excessive arguments + o.reset(); + QCOMPARE(engine->evaluate("object.method_int(10, 11)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 8); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(10)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_intint(10, 11, 12)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 9); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(10)); + QCOMPARE(o.actuals().at(1), QVariant(11)); + + // Test return types + o.reset(); + QCOMPARE(engine->evaluate("object.method_NoArgs()").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 0); + QCOMPARE(o.actuals().count(), 0); + + o.reset(); + QVERIFY(engine->evaluate("object.method_NoArgs_int()").strictlyEquals(QScriptValue(engine, 6))); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 1); + QCOMPARE(o.actuals().count(), 0); + + o.reset(); + QVERIFY(engine->evaluate("object.method_NoArgs_real()").strictlyEquals(QScriptValue(engine, 19.75))); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 2); + QCOMPARE(o.actuals().count(), 0); + + o.reset(); + { + QScriptValue ret = engine->evaluate("object.method_NoArgs_QPointF()"); + QCOMPARE(ret.toVariant(), QVariant(QPointF(123, 4.5))); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 3); + QCOMPARE(o.actuals().count(), 0); + } + + o.reset(); + { + QScriptValue ret = engine->evaluate("object.method_NoArgs_QObject()"); + QVERIFY(ret.isQObject()); + QCOMPARE(ret.toQObject(), (QObject *)&o); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 4); + QCOMPARE(o.actuals().count(), 0); + } + + o.reset(); + QCOMPARE(engine->evaluate("object.method_NoArgs_unknown()").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 5); + QCOMPARE(o.actuals().count(), 0); + + o.reset(); + { + QScriptValue ret = engine->evaluate("object.method_NoArgs_QScriptValue()"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString("Hello world")); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 6); + QCOMPARE(o.actuals().count(), 0); + } + + o.reset(); + QVERIFY(engine->evaluate("object.method_NoArgs_QVariant()").strictlyEquals(QScriptValue(engine, "QML rocks"))); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 7); + QCOMPARE(o.actuals().count(), 0); + + // Test arg types + o.reset(); + QCOMPARE(engine->evaluate("object.method_int(94)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 8); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(94)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_int(\"94\")").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 8); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(94)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_int(\"not a number\")").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 8); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(0)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_int(null)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 8); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(0)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_int(undefined)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 8); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(0)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_int(object)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 8); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(0)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_intint(122, 9)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 9); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(122)); + QCOMPARE(o.actuals().at(1), QVariant(9)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_real(94.3)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 10); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(94.3)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_real(\"94.3\")").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 10); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(94.3)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_real(\"not a number\")").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 10); + QCOMPARE(o.actuals().count(), 1); + QVERIFY(qIsNaN(o.actuals().at(0).toDouble())); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_real(null)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 10); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(0)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_real(undefined)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 10); + QCOMPARE(o.actuals().count(), 1); + QVERIFY(qIsNaN(o.actuals().at(0).toDouble())); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_real(object)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 10); + QCOMPARE(o.actuals().count(), 1); + QVERIFY(qIsNaN(o.actuals().at(0).toDouble())); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QString(\"Hello world\")").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 11); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant("Hello world")); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QString(19)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 11); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant("19")); + + o.reset(); + { + QString expected = "MyInvokableObject(0x" + QString::number((quintptr)&o, 16) + ")"; + QCOMPARE(engine->evaluate("object.method_QString(object)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 11); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(expected)); + } + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QString(null)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 11); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QString())); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QString(undefined)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 11); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QString())); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QPointF(0)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 12); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QPointF())); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QPointF(null)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 12); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QPointF())); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QPointF(undefined)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 12); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QPointF())); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QPointF(object)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 12); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QPointF())); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QPointF(object.method_get_QPointF())").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 12); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QPointF(99.3, -10.2))); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QPointF(object.method_get_QPoint())").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 12); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QPointF(9, 12))); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QObject(0)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 13); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QObject(\"Hello world\")").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 13); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QObject(null)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 13); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QObject(undefined)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 13); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QObject(object)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 13); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)&o)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QScriptValue(null)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 14); + QCOMPARE(o.actuals().count(), 1); + QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(0)).isNull()); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QScriptValue(undefined)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 14); + QCOMPARE(o.actuals().count(), 1); + QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(0)).isUndefined()); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QScriptValue(19)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 14); + QCOMPARE(o.actuals().count(), 1); + QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(0)).strictlyEquals(QScriptValue(engine, 19))); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QScriptValue([19, 20])").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 14); + QCOMPARE(o.actuals().count(), 1); + QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(0)).isArray()); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_intQScriptValue(4, null)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 15); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(4)); + QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(1)).isNull()); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_intQScriptValue(8, undefined)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 15); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(8)); + QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(1)).isUndefined()); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_intQScriptValue(3, 19)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 15); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(3)); + QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(1)).strictlyEquals(QScriptValue(engine, 19))); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_intQScriptValue(44, [19, 20])").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 15); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(44)); + QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(1)).isArray()); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_overload()").isError(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), -1); + QCOMPARE(o.actuals().count(), 0); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_overload(10)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 16); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(10)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_overload(10, 11)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 17); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(10)); + QCOMPARE(o.actuals().at(1), QVariant(11)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_overload(\"Hello\")").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 18); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QString("Hello"))); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_with_enum(9)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 19); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(9)); + + o.reset(); + QVERIFY(engine->evaluate("object.method_default(10)").strictlyEquals(QScriptValue(19))); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 20); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(10)); + QCOMPARE(o.actuals().at(1), QVariant(19)); + + o.reset(); + QVERIFY(engine->evaluate("object.method_default(10, 13)").strictlyEquals(QScriptValue(13))); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 20); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(10)); + QCOMPARE(o.actuals().at(1), QVariant(13)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_inherited(9)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), -3); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(9)); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QVariant(9)").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 21); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(9)); + QCOMPARE(o.actuals().at(1), QVariant()); + + o.reset(); + QCOMPARE(engine->evaluate("object.method_QVariant(\"Hello\", \"World\")").isUndefined(), true); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 21); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(QString("Hello"))); + QCOMPARE(o.actuals().at(1), QVariant(QString("World"))); +} + +// QTBUG-13047 (check that you can pass registered object types as args) +void tst_qdeclarativeecmascript::invokableObjectArg() +{ + QDeclarativeComponent component(&engine, TEST_FILE("invokableObjectArg.qml")); + + QObject *o = component.create(); + QVERIFY(o); + MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o); + QVERIFY(qmlobject); + QCOMPARE(qmlobject->myinvokableObject, qmlobject); + + delete o; +} + +// QTBUG-13047 (check that you can return registered object types from methods) +void tst_qdeclarativeecmascript::invokableObjectRet() +{ + QDeclarativeComponent component(&engine, TEST_FILE("invokableObjectRet.qml")); + + QObject *o = component.create(); + QVERIFY(o); + QCOMPARE(o->property("test").toBool(), true); + delete o; +} + +// QTBUG-5675 +void tst_qdeclarativeecmascript::listToVariant() +{ + QDeclarativeComponent component(&engine, TEST_FILE("listToVariant.qml")); + + MyQmlContainer container; + + QDeclarativeContext context(engine.rootContext()); + context.setContextObject(&container); + + QObject *object = component.create(&context); + QVERIFY(object != 0); + + QVariant v = object->property("test"); + QCOMPARE(v.userType(), qMetaTypeId<QDeclarativeListReference>()); + QVERIFY(qvariant_cast<QDeclarativeListReference>(v).object() == &container); + + delete object; +} + +// QTBUG-7957 +void tst_qdeclarativeecmascript::multiEngineObject() +{ + MyQmlObject obj; + obj.setStringProperty("Howdy planet"); + + QDeclarativeEngine e1; + e1.rootContext()->setContextProperty("thing", &obj); + QDeclarativeComponent c1(&e1, TEST_FILE("multiEngineObject.qml")); + + QDeclarativeEngine e2; + e2.rootContext()->setContextProperty("thing", &obj); + QDeclarativeComponent c2(&e2, TEST_FILE("multiEngineObject.qml")); + + QObject *o1 = c1.create(); + QObject *o2 = c2.create(); + + QCOMPARE(o1->property("test").toString(), QString("Howdy planet")); + QCOMPARE(o2->property("test").toString(), QString("Howdy planet")); + + delete o2; + delete o1; +} + +// Test that references to QObjects are cleanup when the object is destroyed +void tst_qdeclarativeecmascript::deletedObject() +{ + QDeclarativeComponent component(&engine, TEST_FILE("deletedObject.qml")); + + QObject *object = component.create(); + + QCOMPARE(object->property("test1").toBool(), true); + QCOMPARE(object->property("test2").toBool(), true); + QCOMPARE(object->property("test3").toBool(), true); + QCOMPARE(object->property("test4").toBool(), true); + + delete object; +} + +void tst_qdeclarativeecmascript::attachedPropertyScope() +{ + QDeclarativeComponent component(&engine, TEST_FILE("attachedPropertyScope.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + MyQmlAttachedObject *attached = + qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object)); + QVERIFY(attached != 0); + + QCOMPARE(object->property("value2").toInt(), 0); + + attached->emitMySignal(); + + QCOMPARE(object->property("value2").toInt(), 9); + + delete object; +} + +void tst_qdeclarativeecmascript::scriptConnect() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.1.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), false); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toBool(), true); + + delete object; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.2.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), false); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toBool(), true); + + delete object; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.3.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), false); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toBool(), true); + + delete object; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.4.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->methodCalled(), false); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->methodCalled(), true); + + delete object; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.5.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->methodCalled(), false); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->methodCalled(), true); + + delete object; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("scriptConnect.6.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 0); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 2); + + delete object; + } +} + +void tst_qdeclarativeecmascript::scriptDisconnect() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.1.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 0); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 1); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 2); + emit object->basicSignal(); + QCOMPARE(object->property("test").toInt(), 2); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 2); + + delete object; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.2.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 0); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 1); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 2); + emit object->basicSignal(); + QCOMPARE(object->property("test").toInt(), 2); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 2); + + delete object; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.3.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 0); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 1); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 2); + emit object->basicSignal(); + QCOMPARE(object->property("test").toInt(), 2); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 3); + + delete object; + } + { + QDeclarativeComponent component(&engine, TEST_FILE("scriptDisconnect.4.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 0); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 1); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 2); + emit object->basicSignal(); + QCOMPARE(object->property("test").toInt(), 2); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 3); + + delete object; + } +} + +class OwnershipObject : public QObject +{ + Q_OBJECT +public: + OwnershipObject() { object = new QObject; } + + QPointer<QObject> object; + +public slots: + QObject *getObject() { return object; } +}; + +void tst_qdeclarativeecmascript::ownership() +{ + OwnershipObject own; + QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext()); + context->setContextObject(&own); + + { + QDeclarativeComponent component(&engine, TEST_FILE("ownership.qml")); + + QVERIFY(own.object != 0); + + QObject *object = component.create(context); + QDeclarativeEnginePrivate::getScriptEngine(&engine)->collectGarbage(); + + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + + QVERIFY(own.object == 0); + + delete object; + } + + own.object = new QObject(&own); + + { + QDeclarativeComponent component(&engine, TEST_FILE("ownership.qml")); + + QVERIFY(own.object != 0); + + QObject *object = component.create(context); + QDeclarativeEnginePrivate::getScriptEngine(&engine)->collectGarbage(); + + QCoreApplication::processEvents(QEventLoop::DeferredDeletion); + + QVERIFY(own.object != 0); + + delete object; + } +} + +class CppOwnershipReturnValue : public QObject +{ + Q_OBJECT +public: + CppOwnershipReturnValue() : value(0) {} + ~CppOwnershipReturnValue() { delete value; } + + Q_INVOKABLE QObject *create() { + value = new QObject; + QDeclarativeEngine::setObjectOwnership(value, QDeclarativeEngine::CppOwnership); + return value; + } + + Q_INVOKABLE MyQmlObject *createQmlObject() { + MyQmlObject *rv = new MyQmlObject; + value = rv; + return rv; + } + + QPointer<QObject> value; +}; + +// QTBUG-15695. +// Test setObjectOwnership(CppOwnership) works even when there is no QDeclarativeData +void tst_qdeclarativeecmascript::cppOwnershipReturnValue() +{ + CppOwnershipReturnValue source; + + { + QDeclarativeEngine engine; + engine.rootContext()->setContextProperty("source", &source); + + QVERIFY(source.value == 0); + + QDeclarativeComponent component(&engine); + component.setData("import QtQuick 1.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl()); + + QObject *object = component.create(); + + QVERIFY(object != 0); + QVERIFY(source.value != 0); + + delete object; + } + + QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); + + QVERIFY(source.value != 0); +} + +// QTBUG-15697 +void tst_qdeclarativeecmascript::ownershipCustomReturnValue() +{ + CppOwnershipReturnValue source; + + { + QDeclarativeEngine engine; + engine.rootContext()->setContextProperty("source", &source); + + QVERIFY(source.value == 0); + + QDeclarativeComponent component(&engine); + component.setData("import QtQuick 1.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl()); + + QObject *object = component.create(); + + QVERIFY(object != 0); + QVERIFY(source.value != 0); + + delete object; + } + + QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); + + QVERIFY(source.value == 0); +} + +class QListQObjectMethodsObject : public QObject +{ + Q_OBJECT +public: + QListQObjectMethodsObject() { + m_objects.append(new MyQmlObject()); + m_objects.append(new MyQmlObject()); + } + + ~QListQObjectMethodsObject() { + qDeleteAll(m_objects); + } + +public slots: + QList<QObject *> getObjects() { return m_objects; } + +private: + QList<QObject *> m_objects; +}; + +// Tests that returning a QList<QObject*> from a method works +void tst_qdeclarativeecmascript::qlistqobjectMethods() +{ + QListQObjectMethodsObject obj; + QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext()); + context->setContextObject(&obj); + + QDeclarativeComponent component(&engine, TEST_FILE("qlistqobjectMethods.qml")); + + QObject *object = component.create(context); + + QCOMPARE(object->property("test").toInt(), 2); + QCOMPARE(object->property("test2").toBool(), true); + + delete object; +} + +// QTBUG-9205 +void tst_qdeclarativeecmascript::strictlyEquals() +{ + QDeclarativeComponent component(&engine, TEST_FILE("strictlyEquals.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toBool(), true); + QCOMPARE(object->property("test2").toBool(), true); + QCOMPARE(object->property("test3").toBool(), true); + QCOMPARE(object->property("test4").toBool(), true); + QCOMPARE(object->property("test5").toBool(), true); + QCOMPARE(object->property("test6").toBool(), true); + QCOMPARE(object->property("test7").toBool(), true); + QCOMPARE(object->property("test8").toBool(), true); + + delete object; +} + +void tst_qdeclarativeecmascript::compiled() +{ + QDeclarativeComponent component(&engine, TEST_FILE("compiled.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toReal(), qreal(15.7)); + QCOMPARE(object->property("test2").toReal(), qreal(-6.7)); + QCOMPARE(object->property("test3").toBool(), true); + QCOMPARE(object->property("test4").toBool(), false); + QCOMPARE(object->property("test5").toBool(), false); + QCOMPARE(object->property("test6").toBool(), true); + + QCOMPARE(object->property("test7").toInt(), 185); + QCOMPARE(object->property("test8").toInt(), 167); + QCOMPARE(object->property("test9").toBool(), true); + QCOMPARE(object->property("test10").toBool(), false); + QCOMPARE(object->property("test11").toBool(), false); + QCOMPARE(object->property("test12").toBool(), true); + + QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld")); + QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World")); + QCOMPARE(object->property("test15").toBool(), false); + QCOMPARE(object->property("test16").toBool(), true); + + QCOMPARE(object->property("test17").toInt(), 5); + QCOMPARE(object->property("test18").toReal(), qreal(176)); + QCOMPARE(object->property("test19").toInt(), 7); + QCOMPARE(object->property("test20").toReal(), qreal(6.7)); + QCOMPARE(object->property("test21").toString(), QLatin1String("6.7")); + QCOMPARE(object->property("test22").toString(), QLatin1String("!")); + QCOMPARE(object->property("test23").toBool(), true); + QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33)); + QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA)); + + delete object; +} + +// Test that numbers assigned in bindings as strings work consistently +void tst_qdeclarativeecmascript::numberAssignment() +{ + QDeclarativeComponent component(&engine, TEST_FILE("numberAssignment.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1"), QVariant((qreal)6.7)); + QCOMPARE(object->property("test2"), QVariant((qreal)6.7)); + QCOMPARE(object->property("test2"), QVariant((qreal)6.7)); + QCOMPARE(object->property("test3"), QVariant((qreal)6)); + QCOMPARE(object->property("test4"), QVariant((qreal)6)); + + QCOMPARE(object->property("test5"), QVariant((int)7)); + QCOMPARE(object->property("test6"), QVariant((int)7)); + QCOMPARE(object->property("test7"), QVariant((int)6)); + QCOMPARE(object->property("test8"), QVariant((int)6)); + + QCOMPARE(object->property("test9"), QVariant((unsigned int)7)); + QCOMPARE(object->property("test10"), QVariant((unsigned int)7)); + QCOMPARE(object->property("test11"), QVariant((unsigned int)6)); + QCOMPARE(object->property("test12"), QVariant((unsigned int)6)); + + delete object; +} + +void tst_qdeclarativeecmascript::propertySplicing() +{ + QDeclarativeComponent component(&engine, TEST_FILE("propertySplicing.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +// QTBUG-16683 +void tst_qdeclarativeecmascript::signalWithUnknownTypes() +{ + QDeclarativeComponent component(&engine, TEST_FILE("signalWithUnknownTypes.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + MyQmlObject::MyType type; + type.value = 0x8971123; + emit object->signalWithUnknownType(type); + + MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant()); + + QCOMPARE(result.value, type.value); + + + delete object; +} + +// Test that assigning a null object works +// Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4 +void tst_qdeclarativeecmascript::nullObjectBinding() +{ + QDeclarativeComponent component(&engine, TEST_FILE("nullObjectBinding.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0)); + + delete object; +} + +// Test that bindings don't evaluate once the engine has been destroyed +void tst_qdeclarativeecmascript::deletedEngine() +{ + QDeclarativeEngine *engine = new QDeclarativeEngine; + QDeclarativeComponent component(engine, TEST_FILE("deletedEngine.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("a").toInt(), 39); + object->setProperty("b", QVariant(9)); + QCOMPARE(object->property("a").toInt(), 117); + + delete engine; + + QCOMPARE(object->property("a").toInt(), 117); + object->setProperty("b", QVariant(10)); + QCOMPARE(object->property("a").toInt(), 117); + + delete object; +} + +// Test the crashing part of QTBUG-9705 +void tst_qdeclarativeecmascript::libraryScriptAssert() +{ + QDeclarativeComponent component(&engine, TEST_FILE("libraryScriptAssert.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + delete object; +} + +void tst_qdeclarativeecmascript::variantsAssignedUndefined() +{ + QDeclarativeComponent component(&engine, TEST_FILE("variantsAssignedUndefined.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toInt(), 10); + QCOMPARE(object->property("test2").toInt(), 11); + + object->setProperty("runTest", true); + + QCOMPARE(object->property("test1"), QVariant()); + QCOMPARE(object->property("test2"), QVariant()); + + + delete object; +} + +void tst_qdeclarativeecmascript::qtbug_9792() +{ + QDeclarativeComponent component(&engine, TEST_FILE("qtbug_9792.qml")); + + QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext()); + + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context)); + QVERIFY(object != 0); + + QTest::ignoreMessage(QtDebugMsg, "Hello world!"); + object->basicSignal(); + + delete context; + + transientErrorsMsgCount = 0; + QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); + + object->basicSignal(); + + qInstallMsgHandler(old); + + QCOMPARE(transientErrorsMsgCount, 0); + + delete object; +} + +// Verifies that QDeclarativeGuard<>s used in the vmemetaobject are cleaned correctly +void tst_qdeclarativeecmascript::qtcreatorbug_1289() +{ + QDeclarativeComponent component(&engine, TEST_FILE("qtcreatorbug_1289.qml")); + + QObject *o = component.create(); + QVERIFY(o != 0); + + QObject *nested = qvariant_cast<QObject *>(o->property("object")); + QVERIFY(nested != 0); + + QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o); + + delete nested; + nested = qvariant_cast<QObject *>(o->property("object")); + QVERIFY(nested == 0); + + // If the bug is present, the next line will crash + delete o; +} + +// Test that we shut down without stupid warnings +void tst_qdeclarativeecmascript::noSpuriousWarningsAtShutdown() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("noSpuriousWarningsAtShutdown.qml")); + + QObject *o = component.create(); + + transientErrorsMsgCount = 0; + QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); + + delete o; + + qInstallMsgHandler(old); + + QCOMPARE(transientErrorsMsgCount, 0); + } + + + { + QDeclarativeComponent component(&engine, TEST_FILE("noSpuriousWarningsAtShutdown.2.qml")); + + QObject *o = component.create(); + + transientErrorsMsgCount = 0; + QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); + + delete o; + + qInstallMsgHandler(old); + + QCOMPARE(transientErrorsMsgCount, 0); + } +} + +void tst_qdeclarativeecmascript::canAssignNullToQObject() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("canAssignNullToQObject.1.qml")); + + MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(o != 0); + + QVERIFY(o->objectProperty() != 0); + + o->setProperty("runTest", true); + + QVERIFY(o->objectProperty() == 0); + + delete o; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("canAssignNullToQObject.2.qml")); + + MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(o != 0); + + QVERIFY(o->objectProperty() == 0); + + delete o; + } +} + +void tst_qdeclarativeecmascript::functionAssignment_fromBinding() +{ + QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.1.qml")); + + QString url = component.url().toString(); + QString warning = url + ":4: Unable to assign a function to a property."; + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + + MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(o != 0); + + QVERIFY(!o->property("a").isValid()); + + delete o; +} + +void tst_qdeclarativeecmascript::functionAssignment_fromJS() +{ + QFETCH(QString, triggerProperty); + + QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.2.qml")); + QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString())); + + MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(o != 0); + QVERIFY(!o->property("a").isValid()); + + o->setProperty("aNumber", QVariant(5)); + o->setProperty(triggerProperty.toUtf8().constData(), true); + QCOMPARE(o->property("a"), QVariant(50)); + + o->setProperty("aNumber", QVariant(10)); + QCOMPARE(o->property("a"), QVariant(100)); + + delete o; +} + +void tst_qdeclarativeecmascript::functionAssignment_fromJS_data() +{ + QTest::addColumn<QString>("triggerProperty"); + + QTest::newRow("assign to property") << "assignToProperty"; + QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile"; + + QTest::newRow("assign to value type") << "assignToValueType"; + + QTest::newRow("use 'this'") << "assignWithThis"; + QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile"; +} + +void tst_qdeclarativeecmascript::functionAssignmentfromJS_invalid() +{ + QDeclarativeComponent component(&engine, TEST_FILE("functionAssignment.2.qml")); + QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString())); + + MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(o != 0); + QVERIFY(!o->property("a").isValid()); + + o->setProperty("assignFuncWithoutReturn", true); + QVERIFY(!o->property("a").isValid()); + + QString url = component.url().toString(); + QString warning = url + ":63: Unable to assign QString to int"; + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + o->setProperty("assignWrongType", true); + + warning = url + ":70: Unable to assign QString to int"; + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + o->setProperty("assignWrongTypeToValueType", true); + + delete o; +} + +void tst_qdeclarativeecmascript::eval() +{ + QDeclarativeComponent component(&engine, TEST_FILE("eval.qml")); + + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + QCOMPARE(o->property("test4").toBool(), true); + QCOMPARE(o->property("test5").toBool(), true); + + delete o; +} + +void tst_qdeclarativeecmascript::function() +{ + QDeclarativeComponent component(&engine, TEST_FILE("function.qml")); + + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + + delete o; +} + +// Test the "Qt.include" method +void tst_qdeclarativeecmascript::include() +{ + // Non-library relative include + { + QDeclarativeComponent component(&engine, TEST_FILE("include.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test0").toInt(), 99); + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test2_1").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + QCOMPARE(o->property("test3_1").toBool(), true); + + delete o; + } + + // Library relative include + { + QDeclarativeComponent component(&engine, TEST_FILE("include_shared.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test0").toInt(), 99); + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test2_1").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + QCOMPARE(o->property("test3_1").toBool(), true); + + delete o; + } + + // Callback + { + QDeclarativeComponent component(&engine, TEST_FILE("include_callback.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + QCOMPARE(o->property("test4").toBool(), true); + QCOMPARE(o->property("test5").toBool(), true); + QCOMPARE(o->property("test6").toBool(), true); + + delete o; + } + + // Including file with ".pragma library" + { + QDeclarativeComponent component(&engine, TEST_FILE("include_pragma.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test1").toInt(), 100); + + delete o; + } + + // Remote - success + { + TestHTTPServer server(8111); + QVERIFY(server.isValid()); + server.serveDirectory(SRCDIR "/data"); + + QDeclarativeComponent component(&engine, TEST_FILE("include_remote.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QTRY_VERIFY(o->property("done").toBool() == true); + QTRY_VERIFY(o->property("done2").toBool() == true); + + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + QCOMPARE(o->property("test4").toBool(), true); + QCOMPARE(o->property("test5").toBool(), true); + + QCOMPARE(o->property("test6").toBool(), true); + QCOMPARE(o->property("test7").toBool(), true); + QCOMPARE(o->property("test8").toBool(), true); + QCOMPARE(o->property("test9").toBool(), true); + QCOMPARE(o->property("test10").toBool(), true); + + delete o; + } + + // Remote - error + { + TestHTTPServer server(8111); + QVERIFY(server.isValid()); + server.serveDirectory(SRCDIR "/data"); + + QDeclarativeComponent component(&engine, TEST_FILE("include_remote_missing.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QTRY_VERIFY(o->property("done").toBool() == true); + + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + + delete o; + } +} + +void tst_qdeclarativeecmascript::qtbug_10696() +{ + QDeclarativeComponent component(&engine, TEST_FILE("qtbug_10696.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + delete o; +} + +void tst_qdeclarativeecmascript::qtbug_11606() +{ + QDeclarativeComponent component(&engine, TEST_FILE("qtbug_11606.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test").toBool(), true); + delete o; +} + +void tst_qdeclarativeecmascript::qtbug_11600() +{ + QDeclarativeComponent component(&engine, TEST_FILE("qtbug_11600.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test").toBool(), true); + delete o; +} + +// Reading and writing non-scriptable properties should fail +void tst_qdeclarativeecmascript::nonscriptable() +{ + QDeclarativeComponent component(&engine, TEST_FILE("nonscriptable.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("readOk").toBool(), true); + QCOMPARE(o->property("writeOk").toBool(), true); + delete o; +} + +// deleteLater() should not be callable from QML +void tst_qdeclarativeecmascript::deleteLater() +{ + QDeclarativeComponent component(&engine, TEST_FILE("deleteLater.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test").toBool(), true); + delete o; +} + +void tst_qdeclarativeecmascript::in() +{ + QDeclarativeComponent component(&engine, TEST_FILE("in.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + delete o; +} + +void tst_qdeclarativeecmascript::sharedAttachedObject() +{ + QDeclarativeComponent component(&engine, TEST_FILE("sharedAttachedObject.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + delete o; +} + +// QTBUG-13999 +void tst_qdeclarativeecmascript::objectName() +{ + QDeclarativeComponent component(&engine, TEST_FILE("objectName.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test1").toString(), QString("hello")); + QCOMPARE(o->property("test2").toString(), QString("ell")); + + o->setObjectName("world"); + + QCOMPARE(o->property("test1").toString(), QString("world")); + QCOMPARE(o->property("test2").toString(), QString("orl")); + + delete o; +} + +void tst_qdeclarativeecmascript::writeRemovesBinding() +{ + QDeclarativeComponent component(&engine, TEST_FILE("writeRemovesBinding.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; +} + +// Test bindings assigned to alias properties actually assign to the alias' target +void tst_qdeclarativeecmascript::aliasBindingsAssignCorrectly() +{ + QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsAssignCorrectly.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; +} + +// Test bindings assigned to alias properties override a binding on the target (QTBUG-13719) +void tst_qdeclarativeecmascript::aliasBindingsOverrideTarget() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.2.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.3.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } +} + +// Test that writes to alias properties override bindings on the alias target (QTBUG-13719) +void tst_qdeclarativeecmascript::aliasWritesOverrideBindings() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.2.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.3.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } +} + +void tst_qdeclarativeecmascript::revisionErrors() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors.qml")); + QString url = component.url().toString(); + + QString warning1 = url + ":8: ReferenceError: Can't find variable: prop2"; + QString warning2 = url + ":11: ReferenceError: Can't find variable: prop2"; + QString warning3 = url + ":13: ReferenceError: Can't find variable: method2"; + + QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData()); + MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); + QVERIFY(object != 0); + } + { + QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors2.qml")); + QString url = component.url().toString(); + + // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0 + // method2, prop2 from MyRevisionedClass not available + // method4, prop4 from MyRevisionedSubclass not available + QString warning1 = url + ":8: ReferenceError: Can't find variable: prop2"; + QString warning2 = url + ":14: ReferenceError: Can't find variable: prop2"; + QString warning3 = url + ":10: ReferenceError: Can't find variable: prop4"; + QString warning4 = url + ":16: ReferenceError: Can't find variable: prop4"; + QString warning5 = url + ":20: ReferenceError: Can't find variable: method2"; + + QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData()); + MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); + QVERIFY(object != 0); + } + { + QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevisionErrors3.qml")); + QString url = component.url().toString(); + + // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1 + // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1 + QString warning1 = url + ":30: ReferenceError: Can't find variable: methodD"; + QString warning2 = url + ":10: ReferenceError: Can't find variable: propD"; + QString warning3 = url + ":20: ReferenceError: Can't find variable: propD"; + QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData()); + MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); + QVERIFY(object != 0); + } +} + +void tst_qdeclarativeecmascript::revision() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision.qml")); + QString url = component.url().toString(); + + MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); + QVERIFY(object != 0); + } + { + QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision2.qml")); + QString url = component.url().toString(); + + MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); + QVERIFY(object != 0); + } + { + QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision3.qml")); + QString url = component.url().toString(); + + MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); + QVERIFY(object != 0); + } + // Test that non-root classes can resolve revisioned methods + { + QDeclarativeComponent component(&engine, TEST_FILE("metaobjectRevision4.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("test").toReal(), 11.); + delete object; + } +} + +// Test for QScriptDeclarativeClass::pushCleanContext() +void tst_qdeclarativeecmascript::pushCleanContext() +{ + QScriptEngine engine; + engine.globalObject().setProperty("a", 6); + QCOMPARE(engine.evaluate("a").toInt32(), 6); + + // First confirm pushContext() behaves as we expect + QScriptValue object = engine.newObject(); + object.setProperty("a", 15); + QScriptContext *context1 = engine.pushContext(); + context1->pushScope(object); + QCOMPARE(engine.evaluate("a").toInt32(), 15); + + QScriptContext *context2 = engine.pushContext(); + Q_UNUSED(context2); + QCOMPARE(engine.evaluate("a").toInt32(), 15); + QScriptValue func1 = engine.evaluate("(function() { return a; })"); + + // Now check that pushCleanContext() works + QScriptDeclarativeClass::pushCleanContext(&engine); + QCOMPARE(engine.evaluate("a").toInt32(), 6); + QScriptValue func2 = engine.evaluate("(function() { return a; })"); + + engine.popContext(); + QCOMPARE(engine.evaluate("a").toInt32(), 15); + + engine.popContext(); + QCOMPARE(engine.evaluate("a").toInt32(), 15); + + engine.popContext(); + QCOMPARE(engine.evaluate("a").toInt32(), 6); + + // Check that function objects created in these contexts work + QCOMPARE(func1.call().toInt32(), 15); + QCOMPARE(func2.call().toInt32(), 6); +} + +void tst_qdeclarativeecmascript::realToInt() +{ + QDeclarativeComponent component(&engine, TEST_FILE("realToInt.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + + QMetaObject::invokeMethod(object, "test1"); + QCOMPARE(object->value(), int(4)); + QMetaObject::invokeMethod(object, "test2"); + QCOMPARE(object->value(), int(8)); +} + +void tst_qdeclarativeecmascript::qtbug_20648() +{ + QDeclarativeComponent component(&engine, TEST_FILE("qtbug_20648.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test").toInt(), 100); + delete o; +} + +QTEST_MAIN(tst_qdeclarativeecmascript) + +#include "tst_qdeclarativeecmascript.moc" |