blob: 8a96fc52c5ffafaafcfdd8a092db03c3eec68f6a [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "interfaces.h"
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlproperty.h>
#include <QtQml/private/qqmlproperty_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qqmlboundsignal_p.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qdir.h>
#if QT_CONFIG(regularexpression)
#include <QtCore/qregularexpression.h>
#endif
#include <QtCore/private/qobject_p.h>
#include "../../shared/util.h"
#include "qobject.h"
#include <QtQml/QQmlPropertyMap>
#include <QDebug>
class MyQmlObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QPoint pointProperty MEMBER m_point)
public:
MyQmlObject(QObject *parent = nullptr) : QObject(parent) {}
private:
QPoint m_point;
};
QML_DECLARE_TYPE(MyQmlObject);
class MyQObject : public QObject
{
Q_OBJECT
public:
MyQObject(QObject *parent = nullptr) : QObject(parent), m_i(0) {}
int inc() { return ++m_i; }
private:
int m_i;
};
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(QQmlListProperty<MyQmlObject> children READ children)
public:
MyContainer() {}
QQmlListProperty<MyQmlObject> children() { return QQmlListProperty<MyQmlObject>(this, m_children); }
static MyAttached *qmlAttachedProperties(QObject *o) {
return new MyAttached(o);
}
private:
QList<MyQmlObject*> m_children;
};
QML_DECLARE_TYPE(MyContainer);
QML_DECLARE_TYPEINFO(MyContainer, QML_HAS_ATTACHED_PROPERTIES)
class tst_qqmlproperty : public QQmlDataTest
{
Q_OBJECT
public:
tst_qqmlproperty() {}
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();
void urlHandling_data();
void urlHandling();
void variantMapHandling_data();
void variantMapHandling();
// Bugs
void crashOnValueProperty();
void aliasPropertyBindings_data();
void aliasPropertyBindings();
void noContext();
void assignEmptyVariantMap();
void warnOnInvalidBinding();
void registeredCompositeTypeProperty();
void deeplyNestedObject();
void readOnlyDynamicProperties();
void aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSystem();
void nullPropertyBinding();
void interfaceBinding();
void floatToStringPrecision_data();
void floatToStringPrecision();
void copy();
void bindingToAlias();
void nestedQQmlPropertyMap();
void underscorePropertyChangeHandler();
private:
QQmlEngine engine;
};
void tst_qqmlproperty::qmlmetaproperty()
{
QQmlProperty prop;
QObject *obj = new QObject;
QQmlAbstractBinding::Ptr binding(QQmlBinding::create(nullptr, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext())));
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(obj, QObjectPrivate::get(obj)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
QCOMPARE(prop.name(), QString());
QCOMPARE(prop.read(), QVariant());
QCOMPARE(prop.write(QVariant()), false);
QCOMPARE(prop.hasNotifySignal(), false);
QCOMPARE(prop.needsNotifySignal(), false);
QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, 0), false);
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, -1), false);
QVERIFY(!prop.method().isValid());
QCOMPARE(prop.type(), QQmlProperty::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 *)nullptr);
QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
QCOMPARE(prop.propertyType(), 0);
QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
QVERIFY(!prop.property().name());
QVERIFY(!QQmlPropertyPrivate::binding(prop));
QQmlPropertyPrivate::setBinding(prop, binding.data());
QVERIFY(binding->ref == 1);
QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), -1);
QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
// 1 = equal, 0 = unknown, -1 = not equal.
static int compareVariantAndListReference(const QVariant &v, QQmlListReference &r)
{
if (QLatin1String(v.typeName()) != QLatin1String("QQmlListReference"))
return -1;
QQmlListReference lhs = v.value<QQmlListReference>();
if (lhs.isValid() != r.isValid())
return -1;
if (lhs.canCount() != r.canCount())
return -1;
if (!lhs.canCount()) {
if (lhs.canAt() != r.canAt())
return -1; // not equal.
return 0; // not sure if they're equal or not, and no way to tell.
}
// if we get here, we must be able to count.
if (lhs.count() != r.count())
return -1;
if (lhs.canAt() != r.canAt())
return -1;
if (!lhs.canAt())
return 0; // can count, but can't check element equality.
for (int i = 0; i < lhs.count(); ++i) {
if (lhs.at(i) != r.at(i)) {
return -1; // different elements :. not equal.
}
}
return 1; // equal.
}
void tst_qqmlproperty::registeredCompositeTypeProperty()
{
// Composite type properties
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("registeredCompositeTypeProperty.qml"));
QObject *obj = component.create();
QVERIFY(obj);
// create property accessors and check types.
QQmlProperty p1(obj, "first");
QQmlProperty p2(obj, "second");
QQmlProperty p3(obj, "third");
QQmlProperty p1e(obj, "first", &engine);
QQmlProperty p2e(obj, "second", &engine);
QQmlProperty p3e(obj, "third", &engine);
QCOMPARE(p1.propertyType(), p2.propertyType());
QVERIFY(p1.propertyType() != p3.propertyType());
// check that the values are retrievable from CPP
QVariant first = obj->property("first");
QVariant second = obj->property("second");
QVariant third = obj->property("third");
QVERIFY(first.isValid());
QVERIFY(second.isValid());
QVERIFY(third.isValid());
// ensure that conversion from qobject-derived-ptr to qobject-ptr works.
QVERIFY(first.value<QObject*>());
QVERIFY(second.value<QObject*>());
QVERIFY(third.value<QObject*>());
// check that the values retrieved via QQmlProperty match those retrieved via QMetaProperty::read().
QCOMPARE(p1.read().value<QObject*>(), first.value<QObject*>());
QCOMPARE(p2.read().value<QObject*>(), second.value<QObject*>());
QCOMPARE(p3.read().value<QObject*>(), third.value<QObject*>());
QCOMPARE(p1e.read().value<QObject*>(), first.value<QObject*>());
QCOMPARE(p2e.read().value<QObject*>(), second.value<QObject*>());
QCOMPARE(p3e.read().value<QObject*>(), third.value<QObject*>());
delete obj;
}
// List-of-composite-type type properties
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("registeredCompositeTypeProperty.qml"));
QObject *obj = component.create();
QVERIFY(obj);
// create list property accessors and check types
QQmlProperty lp1e(obj, "fclist", &engine);
QQmlProperty lp2e(obj, "sclistOne", &engine);
QQmlProperty lp3e(obj, "sclistTwo", &engine);
QVERIFY(lp1e.propertyType() != lp2e.propertyType());
QCOMPARE(lp2e.propertyType(), lp3e.propertyType());
// check that the list values are retrievable from CPP
QVariant firstList = obj->property("fclist");
QVariant secondList = obj->property("sclistOne");
QVariant thirdList = obj->property("sclistTwo");
QVERIFY(firstList.isValid());
QVERIFY(secondList.isValid());
QVERIFY(thirdList.isValid());
// check that the value returned by QQmlProperty::read() is equivalent to the list reference.
QQmlListReference r1(obj, "fclist", &engine);
QQmlListReference r2(obj, "sclistOne", &engine);
QQmlListReference r3(obj, "sclistTwo", &engine);
QCOMPARE(compareVariantAndListReference(lp1e.read(), r1), 1);
QCOMPARE(compareVariantAndListReference(lp2e.read(), r2), 1);
QCOMPARE(compareVariantAndListReference(lp3e.read(), r3), 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(QVariantMap variantMap READ variantMap WRITE setVariantMap)
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_PROPERTY(MyQObject *qObject READ qObject WRITE setQObject NOTIFY qObjectChanged)
Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty)
Q_PROPERTY(QChar qcharProperty READ qcharProperty WRITE setQcharProperty)
Q_PROPERTY(QChar constQChar READ constQChar STORED false CONSTANT FINAL)
Q_CLASSINFO("DefaultProperty", "defaultProperty")
public:
PropertyObject() : m_resetProperty(9), m_qObject(nullptr), m_stringProperty("foo") {}
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; }
QVariantMap variantMap() const { return m_variantMap; }
void setVariantMap(const QVariantMap &variantMap) { m_variantMap = variantMap; }
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; }
MyQObject *qObject() { return m_qObject; }
void setQObject(MyQObject *object)
{
if (m_qObject != object) {
m_qObject = object;
emit qObjectChanged();
}
}
QString stringProperty() const { return m_stringProperty;}
QChar qcharProperty() const { return m_qcharProperty; }
QChar constQChar() const { return 0x25cf; /* Unicode: black circle */ }
void setStringProperty(QString arg) { m_stringProperty = arg; }
void setQcharProperty(QChar arg) { m_qcharProperty = arg; }
signals:
void clicked();
void oddlyNamedNotifySignal();
void qObjectChanged();
private:
int m_resetProperty;
QRect m_rect;
QUrl m_url;
QVariantMap m_variantMap;
int m_propertyWithNotify;
MyQmlObject m_qmlObject;
MyQObject *m_qObject;
QString m_stringProperty;
QChar m_qcharProperty;
};
QML_DECLARE_TYPE(PropertyObject);
void tst_qqmlproperty::qmlmetaproperty_object()
{
QObject object; // Has no default property
PropertyObject dobject; // Has default property
{
QQmlProperty prop(&object);
QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext())));
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
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(nullptr, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, 0), false);
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, -1), false);
QVERIFY(!prop.method().isValid());
QCOMPARE(prop.type(), QQmlProperty::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 *)nullptr);
QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
QCOMPARE(prop.propertyType(), 0);
QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
QVERIFY(!prop.property().name());
QVERIFY(!QQmlPropertyPrivate::binding(prop));
QQmlPropertyPrivate::setBinding(prop, binding.data());
QVERIFY(binding->ref == 1);
QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), -1);
QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
{
QQmlProperty prop(&dobject);
QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
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(nullptr, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, 0), false);
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, -1), false);
QVERIFY(!prop.method().isValid());
QCOMPARE(prop.type(), QQmlProperty::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<QObject*>(&dobject));
QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::Normal);
QCOMPARE(prop.propertyType(), (int)QVariant::Int);
QCOMPARE(prop.propertyTypeName(), "int");
QCOMPARE(QString(prop.property().name()), QString("defaultProperty"));
QVERIFY(!QQmlPropertyPrivate::binding(prop));
QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Unable to assign null to int");
QQmlPropertyPrivate::setBinding(prop, binding.data());
QVERIFY(binding);
QCOMPARE(QQmlPropertyPrivate::binding(prop), binding.data());
QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty"));
QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
}
void tst_qqmlproperty::qmlmetaproperty_object_string()
{
QObject object;
PropertyObject dobject;
{
QQmlProperty prop(&object, QString("defaultProperty"));
QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext())));
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
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(nullptr, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, 0), false);
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, -1), false);
QVERIFY(!prop.method().isValid());
QCOMPARE(prop.type(), QQmlProperty::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 *)nullptr);
QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
QCOMPARE(prop.propertyType(), 0);
QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
QVERIFY(!prop.property().name());
QVERIFY(!QQmlPropertyPrivate::binding(prop));
QQmlPropertyPrivate::setBinding(prop, binding.data());
QVERIFY(binding->ref == 1);
QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), -1);
QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
{
QQmlProperty prop(&dobject, QString("defaultProperty"));
QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
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(nullptr, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, 0), false);
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, -1), false);
QVERIFY(!prop.method().isValid());
QCOMPARE(prop.type(), QQmlProperty::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<QObject*>(&dobject));
QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::Normal);
QCOMPARE(prop.propertyType(), (int)QVariant::Int);
QCOMPARE(prop.propertyTypeName(), "int");
QCOMPARE(QString(prop.property().name()), QString("defaultProperty"));
QVERIFY(!QQmlPropertyPrivate::binding(prop));
QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Unable to assign null to int");
QQmlPropertyPrivate::setBinding(prop, binding.data());
QVERIFY(binding);
QCOMPARE(QQmlPropertyPrivate::binding(prop), binding.data());
QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty"));
QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
{
QQmlProperty prop(&dobject, QString("onClicked"));
QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
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(nullptr, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, 0), false);
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, -1), false);
QCOMPARE(QString(prop.method().methodSignature()), QString("clicked()"));
QCOMPARE(prop.type(), QQmlProperty::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<QObject*>(&dobject));
QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
QCOMPARE(prop.propertyType(), 0);
QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
QCOMPARE(prop.property().name(), (const char *)nullptr);
QVERIFY(!QQmlPropertyPrivate::binding(prop));
QQmlPropertyPrivate::setBinding(prop, binding.data());
QVERIFY(binding->ref == 1);
QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(!sigExprWatcher.wasDeleted());
QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr);
QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("clicked()"));
QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
{
QQmlProperty prop(&dobject, QString("onPropertyWithNotifyChanged"));
QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
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(nullptr, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, 0), false);
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, -1), false);
QCOMPARE(QString(prop.method().methodSignature()), QString("oddlyNamedNotifySignal()"));
QCOMPARE(prop.type(), QQmlProperty::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<QObject*>(&dobject));
QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
QCOMPARE(prop.propertyType(), 0);
QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
QCOMPARE(prop.property().name(), (const char *)nullptr);
QVERIFY(!QQmlPropertyPrivate::binding(prop));
QQmlPropertyPrivate::setBinding(prop, binding.data());
QVERIFY(binding->ref == 1);
QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(!sigExprWatcher.wasDeleted());
QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr);
QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("oddlyNamedNotifySignal()"));
QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
}
void tst_qqmlproperty::qmlmetaproperty_object_context()
{
QObject object; // Has no default property
PropertyObject dobject; // Has default property
{
QQmlProperty prop(&object, engine.rootContext());
QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext())));
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
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(nullptr, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, 0), false);
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, -1), false);
QVERIFY(!prop.method().isValid());
QCOMPARE(prop.type(), QQmlProperty::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 *)nullptr);
QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
QCOMPARE(prop.propertyType(), 0);
QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
QVERIFY(!prop.property().name());
QVERIFY(!QQmlPropertyPrivate::binding(prop));
QQmlPropertyPrivate::setBinding(prop, binding.data());
QVERIFY(binding->ref == 1);
QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), -1);
QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
{
QQmlProperty prop(&dobject, engine.rootContext());
QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
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(nullptr, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, 0), false);
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, -1), false);
QVERIFY(!prop.method().isValid());
QCOMPARE(prop.type(), QQmlProperty::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<QObject*>(&dobject));
QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::Normal);
QCOMPARE(prop.propertyType(), (int)QVariant::Int);
QCOMPARE(prop.propertyTypeName(), "int");
QCOMPARE(QString(prop.property().name()), QString("defaultProperty"));
QVERIFY(!QQmlPropertyPrivate::binding(prop));
QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Unable to assign null to int");
QQmlPropertyPrivate::setBinding(prop, binding.data());
QVERIFY(binding);
QCOMPARE(QQmlPropertyPrivate::binding(prop), binding.data());
QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty"));
QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
}
void tst_qqmlproperty::qmlmetaproperty_object_string_context()
{
QObject object;
PropertyObject dobject;
{
QQmlProperty prop(&object, QString("defaultProperty"), engine.rootContext());
QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext())));
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
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(nullptr, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, 0), false);
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, -1), false);
QVERIFY(!prop.method().isValid());
QCOMPARE(prop.type(), QQmlProperty::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 *)nullptr);
QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
QCOMPARE(prop.propertyType(), 0);
QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
QVERIFY(!prop.property().name());
QVERIFY(!QQmlPropertyPrivate::binding(prop));
QQmlPropertyPrivate::setBinding(prop, binding.data());
QVERIFY(binding->ref == 1);
QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), -1);
QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
{
QQmlProperty prop(&dobject, QString("defaultProperty"), engine.rootContext());
QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
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(nullptr, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, 0), false);
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, -1), false);
QVERIFY(!prop.method().isValid());
QCOMPARE(prop.type(), QQmlProperty::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<QObject*>(&dobject));
QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::Normal);
QCOMPARE(prop.propertyType(), (int)QVariant::Int);
QCOMPARE(prop.propertyTypeName(), "int");
QCOMPARE(QString(prop.property().name()), QString("defaultProperty"));
QVERIFY(!QQmlPropertyPrivate::binding(prop));
QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Unable to assign null to int");
QQmlPropertyPrivate::setBinding(prop, binding.data());
QVERIFY(binding);
QCOMPARE(QQmlPropertyPrivate::binding(prop), binding.data());
QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty"));
QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
{
QQmlProperty prop(&dobject, QString("onClicked"), engine.rootContext());
QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
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(nullptr, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, 0), false);
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, -1), false);
QCOMPARE(QString(prop.method().methodSignature()), QString("clicked()"));
QCOMPARE(prop.type(), QQmlProperty::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<QObject*>(&dobject));
QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
QCOMPARE(prop.propertyType(), 0);
QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
QCOMPARE(prop.property().name(), (const char *)nullptr);
QVERIFY(!QQmlPropertyPrivate::binding(prop));
QQmlPropertyPrivate::setBinding(prop, binding.data());
QVERIFY(binding->ref == 1);
QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(!sigExprWatcher.wasDeleted());
QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr);
QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("clicked()"));
QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
{
QQmlProperty prop(&dobject, QString("onPropertyWithNotifyChanged"), engine.rootContext());
QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext())));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted());
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(nullptr, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false);
QCOMPARE(prop.connectNotifySignal(obj, 0), false);
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false);
QCOMPARE(prop.connectNotifySignal(obj, -1), false);
QCOMPARE(QString(prop.method().methodSignature()), QString("oddlyNamedNotifySignal()"));
QCOMPARE(prop.type(), QQmlProperty::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<QObject*>(&dobject));
QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory);
QCOMPARE(prop.propertyType(), 0);
QCOMPARE(prop.propertyTypeName(), (const char *)nullptr);
QCOMPARE(prop.property().name(), (const char *)nullptr);
QVERIFY(!QQmlPropertyPrivate::binding(prop));
QQmlPropertyPrivate::setBinding(prop, binding.data());
QVERIFY(binding->ref == 1);
QVERIFY(!QQmlPropertyPrivate::signalExpression(prop));
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(!sigExprWatcher.wasDeleted());
QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr);
QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("oddlyNamedNotifySignal()"));
QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
}
void tst_qqmlproperty::name()
{
{
QQmlProperty p;
QCOMPARE(p.name(), QString());
}
{
PropertyObject o;
QQmlProperty p(&o);
QCOMPARE(p.name(), QString("defaultProperty"));
}
{
QObject o;
QQmlProperty p(&o, QString("objectName"));
QCOMPARE(p.name(), QString("objectName"));
}
{
PropertyObject o;
QQmlProperty p(&o, "onClicked");
QCOMPARE(p.name(), QString("onClicked"));
}
{
QObject o;
QQmlProperty p(&o, "onClicked");
QCOMPARE(p.name(), QString());
}
{
PropertyObject o;
QQmlProperty p(&o, "onPropertyWithNotifyChanged");
QCOMPARE(p.name(), QString("onOddlyNamedNotifySignal"));
}
{
QObject o;
QQmlProperty p(&o, "onPropertyWithNotifyChanged");
QCOMPARE(p.name(), QString());
}
{
QObject o;
QQmlProperty p(&o, "foo");
QCOMPARE(p.name(), QString());
}
{
QQmlProperty p(nullptr, "foo");
QCOMPARE(p.name(), QString());
}
{
PropertyObject o;
QQmlProperty p(&o, "rectProperty");
QCOMPARE(p.name(), QString("rectProperty"));
}
{
PropertyObject o;
QQmlProperty p(&o, "rectProperty.x");
QCOMPARE(p.name(), QString("rectProperty.x"));
}
{
PropertyObject o;
QQmlProperty p(&o, "rectProperty.foo");
QCOMPARE(p.name(), QString());
}
}
void tst_qqmlproperty::read()
{
// Invalid
{
QQmlProperty p;
QCOMPARE(p.read(), QVariant());
}
// Default prop
{
PropertyObject o;
QQmlProperty p(&o);
QCOMPARE(p.read(), QVariant(10));
}
// Invalid default prop
{
QObject o;
QQmlProperty p(&o);
QCOMPARE(p.read(), QVariant());
}
// Value prop by name
{
QObject o;
QQmlProperty p(&o, "objectName");
QCOMPARE(p.read(), QVariant(QString()));
o.setObjectName("myName");
QCOMPARE(p.read(), QVariant("myName"));
}
// Value prop by name (static)
{
QObject o;
QCOMPARE(QQmlProperty::read(&o, "objectName"), QVariant(QString()));
o.setObjectName("myName");
QCOMPARE(QQmlProperty::read(&o, "objectName"), QVariant("myName"));
}
// Value-type prop
{
PropertyObject o;
QQmlProperty p(&o, "rectProperty.x");
QCOMPARE(p.read(), QVariant(10));
}
// Invalid value-type prop
{
PropertyObject o;
QQmlProperty p(&o, "rectProperty.foo");
QCOMPARE(p.read(), QVariant());
}
// Signal property
{
PropertyObject o;
QQmlProperty p(&o, "onClicked");
QCOMPARE(p.read(), QVariant());
QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1));
QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p));
QCOMPARE(p.read(), QVariant());
}
// Automatic signal property
{
PropertyObject o;
QQmlProperty p(&o, "onPropertyWithNotifyChanged");
QCOMPARE(p.read(), QVariant());
QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1));
QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p));
QCOMPARE(p.read(), QVariant());
}
// Deleted object
{
PropertyObject *o = new PropertyObject;
QQmlProperty p(o, "rectProperty.x");
QCOMPARE(p.read(), QVariant(10));
delete o;
QCOMPARE(p.read(), QVariant());
}
// Object property registered with Qt, but not registered with QML.
{
PropertyObject o;
QQmlProperty p(&o, "qObject");
QCOMPARE(p.propertyTypeCategory(), QQmlProperty::Object);
QCOMPARE(p.propertyType(), qMetaTypeId<MyQObject*>());
QVariant v = p.read();
QVERIFY(v.canConvert(QMetaType::QObjectStar));
QVERIFY(qvariant_cast<QObject *>(v) == o.qObject());
}
{
QQmlEngine engine;
PropertyObject o;
QQmlProperty p(&o, "qObject", &engine);
QCOMPARE(p.propertyTypeCategory(), QQmlProperty::Object);
QCOMPARE(p.propertyType(), qMetaTypeId<MyQObject*>());
QVariant v = p.read();
QVERIFY(v.canConvert(QMetaType::QObjectStar));
QVERIFY(qvariant_cast<QObject *>(v) == o.qObject());
}
// Object property
{
PropertyObject o;
QQmlProperty p(&o, "qmlObject");
QCOMPARE(p.propertyTypeCategory(), QQmlProperty::Object);
QCOMPARE(p.propertyType(), qMetaTypeId<MyQmlObject*>());
QVariant v = p.read();
QCOMPARE(v.userType(), int(QMetaType::QObjectStar));
QVERIFY(qvariant_cast<QObject *>(v) == o.qmlObject());
}
{
QQmlComponent component(&engine, testFileUrl("readSynthesizedObject.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
QQmlProperty p(object.data(), "test", &engine);
QCOMPARE(p.propertyTypeCategory(), QQmlProperty::Object);
QVERIFY(p.propertyType() != QMetaType::QObjectStar);
QVariant v = p.read();
QCOMPARE(v.userType(), int(QMetaType::QObjectStar));
QCOMPARE(qvariant_cast<QObject *>(v)->property("a").toInt(), 10);
QCOMPARE(qvariant_cast<QObject *>(v)->property("b").toInt(), 19);
}
{ // static
QQmlComponent component(&engine, testFileUrl("readSynthesizedObject.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
QVariant v = QQmlProperty::read(object.data(), "test", &engine);
QCOMPARE(v.userType(), int(QMetaType::QObjectStar));
QCOMPARE(qvariant_cast<QObject *>(v)->property("a").toInt(), 10);
QCOMPARE(qvariant_cast<QObject *>(v)->property("b").toInt(), 19);
}
// Attached property
{
QQmlComponent component(&engine);
component.setData("import Test 1.0\nMyContainer { }", QUrl());
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
QQmlProperty p(object.data(), "MyContainer.foo", qmlContext(object.data()));
QCOMPARE(p.read(), QVariant(13));
}
{
QQmlComponent component(&engine);
component.setData("import Test 1.0\nMyContainer { MyContainer.foo: 10 }", QUrl());
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
QQmlProperty p(object.data(), "MyContainer.foo", qmlContext(object.data()));
QCOMPARE(p.read(), QVariant(10));
}
{
QQmlComponent component(&engine);
component.setData("import Test 1.0 as Foo\nFoo.MyContainer { Foo.MyContainer.foo: 10 }", QUrl());
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
QQmlProperty p(object.data(), "Foo.MyContainer.foo", qmlContext(object.data()));
QCOMPARE(p.read(), QVariant(10));
}
{ // static
QQmlComponent component(&engine);
component.setData("import Test 1.0 as Foo\nFoo.MyContainer { Foo.MyContainer.foo: 10 }", QUrl());
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
QCOMPARE(QQmlProperty::read(object.data(), "Foo.MyContainer.foo",
qmlContext(object.data())), QVariant(10));
}
}
void tst_qqmlproperty::write()
{
// Invalid
{
QQmlProperty p;
QCOMPARE(p.write(QVariant(10)), false);
}
// Read-only default prop
{
PropertyObject o;
QQmlProperty p(&o);
QCOMPARE(p.write(QVariant(10)), false);
}
// Invalid default prop
{
QObject o;
QQmlProperty p(&o);
QCOMPARE(p.write(QVariant(10)), false);
}
// Read-only prop by name
{
PropertyObject o;
QQmlProperty p(&o, QString("defaultProperty"));
QCOMPARE(p.write(QVariant(10)), false);
}
// Writable prop by name
{
PropertyObject o;
QQmlProperty 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(QQmlProperty::write(&o, QString("objectName"), QVariant(QString("myName"))), true);
QCOMPARE(o.objectName(), QString("myName"));
}
// Deleted object
{
PropertyObject *o = new PropertyObject;
QQmlProperty 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;
QQmlProperty p(&o, "onClicked");
QCOMPARE(p.write(QVariant("console.log(1921)")), false);
QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1));
QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p));
QCOMPARE(p.write(QVariant("console.log(1921)")), false);
QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p));
}
// Automatic signal property
{
PropertyObject o;
QQmlProperty p(&o, "onPropertyWithNotifyChanged");
QCOMPARE(p.write(QVariant("console.log(1921)")), false);
QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1));
QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p));
QCOMPARE(p.write(QVariant("console.log(1921)")), false);
QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p));
}
// Value-type property
{
PropertyObject o;
QQmlProperty p(&o, "wrectProperty");
QCOMPARE(o.wrectProperty(), QRect());
QCOMPARE(p.write(QRect(1, 13, 99, 8)), true);
QCOMPARE(o.wrectProperty(), QRect(1, 13, 99, 8));
QQmlProperty 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;
QQmlProperty p(&o, "url");
QCOMPARE(p.write(QUrl("main.qml")), true);
QCOMPARE(o.url(), QUrl("main.qml"));
QQmlProperty 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(QQmlProperty::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(QQmlProperty::write(&o, "url", QUrl("main.qml"), engine.rootContext()), true);
QCOMPARE(o.url(), result);
}
// Char/string-property
{
PropertyObject o;
QQmlProperty qcharProperty(&o, "qcharProperty");
QQmlProperty stringProperty(&o, "stringProperty");
const int black_circle = 0x25cf;
QCOMPARE(qcharProperty.write(QString("foo")), false);
QCOMPARE(qcharProperty.write('Q'), true);
QCOMPARE(qcharProperty.read(), QVariant('Q'));
QCOMPARE(qcharProperty.write(QChar(black_circle)), true);
QCOMPARE(qcharProperty.read(), QVariant(QChar(black_circle)));
QCOMPARE(o.stringProperty(), QString("foo")); // Default value
QCOMPARE(stringProperty.write(QString("bar")), true);
QCOMPARE(o.stringProperty(), QString("bar"));
QCOMPARE(stringProperty.write(QVariant(1234)), true);
QCOMPARE(stringProperty.read().toString(), QString::number(1234));
QCOMPARE(stringProperty.write('A'), true);
QCOMPARE(stringProperty.read().toString(), QString::number('A'));
QCOMPARE(stringProperty.write(QChar(black_circle)), true);
QCOMPARE(stringProperty.read(), QVariant(QChar(black_circle)));
{ // QChar -> QString
QQmlComponent component(&engine);
component.setData("import Test 1.0\nPropertyObject { stringProperty: constQChar }", QUrl());
QScopedPointer<QObject> object(component.create());
PropertyObject *propertyObject = qobject_cast<PropertyObject*>(object.data());
QVERIFY(propertyObject != nullptr);
if (propertyObject) {
QQmlProperty stringProperty(propertyObject, "stringProperty");
QCOMPARE(stringProperty.read(), QVariant(QString(propertyObject->constQChar())));
}
}
}
// VariantMap-property
QVariantMap vm;
vm.insert("key", "value");
{
PropertyObject o;
QQmlProperty p(&o, "variantMap");
QCOMPARE(p.write(vm), true);
QCOMPARE(o.variantMap(), vm);
QQmlProperty p2(&o, "variantMap", engine.rootContext());
QCOMPARE(p2.write(vm), true);
QCOMPARE(o.variantMap(), vm);
}
{ // static
PropertyObject o;
QCOMPARE(QQmlProperty::write(&o, "variantMap", vm), true);
QCOMPARE(o.variantMap(), vm);
QCOMPARE(QQmlProperty::write(&o, "variantMap", vm, engine.rootContext()), true);
QCOMPARE(o.variantMap(), vm);
}
// Attached property
{
QQmlComponent component(&engine);
component.setData("import Test 1.0\nMyContainer { }", QUrl());
QObject *object = component.create();
QVERIFY(object != nullptr);
QQmlProperty p(object, "MyContainer.foo", qmlContext(object));
p.write(QVariant(99));
QCOMPARE(p.read(), QVariant(99));
delete object;
}
{
QQmlComponent component(&engine);
component.setData("import Test 1.0 as Foo\nFoo.MyContainer { Foo.MyContainer.foo: 10 }", QUrl());
QObject *object = component.create();
QVERIFY(object != nullptr);
QQmlProperty p(object, "Foo.MyContainer.foo", qmlContext(object));
p.write(QVariant(99));
QCOMPARE(p.read(), QVariant(99));
delete object;
}
// Writable pointer to QObject derived
{
PropertyObject o;
QQmlProperty p(&o, QString("qObject"));
QCOMPARE(o.qObject(), (QObject*)nullptr);
QObject *newObject = new MyQObject(this);
QCOMPARE(p.write(QVariant::fromValue(newObject)), true);
QCOMPARE(o.qObject(), newObject);
QVariant data = p.read();
QCOMPARE(data.value<QObject*>(), newObject);
QCOMPARE(data.value<MyQObject*>(), newObject);
// Incompatible types can not be written.
QCOMPARE(p.write(QVariant::fromValue(new MyQmlObject(this))), false);
QVariant newData = p.read();
QCOMPARE(newData.value<QObject*>(), newObject);
QCOMPARE(newData.value<MyQObject*>(), newObject);
}
{
QQmlEngine engine;
PropertyObject o;
QQmlProperty p(&o, QString("qObject"), &engine);
QCOMPARE(o.qObject(), (QObject*)nullptr);
QObject *newObject = new MyQObject(this);
QCOMPARE(p.write(QVariant::fromValue(newObject)), true);
QCOMPARE(o.qObject(), newObject);
QVariant data = p.read();
QCOMPARE(data.value<QObject*>(), newObject);
QCOMPARE(data.value<MyQObject*>(), newObject);
// Incompatible types can not be written.
QCOMPARE(p.write(QVariant::fromValue(new MyQmlObject(this))), false);
QVariant newData = p.read();
QCOMPARE(newData.value<QObject*>(), newObject);
QCOMPARE(newData.value<MyQObject*>(), newObject);
}
}
void tst_qqmlproperty::reset()
{
// Invalid
{
QQmlProperty p;
QCOMPARE(p.isResettable(), false);
QCOMPARE(p.reset(), false);
}
// Read-only default prop
{
PropertyObject o;
QQmlProperty p(&o);
QCOMPARE(p.isResettable(), false);
QCOMPARE(p.reset(), false);
}
// Invalid default prop
{
QObject o;
QQmlProperty p(&o);
QCOMPARE(p.isResettable(), false);
QCOMPARE(p.reset(), false);
}
// Non-resettable-only prop by name
{
PropertyObject o;
QQmlProperty p(&o, QString("defaultProperty"));
QCOMPARE(p.isResettable(), false);
QCOMPARE(p.reset(), false);
}
// Resettable prop by name
{
PropertyObject o;
QQmlProperty 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;
QQmlProperty 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;
QQmlProperty p(&o, "onClicked");
QCOMPARE(p.isResettable(), false);
QCOMPARE(p.reset(), false);
}
// Automatic signal property
{
PropertyObject o;
QQmlProperty p(&o, "onPropertyWithNotifyChanged");
QCOMPARE(p.isResettable(), false);
QCOMPARE(p.reset(), false);
}
}
void tst_qqmlproperty::writeObjectToList()
{
QQmlComponent containerComponent(&engine);
containerComponent.setData("import Test 1.0\nMyContainer { children: MyQmlObject {} }", QUrl());
QScopedPointer<QObject> object(containerComponent.create());
MyContainer *container = qobject_cast<MyContainer*>(object.data());
QVERIFY(container != nullptr);
QQmlListReference list(container, "children");
QCOMPARE(list.count(), 1);
QScopedPointer<MyQmlObject> childObject(new MyQmlObject);
QQmlProperty prop(container, "children");
prop.write(QVariant::fromValue(childObject.data()));
QCOMPARE(list.count(), 1);
QCOMPARE(list.at(0), qobject_cast<QObject*>(childObject.data()));
}
void tst_qqmlproperty::writeListToList()
{
QQmlComponent containerComponent(&engine);
containerComponent.setData("import Test 1.0\nMyContainer { children: MyQmlObject {} }", QUrl());
QScopedPointer<QObject> object(containerComponent.create());
MyContainer *container = qobject_cast<MyContainer*>(object.data());
QVERIFY(container != nullptr);
QQmlListReference list(container, "children");
QCOMPARE(list.count(), 1);
QList<QObject*> objList;
objList << new MyQmlObject(this) << new MyQmlObject(this)
<< new MyQmlObject(this) << new MyQmlObject(this);
QQmlProperty prop(container, "children");
prop.write(QVariant::fromValue(objList));
QCOMPARE(list.count(), 4);
//XXX need to try this with read/write prop (for read-only it correctly doesn't write)
/*QList<MyQmlObject*> typedObjList;
typedObjList << new MyQmlObject();
prop.write(QVariant::fromValue(&typedObjList));
QCOMPARE(container->children()->size(), 1);*/
}
void tst_qqmlproperty::urlHandling_data()
{
QTest::addColumn<QByteArray>("input");
QTest::addColumn<QString>("scheme");
QTest::addColumn<QString>("path");
QTest::addColumn<QByteArray>("encoded");
QTest::newRow("unspecifiedFile")
<< QByteArray("main.qml")
<< QString("")
<< QString("main.qml")
<< QByteArray("main.qml");
QTest::newRow("specifiedFile")
<< QByteArray("file:///main.qml")
<< QString("file")
<< QString("/main.qml")
<< QByteArray("file:///main.qml");
QTest::newRow("httpFile")
<< QByteArray("http://www.example.com/main.qml")
<< QString("http")
<< QString("/main.qml")
<< QByteArray("http://www.example.com/main.qml");
QTest::newRow("pathFile")
<< QByteArray("http://www.example.com/resources/main.qml")
<< QString("http")
<< QString("/resources/main.qml")
<< QByteArray("http://www.example.com/resources/main.qml");
QTest::newRow("encodableName")
<< QByteArray("http://www.example.com/main file.qml")
<< QString("http")
<< QString("/main file.qml")
<< QByteArray("http://www.example.com/main%20file.qml");
QTest::newRow("preencodedName")
<< QByteArray("http://www.example.com/resources%7Cmain%20file.qml")
<< QString("http")
<< QString("/resources|main file.qml")
<< QByteArray("http://www.example.com/resources%7Cmain%20file.qml");
QTest::newRow("encodableQuery")
<< QByteArray("http://www.example.com/main.qml?type=text/qml&comment=now working?")
<< QString("http")
<< QString("/main.qml")
<< QByteArray("http://www.example.com/main.qml?type=text/qml&comment=now%20working?");
// Although 'text%2Fqml' is pre-encoded, it will be decoded to allow correct QUrl classification
QTest::newRow("preencodedQuery")
<< QByteArray("http://www.example.com/main.qml?type=text%2Fqml&comment=now working%3F")
<< QString("http")
<< QString("/main.qml")
<< QByteArray("http://www.example.com/main.qml?type=text/qml&comment=now%20working%3F");
QTest::newRow("encodableFragment")
<< QByteArray("http://www.example.com/main.qml?type=text/qml#start+30000|volume+50%")
<< QString("http")
<< QString("/main.qml")
<< QByteArray("http://www.example.com/main.qml?type=text/qml#start+30000%7Cvolume+50%25");
QTest::newRow("improperlyEncodedFragment")
<< QByteArray("http://www.example.com/main.qml?type=text/qml#start+30000%7Cvolume%2B50%")
<< QString("http")
<< QString("/main.qml")
<< QByteArray("http://www.example.com/main.qml?type=text/qml#start+30000%257Cvolume%252B50%25");
}
void tst_qqmlproperty::urlHandling()
{
QFETCH(QByteArray, input);
QFETCH(QString, scheme);
QFETCH(QString, path);
QFETCH(QByteArray, encoded);
QString inputString(QString::fromUtf8(input));
{
PropertyObject o;
QQmlProperty p(&o, "url");
// Test url written as QByteArray
QCOMPARE(p.write(input), true);
QUrl byteArrayResult(o.url());
QCOMPARE(byteArrayResult.scheme(), scheme);
QCOMPARE(byteArrayResult.path(), path);
QCOMPARE(byteArrayResult.toString(QUrl::FullyEncoded), QString::fromUtf8(encoded));
QCOMPARE(byteArrayResult.toEncoded(), encoded);
}
{
PropertyObject o;
QQmlProperty p(&o, "url");
// Test url written as QString
QCOMPARE(p.write(inputString), true);
QUrl stringResult(o.url());
QCOMPARE(stringResult.scheme(), scheme);
QCOMPARE(stringResult.path(), path);
QCOMPARE(stringResult.toString(QUrl::FullyEncoded), QString::fromUtf8(encoded));
QCOMPARE(stringResult.toEncoded(), encoded);
}
}
void tst_qqmlproperty::variantMapHandling_data()
{
QTest::addColumn<QVariantMap>("vm");
// Object literals
{
QVariantMap m;
QTest::newRow("{}") << m;
}
{
QVariantMap m;
m["a"] = QVariantMap();
QTest::newRow("{ a:{} }") << m;
}
{
QVariantMap m, m2;
m2["b"] = 10;
m2["c"] = 20;
m["a"] = m2;
QTest::newRow("{ a:{b:10, c:20} }") << m;
}
{
QVariantMap m;
m["a"] = 10;
m["b"] = QVariantList() << 20 << 30;
QTest::newRow("{ a:10, b:[20, 30]}") << m;
}
// Cyclic objects
{
QVariantMap m;
m["p"] = QVariantMap();
QTest::newRow("var o={}; o.p=o") << m;
}
{
QVariantMap m;
m["p"] = 123;
m["q"] = QVariantMap();
QTest::newRow("var o={}; o.p=123; o.q=o") << m;
}
}
void tst_qqmlproperty::variantMapHandling()
{
QFETCH(QVariantMap, vm);
PropertyObject o;
QQmlProperty p(&o, "variantMap");
QCOMPARE(p.write(vm), true);
QCOMPARE(o.variantMap(), vm);
}
void tst_qqmlproperty::crashOnValueProperty()
{
QQmlEngine *engine = new QQmlEngine;
QQmlComponent component(engine);
component.setData("import Test 1.0\nPropertyObject { wrectProperty.x: 10 }", QUrl());
QScopedPointer<QObject> object(component.create());
PropertyObject *propertyObject = qobject_cast<PropertyObject*>(object.data());
QVERIFY(propertyObject != nullptr);
QQmlProperty p(propertyObject, "wrectProperty.x", qmlContext(propertyObject));
QCOMPARE(p.name(), QString("wrectProperty.x"));
QCOMPARE(p.read(), QVariant(10));
//don't crash once the engine is deleted
delete engine;
engine = nullptr;
QCOMPARE(p.propertyTypeName(), "int");
QCOMPARE(p.read(), QVariant(10));
p.write(QVariant(20));
QCOMPARE(p.read(), QVariant(20));
}
void tst_qqmlproperty::aliasPropertyBindings_data()
{
QTest::addColumn<QString>("file");
QTest::addColumn<QString>("subObject");
QTest::newRow("same object") << "aliasPropertyBindings.qml" << "";
QTest::newRow("different objects") << "aliasPropertyBindings2.qml" << "innerObject";
}
// QTBUG-13719, QTBUG-58271
void tst_qqmlproperty::aliasPropertyBindings()
{
QFETCH(QString, file);
QFETCH(QString, subObject);
QQmlComponent component(&engine, testFileUrl(file));
QObject *object = component.create();
QVERIFY(object != nullptr);
// the object where realProperty lives
QObject *realPropertyObject = object;
if (!subObject.isEmpty())
realPropertyObject = object->property(subObject.toLatin1()).value<QObject*>();
QCOMPARE(realPropertyObject->property("realProperty").toReal(), 90.);
QCOMPARE(object->property("aliasProperty").toReal(), 90.);
object->setProperty("test", 10);
QCOMPARE(realPropertyObject->property("realProperty").toReal(), 110.);
QCOMPARE(object->property("aliasProperty").toReal(), 110.);
QQmlProperty realProperty(realPropertyObject, QLatin1String("realProperty"));
QQmlProperty aliasProperty(object, QLatin1String("aliasProperty"));
// Check there is a binding on these two properties
QVERIFY(QQmlPropertyPrivate::binding(realProperty) != nullptr);
QVERIFY(QQmlPropertyPrivate::binding(aliasProperty) != nullptr);
// Check that its the same binding on these two properties
QCOMPARE(QQmlPropertyPrivate::binding(realProperty),
QQmlPropertyPrivate::binding(aliasProperty));
// Change the binding
object->setProperty("state", QString("switch"));
QVERIFY(QQmlPropertyPrivate::binding(realProperty) != nullptr);
QVERIFY(QQmlPropertyPrivate::binding(aliasProperty) != nullptr);
QCOMPARE(QQmlPropertyPrivate::binding(realProperty),
QQmlPropertyPrivate::binding(aliasProperty));
QCOMPARE(realPropertyObject->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(realPropertyObject->property("realProperty").toReal(), 96.);
QCOMPARE(object->property("aliasProperty").toReal(), 96.);
object->setProperty("test2", 9);
QCOMPARE(realPropertyObject->property("realProperty").toReal(), 288.);
QCOMPARE(object->property("aliasProperty").toReal(), 288.);
// Revert
object->setProperty("state", QString(""));
QVERIFY(QQmlPropertyPrivate::binding(realProperty) != nullptr);
QVERIFY(QQmlPropertyPrivate::binding(aliasProperty) != nullptr);
QCOMPARE(QQmlPropertyPrivate::binding(realProperty),
QQmlPropertyPrivate::binding(aliasProperty));
QCOMPARE(realPropertyObject->property("realProperty").toReal(), 20.);
QCOMPARE(object->property("aliasProperty").toReal(), 20.);
object->setProperty("test2", 3);
QCOMPARE(realPropertyObject->property("realProperty").toReal(), 20.);
QCOMPARE(object->property("aliasProperty").toReal(), 20.);
delete object;
}
void tst_qqmlproperty::copy()
{
PropertyObject object;
QQmlProperty *property = new QQmlProperty(&object, QLatin1String("defaultProperty"));
QCOMPARE(property->name(), QString("defaultProperty"));
QCOMPARE(property->read(), QVariant(10));
QCOMPARE(property->type(), QQmlProperty::Property);
QCOMPARE(property->propertyTypeCategory(), QQmlProperty::Normal);
QCOMPARE(property->propertyType(), (int)QVariant::Int);
QQmlProperty p1(*property);
QCOMPARE(p1.name(), QString("defaultProperty"));
QCOMPARE(p1.read(), QVariant(10));
QCOMPARE(p1.type(), QQmlProperty::Property);
QCOMPARE(p1.propertyTypeCategory(), QQmlProperty::Normal);
QCOMPARE(p1.propertyType(), (int)QVariant::Int);
QQmlProperty p2(&object, QLatin1String("url"));
QCOMPARE(p2.name(), QString("url"));
p2 = *property;
QCOMPARE(p2.name(), QString("defaultProperty"));
QCOMPARE(p2.read(), QVariant(10));
QCOMPARE(p2.type(), QQmlProperty::Property);
QCOMPARE(p2.propertyTypeCategory(), QQmlProperty::Normal);
QCOMPARE(p2.propertyType(), (int)QVariant::Int);
delete property; property = nullptr;
QCOMPARE(p1.name(), QString("defaultProperty"));
QCOMPARE(p1.read(), QVariant(10));
QCOMPARE(p1.type(), QQmlProperty::Property);
QCOMPARE(p1.propertyTypeCategory(), QQmlProperty::Normal);
QCOMPARE(p1.propertyType(), (int)QVariant::Int);
QCOMPARE(p2.name(), QString("defaultProperty"));
QCOMPARE(p2.read(), QVariant(10));
QCOMPARE(p2.type(), QQmlProperty::Property);
QCOMPARE(p2.propertyTypeCategory(), QQmlProperty::Normal);
QCOMPARE(p2.propertyType(), (int)QVariant::Int);
}
void tst_qqmlproperty::noContext()
{
QQmlComponent compA(&engine, testFileUrl("NoContextTypeA.qml"));
QQmlComponent compB(&engine, testFileUrl("NoContextTypeB.qml"));
QObject *a = compA.create();
QVERIFY(a != nullptr);
QObject *b = compB.create();
QVERIFY(b != nullptr);
QVERIFY(QQmlProperty::write(b, "myTypeA", QVariant::fromValue(a), &engine));
delete a;
delete b;
}
void tst_qqmlproperty::assignEmptyVariantMap()
{
PropertyObject o;
QVariantMap map;
map.insert("key", "value");
o.setVariantMap(map);
QCOMPARE(o.variantMap().count(), 1);
QCOMPARE(o.variantMap().isEmpty(), false);
QQmlComponent component(&engine, testFileUrl("assignEmptyVariantMap.qml"));
QObject *obj = component.createWithInitialProperties({{"o", QVariant::fromValue(&o)}});
QVERIFY(obj);
QCOMPARE(o.variantMap().count(), 0);
QCOMPARE(o.variantMap().isEmpty(), true);
delete obj;
}
void tst_qqmlproperty::warnOnInvalidBinding()
{
QUrl testUrl(testFileUrl("invalidBinding.qml"));
QString expectedWarning;
// V4 error message for property-to-property binding
expectedWarning = testUrl.toString() + QString::fromLatin1(":6:5: Unable to assign QQuickText to QQuickRectangle");
QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData());
// V8 error message for function-to-property binding
expectedWarning = testUrl.toString() + QString::fromLatin1(":7:5: Unable to assign QQuickText to QQuickRectangle");
QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData());
#if QT_CONFIG(regularexpression)
// V8 error message for invalid binding to anchor
const QRegularExpression warning(
"^" + testUrl.toString()
+ ":14:9: Unable to assign QQuickItem_QML_\\d+ to QQuickAnchorLine$");
QTest::ignoreMessage(QtWarningMsg, warning);
#endif
QQmlComponent component(&engine, testUrl);
QObject *obj = component.create();
QVERIFY(obj);
delete obj;
}
void tst_qqmlproperty::deeplyNestedObject()
{
PropertyObject o;
QQmlProperty p(&o, "qmlObject.pointProperty.x");
QCOMPARE(p.isValid(), true);
p.write(14);
QCOMPARE(p.read(), QVariant(14));
}
void tst_qqmlproperty::readOnlyDynamicProperties()
{
QQmlComponent comp(&engine, testFileUrl("readonlyPrimitiveVsVar.qml"));
QObject *obj = comp.create();
QVERIFY(obj != nullptr);
QVERIFY(!obj->metaObject()->property(obj->metaObject()->indexOfProperty("r_var")).isWritable());
QVERIFY(!obj->metaObject()->property(obj->metaObject()->indexOfProperty("r_int")).isWritable());
QVERIFY(obj->metaObject()->property(obj->metaObject()->indexOfProperty("w_var")).isWritable());
QVERIFY(obj->metaObject()->property(obj->metaObject()->indexOfProperty("w_int")).isWritable());
delete obj;
}
void tst_qqmlproperty::aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSystem()
{
const QUrl url = testFileUrl("aliasToIdWithMatchingQmlFileName.qml");
QQmlEngine engine;
QQmlComponent component(&engine, url);
QScopedPointer<QObject> root(component.create());
QQmlProperty property(root.data(), "testType.objectName", QQmlEngine::contextForObject(root.data()));
QVERIFY(property.isValid());
}
// QTBUG-77027
void tst_qqmlproperty::nullPropertyBinding()
{
const QUrl url = testFileUrl("nullPropertyBinding.qml");
QQmlEngine engine;
QQmlComponent component(&engine, url);
QScopedPointer<QObject> root(component.create());
QVERIFY(root);
QTest::ignoreMessage(QtMsgType::QtInfoMsg, "undefined");
QMetaObject::invokeMethod(root.get(), "tog");
QTest::ignoreMessage(QtMsgType::QtInfoMsg, "defined");
QMetaObject::invokeMethod(root.get(), "tog");
QTest::ignoreMessage(QtMsgType::QtInfoMsg, "undefined");
QMetaObject::invokeMethod(root.get(), "tog");
}
void tst_qqmlproperty::interfaceBinding()
{
qmlRegisterInterface<Interface>("Interface");
qmlRegisterType<A>("io.qt.bugreports", 1, 0, "A");
qmlRegisterType<B>("io.qt.bugreports", 1, 0, "B");
qmlRegisterType<C>("io.qt.bugreports", 1, 0, "C");
qmlRegisterType<InterfaceConsumer>("io.qt.bugreports", 1, 0, "InterfaceConsumer");
const QVector<QUrl> urls = {
testFileUrl("interfaceBinding.qml"),
testFileUrl("interfaceBinding2.qml")
};
for (const QUrl &url : urls) {
QQmlEngine engine;
QQmlComponent component(&engine, url);
QScopedPointer<QObject> root(component.create());
QVERIFY(root);
QCOMPARE(root->findChild<QObject*>("a1")->property("testValue").toInt(), 42);
QCOMPARE(root->findChild<QObject*>("a2")->property("testValue").toInt(), 43);
QCOMPARE(root->findChild<QObject*>("a3")->property("testValue").toInt(), 44);
}
}
void tst_qqmlproperty::floatToStringPrecision_data()
{
QTest::addColumn<QString>("propertyName");
QTest::addColumn<double>("number");
QTest::addColumn<QString>("qtString");
QTest::addColumn<QString>("jsString");
QTest::newRow("3.4") << "a" << 3.4 << "3.4" << "3.4";
QTest::newRow("0.035003945") << "b" << 0.035003945 << "0.035003945" << "0.035003945";
QTest::newRow("0.0000012345") << "c" << 0.0000012345 << "1.2345e-06" << "0.0000012345";
QTest::newRow("0.00000012345") << "d" << 0.00000012345 << "1.2345e-07" << "1.2345e-7";
QTest::newRow("1e20") << "e" << 1e20 << "1e+20" << "100000000000000000000";
QTest::newRow("1e21") << "f" << 1e21 << "1e+21" << "1e+21";
}
void tst_qqmlproperty::floatToStringPrecision()
{
QQmlComponent comp(&engine, testFileUrl("floatToStringPrecision.qml"));
QScopedPointer<QObject> obj(comp.create());
QVERIFY(obj != nullptr);
QFETCH(QString, propertyName);
QFETCH(double, number);
QFETCH(QString, qtString);
QFETCH(QString, jsString);
QByteArray name = propertyName.toLatin1();
QCOMPARE(obj->property(name).toDouble(), number);
QCOMPARE(obj->property(name).toString(), qtString);
QByteArray name1 = (propertyName + QLatin1Char('1')).toLatin1();
QCOMPARE(obj->property(name1).toDouble(), number);
QCOMPARE(obj->property(name1).toString(), qtString);
QByteArray name2 = (propertyName + QLatin1Char('2')).toLatin1();
QCOMPARE(obj->property(name2).toDouble(), number);
QCOMPARE(obj->property(name2).toString(), jsString);
}
void tst_qqmlproperty::initTestCase()
{
QQmlDataTest::initTestCase();
qmlRegisterType<MyQmlObject>("Test",1,0,"MyQmlObject");
qmlRegisterType<PropertyObject>("Test",1,0,"PropertyObject");
qmlRegisterType<MyContainer>("Test",1,0,"MyContainer");
}
// QTBUG-60908
void tst_qqmlproperty::bindingToAlias()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("aliasToBinding.qml"));
QScopedPointer<QObject> o(component.create());
QVERIFY(!o.isNull());
}
void tst_qqmlproperty::nestedQQmlPropertyMap()
{
QQmlPropertyMap mainPropertyMap;
QQmlPropertyMap nestedPropertyMap;
QQmlPropertyMap deeplyNestedPropertyMap;
mainPropertyMap.insert("nesting1", QVariant::fromValue(&nestedPropertyMap));
nestedPropertyMap.insert("value", 42);
nestedPropertyMap.insert("nesting2", QVariant::fromValue(&deeplyNestedPropertyMap));
deeplyNestedPropertyMap.insert("value", "success");
QQmlProperty value{&mainPropertyMap, "nesting1.value"};
QCOMPARE(value.read().toInt(), 42);
QQmlProperty success{&mainPropertyMap, "nesting1.nesting2.value"};
QCOMPARE(success.read().toString(), QLatin1String("success"));
}
void tst_qqmlproperty::underscorePropertyChangeHandler()
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(R"(
import QtQuick 2.12
Item {
property int __withUnderScore
}
)", QUrl::fromLocalFile("."));
QScopedPointer<QObject> root { component.create() };
QVERIFY(root);
QQmlProperty changeHandler(root.get(), "on__WithUnderScoreChanged");
QVERIFY(changeHandler.isValid());
QVERIFY(changeHandler.isSignalProperty());
}
QTEST_MAIN(tst_qqmlproperty)
#include "tst_qqmlproperty.moc"