From c621a6fa084fba973b88542e56142eb4d52d2b62 Mon Sep 17 00:00:00 2001 From: Ivailo Monev Date: Tue, 23 Nov 2021 17:46:36 +0200 Subject: [PATCH] add QDeclarativeProperty test Signed-off-by: Ivailo Monev --- .../auto/qdeclarativeproperty/CMakeLists.txt | 5 + .../qdeclarativeproperty/data/TestType.qml | 6 + .../data/aliasPropertyBindings.qml | 19 + .../data/readSynthesizedObject.qml | 9 + .../tst_qdeclarativeproperty.cpp | 1466 +++++++++++++++++ 5 files changed, 1505 insertions(+) create mode 100644 tests/auto/qdeclarativeproperty/CMakeLists.txt create mode 100644 tests/auto/qdeclarativeproperty/data/TestType.qml create mode 100644 tests/auto/qdeclarativeproperty/data/aliasPropertyBindings.qml create mode 100644 tests/auto/qdeclarativeproperty/data/readSynthesizedObject.qml create mode 100644 tests/auto/qdeclarativeproperty/tst_qdeclarativeproperty.cpp diff --git a/tests/auto/qdeclarativeproperty/CMakeLists.txt b/tests/auto/qdeclarativeproperty/CMakeLists.txt new file mode 100644 index 000000000..be71feb32 --- /dev/null +++ b/tests/auto/qdeclarativeproperty/CMakeLists.txt @@ -0,0 +1,5 @@ +katie_gui_test(tst_qdeclarativeproperty + ${CMAKE_CURRENT_SOURCE_DIR}/tst_qdeclarativeproperty.cpp +) + +target_link_libraries(tst_qdeclarativeproperty KtDeclarative) diff --git a/tests/auto/qdeclarativeproperty/data/TestType.qml b/tests/auto/qdeclarativeproperty/data/TestType.qml new file mode 100644 index 000000000..cef86b4f5 --- /dev/null +++ b/tests/auto/qdeclarativeproperty/data/TestType.qml @@ -0,0 +1,6 @@ +import QtQuick 1.0 + +QtObject { + property int a: 10 +} + diff --git a/tests/auto/qdeclarativeproperty/data/aliasPropertyBindings.qml b/tests/auto/qdeclarativeproperty/data/aliasPropertyBindings.qml new file mode 100644 index 000000000..a253a58da --- /dev/null +++ b/tests/auto/qdeclarativeproperty/data/aliasPropertyBindings.qml @@ -0,0 +1,19 @@ +import QtQuick 1.0 + +Item { + id: root + + property real test: 9 + property real test2: 3 + + property real realProperty: test * test + test + property alias aliasProperty: root.realProperty + + states: State { + name: "switch" + PropertyChanges { + target: root + aliasProperty: 32 * test2 + } + } +} diff --git a/tests/auto/qdeclarativeproperty/data/readSynthesizedObject.qml b/tests/auto/qdeclarativeproperty/data/readSynthesizedObject.qml new file mode 100644 index 000000000..a91d3ed9a --- /dev/null +++ b/tests/auto/qdeclarativeproperty/data/readSynthesizedObject.qml @@ -0,0 +1,9 @@ +import QtQuick 1.0 + +QtObject { + property TestType test + + test: TestType { + property int b: 19 + } +} diff --git a/tests/auto/qdeclarativeproperty/tst_qdeclarativeproperty.cpp b/tests/auto/qdeclarativeproperty/tst_qdeclarativeproperty.cpp new file mode 100644 index 000000000..9cf98b4d1 --- /dev/null +++ b/tests/auto/qdeclarativeproperty/tst_qdeclarativeproperty.cpp @@ -0,0 +1,1466 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2016 Ivailo Monev +** +** This file is part of the test suite of the Katie 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +inline QUrl TEST_FILE(const QString &filename) +{ + return QUrl::fromLocalFile(QLatin1String(SRCDIR) + QLatin1String("/data/") + filename); +} + +class MyQmlObject : public QObject +{ + Q_OBJECT +public: + MyQmlObject() {} +}; + +QML_DECLARE_TYPE(MyQmlObject); + +class MyAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(int foo READ foo WRITE setFoo) +public: + MyAttached(QObject *parent) : QObject(parent), m_foo(13) {} + + int foo() const { return m_foo; } + void setFoo(int f) { m_foo = f; } + +private: + int m_foo; +}; + +class MyContainer : public QObject +{ + Q_OBJECT + Q_PROPERTY(QDeclarativeListProperty children READ children) +public: + MyContainer() {} + + QDeclarativeListProperty children() { return QDeclarativeListProperty(this, m_children); } + + static MyAttached *qmlAttachedProperties(QObject *o) { + return new MyAttached(o); + } + +private: + QList m_children; +}; + +QML_DECLARE_TYPE(MyContainer); +QML_DECLARE_TYPEINFO(MyContainer, QML_HAS_ATTACHED_PROPERTIES) + +class tst_qdeclarativeproperty : public QObject +{ + Q_OBJECT +public: + tst_qdeclarativeproperty() {} + +private slots: + void initTestCase(); + + // Constructors + void qmlmetaproperty(); + void qmlmetaproperty_object(); + void qmlmetaproperty_object_string(); + void qmlmetaproperty_object_context(); + void qmlmetaproperty_object_string_context(); + + // Methods + void name(); + void read(); + void write(); + void reset(); + + // Functionality + void writeObjectToList(); + void writeListToList(); + + //writeToReadOnly(); + + // Bugs + void crashOnValueProperty(); + void aliasPropertyBindings(); + + void copy(); +private: + QDeclarativeEngine engine; +}; + +void tst_qdeclarativeproperty::qmlmetaproperty() +{ + QDeclarativeProperty prop; + + QWeakPointer binding(new QDeclarativeBinding(QLatin1String("null"), 0, engine.rootContext())); + QVERIFY(binding != 0); + QWeakPointer expression(new QDeclarativeExpression()); + QVERIFY(expression != 0); + + QObject *obj = new QObject; + + QCOMPARE(prop.name(), QString()); + QCOMPARE(prop.read(), QVariant()); + QCOMPARE(prop.write(QVariant()), false); + QCOMPARE(prop.hasNotifySignal(), false); + QCOMPARE(prop.needsNotifySignal(), false); + QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, 0), false); + QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, -1), false); + QVERIFY(prop.method().signature() == 0); + QCOMPARE(prop.type(), QDeclarativeProperty::Invalid); + QCOMPARE(prop.isProperty(), false); + QCOMPARE(prop.isWritable(), false); + QCOMPARE(prop.isDesignable(), false); + QCOMPARE(prop.isResettable(), false); + QCOMPARE(prop.isSignalProperty(), false); + QCOMPARE(prop.isValid(), false); + QCOMPARE(prop.object(), (QObject *)0); + QCOMPARE(prop.propertyTypeCategory(), QDeclarativeProperty::InvalidCategory); + QCOMPARE(prop.propertyType(), 0); + QCOMPARE(prop.propertyTypeName(), (const char *)0); + QVERIFY(prop.property().name() == 0); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setBinding(prop, binding.data()) == 0); + QVERIFY(binding == 0); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setSignalExpression(prop, expression.data()) == 0); + QVERIFY(expression == 0); + QCOMPARE(prop.index(), -1); + QCOMPARE(QDeclarativePropertyPrivate::valueTypeCoreIndex(prop), -1); + + delete obj; +} + +class PropertyObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(int defaultProperty READ defaultProperty) + Q_PROPERTY(QRect rectProperty READ rectProperty) + Q_PROPERTY(QRect wrectProperty READ wrectProperty WRITE setWRectProperty) + Q_PROPERTY(QUrl url READ url WRITE setUrl) + Q_PROPERTY(int resettableProperty READ resettableProperty WRITE setResettableProperty RESET resetProperty) + Q_PROPERTY(int propertyWithNotify READ propertyWithNotify WRITE setPropertyWithNotify NOTIFY oddlyNamedNotifySignal) + Q_PROPERTY(MyQmlObject *qmlObject READ qmlObject) + + Q_CLASSINFO("DefaultProperty", "defaultProperty") +public: + PropertyObject() : m_resetProperty(9) {} + + int defaultProperty() { return 10; } + QRect rectProperty() { return QRect(10, 10, 1, 209); } + + QRect wrectProperty() { return m_rect; } + void setWRectProperty(const QRect &r) { m_rect = r; } + + QUrl url() { return m_url; } + void setUrl(const QUrl &u) { m_url = u; } + + int resettableProperty() const { return m_resetProperty; } + void setResettableProperty(int r) { m_resetProperty = r; } + void resetProperty() { m_resetProperty = 9; } + + int propertyWithNotify() const { return m_propertyWithNotify; } + void setPropertyWithNotify(int i) { m_propertyWithNotify = i; emit oddlyNamedNotifySignal(); } + + MyQmlObject *qmlObject() { return &m_qmlObject; } +signals: + void clicked(); + void oddlyNamedNotifySignal(); + +private: + int m_resetProperty; + QRect m_rect; + QUrl m_url; + int m_propertyWithNotify; + MyQmlObject m_qmlObject; +}; + +QML_DECLARE_TYPE(PropertyObject); + +void tst_qdeclarativeproperty::qmlmetaproperty_object() +{ + QObject object; // Has no default property + PropertyObject dobject; // Has default property + + { + QDeclarativeProperty prop(&object); + + QWeakPointer binding(new QDeclarativeBinding(QLatin1String("null"), 0, engine.rootContext())); + QVERIFY(binding != 0); + QWeakPointer expression(new QDeclarativeExpression()); + QVERIFY(expression != 0); + + QObject *obj = new QObject; + + QCOMPARE(prop.name(), QString()); + QCOMPARE(prop.read(), QVariant()); + QCOMPARE(prop.write(QVariant()), false); + QCOMPARE(prop.hasNotifySignal(), false); + QCOMPARE(prop.needsNotifySignal(), false); + QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, 0), false); + QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, -1), false); + QVERIFY(prop.method().signature() == 0); + QCOMPARE(prop.type(), QDeclarativeProperty::Invalid); + QCOMPARE(prop.isProperty(), false); + QCOMPARE(prop.isWritable(), false); + QCOMPARE(prop.isDesignable(), false); + QCOMPARE(prop.isResettable(), false); + QCOMPARE(prop.isSignalProperty(), false); + QCOMPARE(prop.isValid(), false); + QCOMPARE(prop.object(), (QObject *)0); + QCOMPARE(prop.propertyTypeCategory(), QDeclarativeProperty::InvalidCategory); + QCOMPARE(prop.propertyType(), 0); + QCOMPARE(prop.propertyTypeName(), (const char *)0); + QVERIFY(prop.property().name() == 0); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setBinding(prop, binding.data()) == 0); + QVERIFY(binding == 0); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setSignalExpression(prop, expression.data()) == 0); + QVERIFY(expression == 0); + QCOMPARE(prop.index(), -1); + QCOMPARE(QDeclarativePropertyPrivate::valueTypeCoreIndex(prop), -1); + + delete obj; + } + + { + QDeclarativeProperty prop(&dobject); + + QWeakPointer binding(new QDeclarativeBinding(QLatin1String("null"), 0, engine.rootContext())); + binding.data()->setTarget(prop); + QVERIFY(binding != 0); + QWeakPointer expression(new QDeclarativeExpression()); + QVERIFY(expression != 0); + + QObject *obj = new QObject; + + QCOMPARE(prop.name(), QString("defaultProperty")); + QCOMPARE(prop.read(), QVariant(10)); + QCOMPARE(prop.write(QVariant()), false); + QCOMPARE(prop.hasNotifySignal(), false); + QCOMPARE(prop.needsNotifySignal(), true); + QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, 0), false); + QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, -1), false); + QVERIFY(prop.method().signature() == 0); + QCOMPARE(prop.type(), QDeclarativeProperty::Property); + QCOMPARE(prop.isProperty(), true); + QCOMPARE(prop.isWritable(), false); + QCOMPARE(prop.isDesignable(), true); + QCOMPARE(prop.isResettable(), false); + QCOMPARE(prop.isSignalProperty(), false); + QCOMPARE(prop.isValid(), true); + QCOMPARE(prop.object(), qobject_cast(&dobject)); + QCOMPARE(prop.propertyTypeCategory(), QDeclarativeProperty::Normal); + QCOMPARE(prop.propertyType(), (int)QVariant::Int); + QCOMPARE(prop.propertyTypeName(), "int"); + QCOMPARE(QString(prop.property().name()), QString("defaultProperty")); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == 0); + QTest::ignoreMessage(QtWarningMsg, ": Unable to assign null to int"); + QVERIFY(QDeclarativePropertyPrivate::setBinding(prop, binding.data()) == 0); + QVERIFY(binding != 0); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == binding.data()); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setSignalExpression(prop, expression.data()) == 0); + QVERIFY(expression == 0); + QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); + QCOMPARE(QDeclarativePropertyPrivate::valueTypeCoreIndex(prop), -1); + + delete obj; + } +} + +void tst_qdeclarativeproperty::qmlmetaproperty_object_string() +{ + QObject object; + PropertyObject dobject; + + { + QDeclarativeProperty prop(&object, QString("defaultProperty")); + + QWeakPointer binding(new QDeclarativeBinding(QLatin1String("null"), 0, engine.rootContext())); + QVERIFY(binding != 0); + QWeakPointer expression(new QDeclarativeExpression()); + QVERIFY(expression != 0); + + QObject *obj = new QObject; + + QCOMPARE(prop.name(), QString()); + QCOMPARE(prop.read(), QVariant()); + QCOMPARE(prop.write(QVariant()), false); + QCOMPARE(prop.hasNotifySignal(), false); + QCOMPARE(prop.needsNotifySignal(), false); + QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, 0), false); + QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, -1), false); + QVERIFY(prop.method().signature() == 0); + QCOMPARE(prop.type(), QDeclarativeProperty::Invalid); + QCOMPARE(prop.isProperty(), false); + QCOMPARE(prop.isWritable(), false); + QCOMPARE(prop.isDesignable(), false); + QCOMPARE(prop.isResettable(), false); + QCOMPARE(prop.isSignalProperty(), false); + QCOMPARE(prop.isValid(), false); + QCOMPARE(prop.object(), (QObject *)0); + QCOMPARE(prop.propertyTypeCategory(), QDeclarativeProperty::InvalidCategory); + QCOMPARE(prop.propertyType(), 0); + QCOMPARE(prop.propertyTypeName(), (const char *)0); + QVERIFY(prop.property().name() == 0); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setBinding(prop, binding.data()) == 0); + QVERIFY(binding == 0); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setSignalExpression(prop, expression.data()) == 0); + QVERIFY(expression == 0); + QCOMPARE(prop.index(), -1); + QCOMPARE(QDeclarativePropertyPrivate::valueTypeCoreIndex(prop), -1); + + delete obj; + } + + { + QDeclarativeProperty prop(&dobject, QString("defaultProperty")); + + QWeakPointer binding(new QDeclarativeBinding(QLatin1String("null"), 0, engine.rootContext())); + binding.data()->setTarget(prop); + QVERIFY(binding != 0); + QWeakPointer expression(new QDeclarativeExpression()); + QVERIFY(expression != 0); + + QObject *obj = new QObject; + + QCOMPARE(prop.name(), QString("defaultProperty")); + QCOMPARE(prop.read(), QVariant(10)); + QCOMPARE(prop.write(QVariant()), false); + QCOMPARE(prop.hasNotifySignal(), false); + QCOMPARE(prop.needsNotifySignal(), true); + QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, 0), false); + QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, -1), false); + QVERIFY(prop.method().signature() == 0); + QCOMPARE(prop.type(), QDeclarativeProperty::Property); + QCOMPARE(prop.isProperty(), true); + QCOMPARE(prop.isWritable(), false); + QCOMPARE(prop.isDesignable(), true); + QCOMPARE(prop.isResettable(), false); + QCOMPARE(prop.isSignalProperty(), false); + QCOMPARE(prop.isValid(), true); + QCOMPARE(prop.object(), qobject_cast(&dobject)); + QCOMPARE(prop.propertyTypeCategory(), QDeclarativeProperty::Normal); + QCOMPARE(prop.propertyType(), (int)QVariant::Int); + QCOMPARE(prop.propertyTypeName(), "int"); + QCOMPARE(QString(prop.property().name()), QString("defaultProperty")); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == 0); + QTest::ignoreMessage(QtWarningMsg, ": Unable to assign null to int"); + QVERIFY(QDeclarativePropertyPrivate::setBinding(prop, binding.data()) == 0); + QVERIFY(binding != 0); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == binding.data()); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setSignalExpression(prop, expression.data()) == 0); + QVERIFY(expression == 0); + QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); + QCOMPARE(QDeclarativePropertyPrivate::valueTypeCoreIndex(prop), -1); + + delete obj; + } + + { + QDeclarativeProperty prop(&dobject, QString("onClicked")); + + QWeakPointer binding(new QDeclarativeBinding(QLatin1String("null"), 0, engine.rootContext())); + binding.data()->setTarget(prop); + QVERIFY(binding != 0); + QWeakPointer expression(new QDeclarativeExpression()); + QVERIFY(expression != 0); + + QObject *obj = new QObject; + + QCOMPARE(prop.name(), QString("onClicked")); + QCOMPARE(prop.read(), QVariant()); + QCOMPARE(prop.write(QVariant("Hello")), false); + QCOMPARE(prop.hasNotifySignal(), false); + QCOMPARE(prop.needsNotifySignal(), false); + QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, 0), false); + QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, -1), false); + QCOMPARE(QString(prop.method().signature()), QString("clicked()")); + QCOMPARE(prop.type(), QDeclarativeProperty::SignalProperty); + QCOMPARE(prop.isProperty(), false); + QCOMPARE(prop.isWritable(), false); + QCOMPARE(prop.isDesignable(), false); + QCOMPARE(prop.isResettable(), false); + QCOMPARE(prop.isSignalProperty(), true); + QCOMPARE(prop.isValid(), true); + QCOMPARE(prop.object(), qobject_cast(&dobject)); + QCOMPARE(prop.propertyTypeCategory(), QDeclarativeProperty::InvalidCategory); + QCOMPARE(prop.propertyType(), 0); + QCOMPARE(prop.propertyTypeName(), (const char *)0); + QCOMPARE(prop.property().name(), (const char *)0); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setBinding(prop, binding.data()) == 0); + QVERIFY(binding == 0); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setSignalExpression(prop, expression.data()) == 0); + QVERIFY(expression != 0); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == expression.data()); + QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("clicked()")); + QCOMPARE(QDeclarativePropertyPrivate::valueTypeCoreIndex(prop), -1); + + delete obj; + } + + { + QDeclarativeProperty prop(&dobject, QString("onPropertyWithNotifyChanged")); + + QWeakPointer binding(new QDeclarativeBinding(QLatin1String("null"), 0, engine.rootContext())); + binding.data()->setTarget(prop); + QVERIFY(binding != 0); + QWeakPointer expression(new QDeclarativeExpression()); + QVERIFY(expression != 0); + + QObject *obj = new QObject; + + QCOMPARE(prop.name(), QString("onOddlyNamedNotifySignal")); + QCOMPARE(prop.read(), QVariant()); + QCOMPARE(prop.write(QVariant("Hello")), false); + QCOMPARE(prop.hasNotifySignal(), false); + QCOMPARE(prop.needsNotifySignal(), false); + QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, 0), false); + QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, -1), false); + QCOMPARE(QString(prop.method().signature()), QString("oddlyNamedNotifySignal()")); + QCOMPARE(prop.type(), QDeclarativeProperty::SignalProperty); + QCOMPARE(prop.isProperty(), false); + QCOMPARE(prop.isWritable(), false); + QCOMPARE(prop.isDesignable(), false); + QCOMPARE(prop.isResettable(), false); + QCOMPARE(prop.isSignalProperty(), true); + QCOMPARE(prop.isValid(), true); + QCOMPARE(prop.object(), qobject_cast(&dobject)); + QCOMPARE(prop.propertyTypeCategory(), QDeclarativeProperty::InvalidCategory); + QCOMPARE(prop.propertyType(), 0); + QCOMPARE(prop.propertyTypeName(), (const char *)0); + QCOMPARE(prop.property().name(), (const char *)0); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setBinding(prop, binding.data()) == 0); + QVERIFY(binding == 0); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setSignalExpression(prop, expression.data()) == 0); + QVERIFY(expression != 0); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == expression.data()); + QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("oddlyNamedNotifySignal()")); + QCOMPARE(QDeclarativePropertyPrivate::valueTypeCoreIndex(prop), -1); + + delete obj; + } +} + +void tst_qdeclarativeproperty::qmlmetaproperty_object_context() +{ + QObject object; // Has no default property + PropertyObject dobject; // Has default property + + { + QDeclarativeProperty prop(&object, engine.rootContext()); + + QWeakPointer binding(new QDeclarativeBinding(QLatin1String("null"), 0, engine.rootContext())); + QVERIFY(binding != 0); + QWeakPointer expression(new QDeclarativeExpression()); + QVERIFY(expression != 0); + + QObject *obj = new QObject; + + QCOMPARE(prop.name(), QString()); + QCOMPARE(prop.read(), QVariant()); + QCOMPARE(prop.write(QVariant()), false); + QCOMPARE(prop.hasNotifySignal(), false); + QCOMPARE(prop.needsNotifySignal(), false); + QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, 0), false); + QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, -1), false); + QVERIFY(prop.method().signature() == 0); + QCOMPARE(prop.type(), QDeclarativeProperty::Invalid); + QCOMPARE(prop.isProperty(), false); + QCOMPARE(prop.isWritable(), false); + QCOMPARE(prop.isDesignable(), false); + QCOMPARE(prop.isResettable(), false); + QCOMPARE(prop.isSignalProperty(), false); + QCOMPARE(prop.isValid(), false); + QCOMPARE(prop.object(), (QObject *)0); + QCOMPARE(prop.propertyTypeCategory(), QDeclarativeProperty::InvalidCategory); + QCOMPARE(prop.propertyType(), 0); + QCOMPARE(prop.propertyTypeName(), (const char *)0); + QVERIFY(prop.property().name() == 0); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setBinding(prop, binding.data()) == 0); + QVERIFY(binding == 0); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setSignalExpression(prop, expression.data()) == 0); + QVERIFY(expression == 0); + QCOMPARE(prop.index(), -1); + QCOMPARE(QDeclarativePropertyPrivate::valueTypeCoreIndex(prop), -1); + + delete obj; + } + + { + QDeclarativeProperty prop(&dobject, engine.rootContext()); + + QWeakPointer binding(new QDeclarativeBinding(QLatin1String("null"), 0, engine.rootContext())); + binding.data()->setTarget(prop); + QVERIFY(binding != 0); + QWeakPointer expression(new QDeclarativeExpression()); + QVERIFY(expression != 0); + + QObject *obj = new QObject; + + QCOMPARE(prop.name(), QString("defaultProperty")); + QCOMPARE(prop.read(), QVariant(10)); + QCOMPARE(prop.write(QVariant()), false); + QCOMPARE(prop.hasNotifySignal(), false); + QCOMPARE(prop.needsNotifySignal(), true); + QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, 0), false); + QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, -1), false); + QVERIFY(prop.method().signature() == 0); + QCOMPARE(prop.type(), QDeclarativeProperty::Property); + QCOMPARE(prop.isProperty(), true); + QCOMPARE(prop.isWritable(), false); + QCOMPARE(prop.isDesignable(), true); + QCOMPARE(prop.isResettable(), false); + QCOMPARE(prop.isSignalProperty(), false); + QCOMPARE(prop.isValid(), true); + QCOMPARE(prop.object(), qobject_cast(&dobject)); + QCOMPARE(prop.propertyTypeCategory(), QDeclarativeProperty::Normal); + QCOMPARE(prop.propertyType(), (int)QVariant::Int); + QCOMPARE(prop.propertyTypeName(), "int"); + QCOMPARE(QString(prop.property().name()), QString("defaultProperty")); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == 0); + QTest::ignoreMessage(QtWarningMsg, ": Unable to assign null to int"); + QVERIFY(QDeclarativePropertyPrivate::setBinding(prop, binding.data()) == 0); + QVERIFY(binding != 0); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == binding.data()); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setSignalExpression(prop, expression.data()) == 0); + QVERIFY(expression == 0); + QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); + QCOMPARE(QDeclarativePropertyPrivate::valueTypeCoreIndex(prop), -1); + + delete obj; + } +} + +void tst_qdeclarativeproperty::qmlmetaproperty_object_string_context() +{ + QObject object; + PropertyObject dobject; + + { + QDeclarativeProperty prop(&object, QString("defaultProperty"), engine.rootContext()); + + QWeakPointer binding(new QDeclarativeBinding(QLatin1String("null"), 0, engine.rootContext())); + QVERIFY(binding != 0); + QWeakPointer expression(new QDeclarativeExpression()); + QVERIFY(expression != 0); + + QObject *obj = new QObject; + + QCOMPARE(prop.name(), QString()); + QCOMPARE(prop.read(), QVariant()); + QCOMPARE(prop.write(QVariant()), false); + QCOMPARE(prop.hasNotifySignal(), false); + QCOMPARE(prop.needsNotifySignal(), false); + QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, 0), false); + QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, -1), false); + QVERIFY(prop.method().signature() == 0); + QCOMPARE(prop.type(), QDeclarativeProperty::Invalid); + QCOMPARE(prop.isProperty(), false); + QCOMPARE(prop.isWritable(), false); + QCOMPARE(prop.isDesignable(), false); + QCOMPARE(prop.isResettable(), false); + QCOMPARE(prop.isSignalProperty(), false); + QCOMPARE(prop.isValid(), false); + QCOMPARE(prop.object(), (QObject *)0); + QCOMPARE(prop.propertyTypeCategory(), QDeclarativeProperty::InvalidCategory); + QCOMPARE(prop.propertyType(), 0); + QCOMPARE(prop.propertyTypeName(), (const char *)0); + QVERIFY(prop.property().name() == 0); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setBinding(prop, binding.data()) == 0); + QVERIFY(binding == 0); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setSignalExpression(prop, expression.data()) == 0); + QVERIFY(expression == 0); + QCOMPARE(prop.index(), -1); + QCOMPARE(QDeclarativePropertyPrivate::valueTypeCoreIndex(prop), -1); + + delete obj; + } + + { + QDeclarativeProperty prop(&dobject, QString("defaultProperty"), engine.rootContext()); + + QWeakPointer binding(new QDeclarativeBinding(QLatin1String("null"), 0, engine.rootContext())); + binding.data()->setTarget(prop); + QVERIFY(binding != 0); + QWeakPointer expression(new QDeclarativeExpression()); + QVERIFY(expression != 0); + + QObject *obj = new QObject; + + QCOMPARE(prop.name(), QString("defaultProperty")); + QCOMPARE(prop.read(), QVariant(10)); + QCOMPARE(prop.write(QVariant()), false); + QCOMPARE(prop.hasNotifySignal(), false); + QCOMPARE(prop.needsNotifySignal(), true); + QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, 0), false); + QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, -1), false); + QVERIFY(prop.method().signature() == 0); + QCOMPARE(prop.type(), QDeclarativeProperty::Property); + QCOMPARE(prop.isProperty(), true); + QCOMPARE(prop.isWritable(), false); + QCOMPARE(prop.isDesignable(), true); + QCOMPARE(prop.isResettable(), false); + QCOMPARE(prop.isSignalProperty(), false); + QCOMPARE(prop.isValid(), true); + QCOMPARE(prop.object(), qobject_cast(&dobject)); + QCOMPARE(prop.propertyTypeCategory(), QDeclarativeProperty::Normal); + QCOMPARE(prop.propertyType(), (int)QVariant::Int); + QCOMPARE(prop.propertyTypeName(), "int"); + QCOMPARE(QString(prop.property().name()), QString("defaultProperty")); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == 0); + QTest::ignoreMessage(QtWarningMsg, ": Unable to assign null to int"); + QVERIFY(QDeclarativePropertyPrivate::setBinding(prop, binding.data()) == 0); + QVERIFY(binding != 0); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == binding.data()); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setSignalExpression(prop, expression.data()) == 0); + QVERIFY(expression == 0); + QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); + QCOMPARE(QDeclarativePropertyPrivate::valueTypeCoreIndex(prop), -1); + + delete obj; + } + + { + QDeclarativeProperty prop(&dobject, QString("onClicked"), engine.rootContext()); + + QWeakPointer binding(new QDeclarativeBinding(QLatin1String("null"), 0, engine.rootContext())); + binding.data()->setTarget(prop); + QVERIFY(binding != 0); + QWeakPointer expression(new QDeclarativeExpression()); + QVERIFY(expression != 0); + + QObject *obj = new QObject; + + QCOMPARE(prop.name(), QString("onClicked")); + QCOMPARE(prop.read(), QVariant()); + QCOMPARE(prop.write(QVariant("Hello")), false); + QCOMPARE(prop.hasNotifySignal(), false); + QCOMPARE(prop.needsNotifySignal(), false); + QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, 0), false); + QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, -1), false); + QCOMPARE(QString(prop.method().signature()), QString("clicked()")); + QCOMPARE(prop.type(), QDeclarativeProperty::SignalProperty); + QCOMPARE(prop.isProperty(), false); + QCOMPARE(prop.isWritable(), false); + QCOMPARE(prop.isDesignable(), false); + QCOMPARE(prop.isResettable(), false); + QCOMPARE(prop.isSignalProperty(), true); + QCOMPARE(prop.isValid(), true); + QCOMPARE(prop.object(), qobject_cast(&dobject)); + QCOMPARE(prop.propertyTypeCategory(), QDeclarativeProperty::InvalidCategory); + QCOMPARE(prop.propertyType(), 0); + QCOMPARE(prop.propertyTypeName(), (const char *)0); + QCOMPARE(prop.property().name(), (const char *)0); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setBinding(prop, binding.data()) == 0); + QVERIFY(binding == 0); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setSignalExpression(prop, expression.data()) == 0); + QVERIFY(expression != 0); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == expression.data()); + QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("clicked()")); + QCOMPARE(QDeclarativePropertyPrivate::valueTypeCoreIndex(prop), -1); + + delete obj; + } + + { + QDeclarativeProperty prop(&dobject, QString("onPropertyWithNotifyChanged"), engine.rootContext()); + + QWeakPointer binding(new QDeclarativeBinding(QLatin1String("null"), 0, engine.rootContext())); + binding.data()->setTarget(prop); + QVERIFY(binding != 0); + QWeakPointer expression(new QDeclarativeExpression()); + QVERIFY(expression != 0); + + QObject *obj = new QObject; + + QCOMPARE(prop.name(), QString("onOddlyNamedNotifySignal")); + QCOMPARE(prop.read(), QVariant()); + QCOMPARE(prop.write(QVariant("Hello")), false); + QCOMPARE(prop.hasNotifySignal(), false); + QCOMPARE(prop.needsNotifySignal(), false); + QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(obj, 0), false); + QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(obj, -1), false); + QCOMPARE(QString(prop.method().signature()), QString("oddlyNamedNotifySignal()")); + QCOMPARE(prop.type(), QDeclarativeProperty::SignalProperty); + QCOMPARE(prop.isProperty(), false); + QCOMPARE(prop.isWritable(), false); + QCOMPARE(prop.isDesignable(), false); + QCOMPARE(prop.isResettable(), false); + QCOMPARE(prop.isSignalProperty(), true); + QCOMPARE(prop.isValid(), true); + QCOMPARE(prop.object(), qobject_cast(&dobject)); + QCOMPARE(prop.propertyTypeCategory(), QDeclarativeProperty::InvalidCategory); + QCOMPARE(prop.propertyType(), 0); + QCOMPARE(prop.propertyTypeName(), (const char *)0); + QCOMPARE(prop.property().name(), (const char *)0); + QVERIFY(QDeclarativePropertyPrivate::binding(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setBinding(prop, binding.data()) == 0); + QVERIFY(binding == 0); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == 0); + QVERIFY(QDeclarativePropertyPrivate::setSignalExpression(prop, expression.data()) == 0); + QVERIFY(expression != 0); + QVERIFY(QDeclarativePropertyPrivate::signalExpression(prop) == expression.data()); + QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("oddlyNamedNotifySignal()")); + QCOMPARE(QDeclarativePropertyPrivate::valueTypeCoreIndex(prop), -1); + + delete obj; + } +} + +void tst_qdeclarativeproperty::name() +{ + { + QDeclarativeProperty p; + QCOMPARE(p.name(), QString()); + } + + { + PropertyObject o; + QDeclarativeProperty p(&o); + QCOMPARE(p.name(), QString("defaultProperty")); + } + + { + QObject o; + QDeclarativeProperty p(&o, QString("objectName")); + QCOMPARE(p.name(), QString("objectName")); + } + + { + PropertyObject o; + QDeclarativeProperty p(&o, "onClicked"); + QCOMPARE(p.name(), QString("onClicked")); + } + + { + QObject o; + QDeclarativeProperty p(&o, "onClicked"); + QCOMPARE(p.name(), QString()); + } + + { + PropertyObject o; + QDeclarativeProperty p(&o, "onPropertyWithNotifyChanged"); + QCOMPARE(p.name(), QString("onOddlyNamedNotifySignal")); + } + + { + QObject o; + QDeclarativeProperty p(&o, "onPropertyWithNotifyChanged"); + QCOMPARE(p.name(), QString()); + } + + { + QObject o; + QDeclarativeProperty p(&o, "foo"); + QCOMPARE(p.name(), QString()); + } + + { + QDeclarativeProperty p(0, "foo"); + QCOMPARE(p.name(), QString()); + } + + { + PropertyObject o; + QDeclarativeProperty p(&o, "rectProperty"); + QCOMPARE(p.name(), QString("rectProperty")); + } + + { + PropertyObject o; + QDeclarativeProperty p(&o, "rectProperty.x"); + QCOMPARE(p.name(), QString("rectProperty.x")); + } + + { + PropertyObject o; + QDeclarativeProperty p(&o, "rectProperty.foo"); + QCOMPARE(p.name(), QString()); + } +} + +void tst_qdeclarativeproperty::read() +{ + // Invalid + { + QDeclarativeProperty p; + QCOMPARE(p.read(), QVariant()); + } + + // Default prop + { + PropertyObject o; + QDeclarativeProperty p(&o); + QCOMPARE(p.read(), QVariant(10)); + } + + // Invalid default prop + { + QObject o; + QDeclarativeProperty p(&o); + QCOMPARE(p.read(), QVariant()); + } + + // Value prop by name + { + QObject o; + + QDeclarativeProperty p(&o, "objectName"); + QCOMPARE(p.read(), QVariant(QString())); + + o.setObjectName("myName"); + + QCOMPARE(p.read(), QVariant("myName")); + } + + // Value prop by name (static) + { + QObject o; + + QCOMPARE(QDeclarativeProperty::read(&o, "objectName"), QVariant(QString())); + + o.setObjectName("myName"); + + QCOMPARE(QDeclarativeProperty::read(&o, "objectName"), QVariant("myName")); + } + + // Value-type prop + { + PropertyObject o; + QDeclarativeProperty p(&o, "rectProperty.x"); + QCOMPARE(p.read(), QVariant(10)); + } + + // Invalid value-type prop + { + PropertyObject o; + QDeclarativeProperty p(&o, "rectProperty.foo"); + QCOMPARE(p.read(), QVariant()); + } + + // Signal property + { + PropertyObject o; + QDeclarativeProperty p(&o, "onClicked"); + QCOMPARE(p.read(), QVariant()); + + QVERIFY(0 == QDeclarativePropertyPrivate::setSignalExpression(p, new QDeclarativeExpression())); + QVERIFY(0 != QDeclarativePropertyPrivate::signalExpression(p)); + + QCOMPARE(p.read(), QVariant()); + } + + // Automatic signal property + { + PropertyObject o; + QDeclarativeProperty p(&o, "onPropertyWithNotifyChanged"); + QCOMPARE(p.read(), QVariant()); + + QVERIFY(0 == QDeclarativePropertyPrivate::setSignalExpression(p, new QDeclarativeExpression())); + QVERIFY(0 != QDeclarativePropertyPrivate::signalExpression(p)); + + QCOMPARE(p.read(), QVariant()); + } + + // Deleted object + { + PropertyObject *o = new PropertyObject; + QDeclarativeProperty p(o, "rectProperty.x"); + QCOMPARE(p.read(), QVariant(10)); + delete o; + QCOMPARE(p.read(), QVariant()); + } + + // Object property + { + PropertyObject o; + QDeclarativeProperty p(&o, "qmlObject"); + QCOMPARE(p.propertyTypeCategory(), QDeclarativeProperty::Object); + QCOMPARE(p.propertyType(), qMetaTypeId()); + QVariant v = p.read(); + QVERIFY(v.userType() == QMetaType::QObjectStar); + QVERIFY(qvariant_cast(v) == o.qmlObject()); + } + { + QDeclarativeComponent component(&engine, TEST_FILE("readSynthesizedObject.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QDeclarativeProperty p(object, "test", &engine); + + QCOMPARE(p.propertyTypeCategory(), QDeclarativeProperty::Object); + QVERIFY(p.propertyType() != QMetaType::QObjectStar); + + QVariant v = p.read(); + QVERIFY(v.userType() == QMetaType::QObjectStar); + QCOMPARE(qvariant_cast(v)->property("a").toInt(), 10); + QCOMPARE(qvariant_cast(v)->property("b").toInt(), 19); + } + { // static + QDeclarativeComponent component(&engine, TEST_FILE("readSynthesizedObject.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QVariant v = QDeclarativeProperty::read(object, "test", &engine); + QVERIFY(v.userType() == QMetaType::QObjectStar); + QCOMPARE(qvariant_cast(v)->property("a").toInt(), 10); + QCOMPARE(qvariant_cast(v)->property("b").toInt(), 19); + } + + // Attached property + { + QDeclarativeComponent component(&engine); + component.setData("import Test 1.0\nMyContainer { }", QUrl()); + QObject *object = component.create(); + QVERIFY(object != 0); + + QDeclarativeProperty p(object, "MyContainer.foo", qmlContext(object)); + QCOMPARE(p.read(), QVariant(13)); + delete object; + } + { + QDeclarativeComponent component(&engine); + component.setData("import Test 1.0\nMyContainer { MyContainer.foo: 10 }", QUrl()); + QObject *object = component.create(); + QVERIFY(object != 0); + + QDeclarativeProperty p(object, "MyContainer.foo", qmlContext(object)); + QCOMPARE(p.read(), QVariant(10)); + delete object; + } + { + QDeclarativeComponent component(&engine); + component.setData("import Test 1.0 as Foo\nFoo.MyContainer { Foo.MyContainer.foo: 10 }", QUrl()); + QObject *object = component.create(); + QVERIFY(object != 0); + + QDeclarativeProperty p(object, "Foo.MyContainer.foo", qmlContext(object)); + QCOMPARE(p.read(), QVariant(10)); + delete object; + } + { // static + QDeclarativeComponent component(&engine); + component.setData("import Test 1.0 as Foo\nFoo.MyContainer { Foo.MyContainer.foo: 10 }", QUrl()); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(QDeclarativeProperty::read(object, "Foo.MyContainer.foo", qmlContext(object)), QVariant(10)); + delete object; + } +} + +void tst_qdeclarativeproperty::write() +{ + // Invalid + { + QDeclarativeProperty p; + QCOMPARE(p.write(QVariant(10)), false); + } + + // Read-only default prop + { + PropertyObject o; + QDeclarativeProperty p(&o); + QCOMPARE(p.write(QVariant(10)), false); + } + + // Invalid default prop + { + QObject o; + QDeclarativeProperty p(&o); + QCOMPARE(p.write(QVariant(10)), false); + } + + // Read-only prop by name + { + PropertyObject o; + QDeclarativeProperty p(&o, QString("defaultProperty")); + QCOMPARE(p.write(QVariant(10)), false); + } + + // Writable prop by name + { + PropertyObject o; + QDeclarativeProperty p(&o, QString("objectName")); + QCOMPARE(o.objectName(), QString()); + QCOMPARE(p.write(QVariant(QString("myName"))), true); + QCOMPARE(o.objectName(), QString("myName")); + } + + // Writable prop by name (static) + { + PropertyObject o; + QCOMPARE(QDeclarativeProperty::write(&o, QString("objectName"), QVariant(QString("myName"))), true); + QCOMPARE(o.objectName(), QString("myName")); + } + + // Deleted object + { + PropertyObject *o = new PropertyObject; + QDeclarativeProperty p(o, QString("objectName")); + QCOMPARE(p.write(QVariant(QString("myName"))), true); + QCOMPARE(o->objectName(), QString("myName")); + + delete o; + + QCOMPARE(p.write(QVariant(QString("myName"))), false); + } + + // Signal property + { + PropertyObject o; + QDeclarativeProperty p(&o, "onClicked"); + QCOMPARE(p.write(QVariant("console.log(1921)")), false); + + QVERIFY(0 == QDeclarativePropertyPrivate::setSignalExpression(p, new QDeclarativeExpression())); + QVERIFY(0 != QDeclarativePropertyPrivate::signalExpression(p)); + + QCOMPARE(p.write(QVariant("console.log(1921)")), false); + + QVERIFY(0 != QDeclarativePropertyPrivate::signalExpression(p)); + } + + // Automatic signal property + { + PropertyObject o; + QDeclarativeProperty p(&o, "onPropertyWithNotifyChanged"); + QCOMPARE(p.write(QVariant("console.log(1921)")), false); + + QVERIFY(0 == QDeclarativePropertyPrivate::setSignalExpression(p, new QDeclarativeExpression())); + QVERIFY(0 != QDeclarativePropertyPrivate::signalExpression(p)); + + QCOMPARE(p.write(QVariant("console.log(1921)")), false); + + QVERIFY(0 != QDeclarativePropertyPrivate::signalExpression(p)); + } + + // Value-type property + { + PropertyObject o; + QDeclarativeProperty p(&o, "wrectProperty"); + + QCOMPARE(o.wrectProperty(), QRect()); + QCOMPARE(p.write(QRect(1, 13, 99, 8)), true); + QCOMPARE(o.wrectProperty(), QRect(1, 13, 99, 8)); + + QDeclarativeProperty p2(&o, "wrectProperty.x"); + QCOMPARE(p2.read(), QVariant(1)); + QCOMPARE(p2.write(QVariant(6)), true); + QCOMPARE(p2.read(), QVariant(6)); + QCOMPARE(o.wrectProperty(), QRect(6, 13, 99, 8)); + } + + // URL-property + { + PropertyObject o; + QDeclarativeProperty p(&o, "url"); + + QCOMPARE(p.write(QUrl("main.qml")), true); + QCOMPARE(o.url(), QUrl("main.qml")); + + QDeclarativeProperty p2(&o, "url", engine.rootContext()); + + QUrl result = engine.baseUrl().resolved(QUrl("main.qml")); + QVERIFY(result != QUrl("main.qml")); + + QCOMPARE(p2.write(QUrl("main.qml")), true); + QCOMPARE(o.url(), result); + } + { // static + PropertyObject o; + + QCOMPARE(QDeclarativeProperty::write(&o, "url", QUrl("main.qml")), true); + QCOMPARE(o.url(), QUrl("main.qml")); + + QUrl result = engine.baseUrl().resolved(QUrl("main.qml")); + QVERIFY(result != QUrl("main.qml")); + + QCOMPARE(QDeclarativeProperty::write(&o, "url", QUrl("main.qml"), engine.rootContext()), true); + QCOMPARE(o.url(), result); + } + + // Attached property + { + QDeclarativeComponent component(&engine); + component.setData("import Test 1.0\nMyContainer { }", QUrl()); + QObject *object = component.create(); + QVERIFY(object != 0); + + QDeclarativeProperty p(object, "MyContainer.foo", qmlContext(object)); + p.write(QVariant(99)); + QCOMPARE(p.read(), QVariant(99)); + delete object; + } + { + QDeclarativeComponent component(&engine); + component.setData("import Test 1.0 as Foo\nFoo.MyContainer { Foo.MyContainer.foo: 10 }", QUrl()); + QObject *object = component.create(); + QVERIFY(object != 0); + + QDeclarativeProperty p(object, "Foo.MyContainer.foo", qmlContext(object)); + p.write(QVariant(99)); + QCOMPARE(p.read(), QVariant(99)); + delete object; + } +} + +void tst_qdeclarativeproperty::reset() +{ + // Invalid + { + QDeclarativeProperty p; + QCOMPARE(p.isResettable(), false); + QCOMPARE(p.reset(), false); + } + + // Read-only default prop + { + PropertyObject o; + QDeclarativeProperty p(&o); + QCOMPARE(p.isResettable(), false); + QCOMPARE(p.reset(), false); + } + + // Invalid default prop + { + QObject o; + QDeclarativeProperty p(&o); + QCOMPARE(p.isResettable(), false); + QCOMPARE(p.reset(), false); + } + + // Non-resettable-only prop by name + { + PropertyObject o; + QDeclarativeProperty p(&o, QString("defaultProperty")); + QCOMPARE(p.isResettable(), false); + QCOMPARE(p.reset(), false); + } + + // Resettable prop by name + { + PropertyObject o; + QDeclarativeProperty p(&o, QString("resettableProperty")); + + QCOMPARE(p.read(), QVariant(9)); + QCOMPARE(p.write(QVariant(11)), true); + QCOMPARE(p.read(), QVariant(11)); + + QCOMPARE(p.isResettable(), true); + QCOMPARE(p.reset(), true); + + QCOMPARE(p.read(), QVariant(9)); + } + + // Deleted object + { + PropertyObject *o = new PropertyObject; + + QDeclarativeProperty p(o, QString("resettableProperty")); + + QCOMPARE(p.isResettable(), true); + QCOMPARE(p.reset(), true); + + delete o; + + QCOMPARE(p.isResettable(), false); + QCOMPARE(p.reset(), false); + } + + // Signal property + { + PropertyObject o; + QDeclarativeProperty p(&o, "onClicked"); + + QCOMPARE(p.isResettable(), false); + QCOMPARE(p.reset(), false); + } + + // Automatic signal property + { + PropertyObject o; + QDeclarativeProperty p(&o, "onPropertyWithNotifyChanged"); + + QCOMPARE(p.isResettable(), false); + QCOMPARE(p.reset(), false); + } +} + +void tst_qdeclarativeproperty::writeObjectToList() +{ + QDeclarativeComponent containerComponent(&engine); + containerComponent.setData("import Test 1.0\nMyContainer { children: MyQmlObject {} }", QUrl()); + MyContainer *container = qobject_cast(containerComponent.create()); + QVERIFY(container != 0); + QDeclarativeListReference list(container, "children"); + QVERIFY(list.count() == 1); + + MyQmlObject *object = new MyQmlObject; + QDeclarativeProperty prop(container, "children"); + prop.write(qVariantFromValue(object)); + QCOMPARE(list.count(), 1); + QCOMPARE(list.at(0), qobject_cast(object)); +} + +Q_DECLARE_METATYPE(QList); +void tst_qdeclarativeproperty::writeListToList() +{ + QDeclarativeComponent containerComponent(&engine); + containerComponent.setData("import Test 1.0\nMyContainer { children: MyQmlObject {} }", QUrl()); + MyContainer *container = qobject_cast(containerComponent.create()); + QVERIFY(container != 0); + QDeclarativeListReference list(container, "children"); + QVERIFY(list.count() == 1); + + QList objList; + objList << new MyQmlObject() << new MyQmlObject() << new MyQmlObject() << new MyQmlObject(); + QDeclarativeProperty prop(container, "children"); + prop.write(qVariantFromValue(objList)); + QCOMPARE(list.count(), 4); + + //XXX need to try this with read/write prop (for read-only it correctly doesn't write) + /*QList typedObjList; + typedObjList << new MyQmlObject(); + prop.write(qVariantFromValue(&typedObjList)); + QCOMPARE(container->children()->size(), 1);*/ +} + +void tst_qdeclarativeproperty::crashOnValueProperty() +{ + QDeclarativeEngine *engine = new QDeclarativeEngine; + QDeclarativeComponent component(engine); + + component.setData("import Test 1.0\nPropertyObject { wrectProperty.x: 10 }", QUrl()); + PropertyObject *obj = qobject_cast(component.create()); + QVERIFY(obj != 0); + + QDeclarativeProperty p(obj, "wrectProperty.x", qmlContext(obj)); + QCOMPARE(p.name(), QString("wrectProperty.x")); + + QCOMPARE(p.read(), QVariant(10)); + + //don't crash once the engine is deleted + delete engine; + engine = 0; + + QCOMPARE(p.propertyTypeName(), "int"); + QCOMPARE(p.read(), QVariant(10)); + p.write(QVariant(20)); + QCOMPARE(p.read(), QVariant(20)); +} + +// QTBUG-13719 +void tst_qdeclarativeproperty::aliasPropertyBindings() +{ + QDeclarativeComponent component(&engine, TEST_FILE("aliasPropertyBindings.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("realProperty").toReal(), 90.); + QCOMPARE(object->property("aliasProperty").toReal(), 90.); + + object->setProperty("test", 10); + + QCOMPARE(object->property("realProperty").toReal(), 110.); + QCOMPARE(object->property("aliasProperty").toReal(), 110.); + + QDeclarativeProperty realProperty(object, QLatin1String("realProperty")); + QDeclarativeProperty aliasProperty(object, QLatin1String("aliasProperty")); + + // Check there is a binding on these two properties + QVERIFY(QDeclarativePropertyPrivate::binding(realProperty) != 0); + QVERIFY(QDeclarativePropertyPrivate::binding(aliasProperty) != 0); + + // Check that its the same binding on these two properties + QCOMPARE(QDeclarativePropertyPrivate::binding(realProperty), + QDeclarativePropertyPrivate::binding(aliasProperty)); + + // Change the binding + object->setProperty("state", QString("switch")); + + QVERIFY(QDeclarativePropertyPrivate::binding(realProperty) != 0); + QVERIFY(QDeclarativePropertyPrivate::binding(aliasProperty) != 0); + QCOMPARE(QDeclarativePropertyPrivate::binding(realProperty), + QDeclarativePropertyPrivate::binding(aliasProperty)); + + QCOMPARE(object->property("realProperty").toReal(), 96.); + QCOMPARE(object->property("aliasProperty").toReal(), 96.); + + // Check the old binding really has not effect any more + object->setProperty("test", 4); + + QCOMPARE(object->property("realProperty").toReal(), 96.); + QCOMPARE(object->property("aliasProperty").toReal(), 96.); + + object->setProperty("test2", 9); + + QCOMPARE(object->property("realProperty").toReal(), 288.); + QCOMPARE(object->property("aliasProperty").toReal(), 288.); + + // Revert + object->setProperty("state", QString("")); + + QVERIFY(QDeclarativePropertyPrivate::binding(realProperty) != 0); + QVERIFY(QDeclarativePropertyPrivate::binding(aliasProperty) != 0); + QCOMPARE(QDeclarativePropertyPrivate::binding(realProperty), + QDeclarativePropertyPrivate::binding(aliasProperty)); + + QCOMPARE(object->property("realProperty").toReal(), 20.); + QCOMPARE(object->property("aliasProperty").toReal(), 20.); + + object->setProperty("test2", 3); + + QCOMPARE(object->property("realProperty").toReal(), 20.); + QCOMPARE(object->property("aliasProperty").toReal(), 20.); + + delete object; +} + +void tst_qdeclarativeproperty::copy() +{ + PropertyObject object; + + QDeclarativeProperty *property = new QDeclarativeProperty(&object, QLatin1String("defaultProperty")); + QCOMPARE(property->name(), QString("defaultProperty")); + QCOMPARE(property->read(), QVariant(10)); + QCOMPARE(property->type(), QDeclarativeProperty::Property); + QCOMPARE(property->propertyTypeCategory(), QDeclarativeProperty::Normal); + QCOMPARE(property->propertyType(), (int)QVariant::Int); + + QDeclarativeProperty p1(*property); + QCOMPARE(p1.name(), QString("defaultProperty")); + QCOMPARE(p1.read(), QVariant(10)); + QCOMPARE(p1.type(), QDeclarativeProperty::Property); + QCOMPARE(p1.propertyTypeCategory(), QDeclarativeProperty::Normal); + QCOMPARE(p1.propertyType(), (int)QVariant::Int); + + QDeclarativeProperty p2(&object, QLatin1String("url")); + QCOMPARE(p2.name(), QString("url")); + p2 = *property; + QCOMPARE(p2.name(), QString("defaultProperty")); + QCOMPARE(p2.read(), QVariant(10)); + QCOMPARE(p2.type(), QDeclarativeProperty::Property); + QCOMPARE(p2.propertyTypeCategory(), QDeclarativeProperty::Normal); + QCOMPARE(p2.propertyType(), (int)QVariant::Int); + + delete property; property = 0; + + QCOMPARE(p1.name(), QString("defaultProperty")); + QCOMPARE(p1.read(), QVariant(10)); + QCOMPARE(p1.type(), QDeclarativeProperty::Property); + QCOMPARE(p1.propertyTypeCategory(), QDeclarativeProperty::Normal); + QCOMPARE(p1.propertyType(), (int)QVariant::Int); + + QCOMPARE(p2.name(), QString("defaultProperty")); + QCOMPARE(p2.read(), QVariant(10)); + QCOMPARE(p2.type(), QDeclarativeProperty::Property); + QCOMPARE(p2.propertyTypeCategory(), QDeclarativeProperty::Normal); + QCOMPARE(p2.propertyType(), (int)QVariant::Int); +} + +void tst_qdeclarativeproperty::initTestCase() +{ + qmlRegisterType("Test",1,0,"MyQmlObject"); + qmlRegisterType("Test",1,0,"PropertyObject"); + qmlRegisterType("Test",1,0,"MyContainer"); +} + +QTEST_MAIN(tst_qdeclarativeproperty) + +#include "moc_tst_qdeclarativeproperty.cpp"