2566 lines
100 KiB
C++
2566 lines
100 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#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
|
|
#if QT_CONFIG(process)
|
|
#include <QtCore/qprocess.h>
|
|
#endif
|
|
#include <QtCore/private/qobject_p.h>
|
|
#include <QtQuickTestUtils/private/qmlutils_p.h>
|
|
#include "qobject.h"
|
|
#include <QtQml/QQmlPropertyMap>
|
|
|
|
using namespace Qt::StringLiterals;
|
|
|
|
#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 MyReplaceIfNotDefaultBehaviorContainer : public MyContainer
|
|
{
|
|
Q_OBJECT
|
|
Q_PROPERTY(QQmlListProperty<MyQmlObject> defaultList READ defaultList)
|
|
|
|
QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE_IF_NOT_DEFAULT
|
|
Q_CLASSINFO("DefaultProperty", "defaultList")
|
|
public:
|
|
MyReplaceIfNotDefaultBehaviorContainer() {}
|
|
|
|
QQmlListProperty<MyQmlObject> defaultList() { return QQmlListProperty<MyQmlObject>(this, &m_defaultList); }
|
|
|
|
private:
|
|
QList<MyQmlObject*> m_defaultList;
|
|
};
|
|
|
|
QML_DECLARE_TYPE(MyReplaceIfNotDefaultBehaviorContainer);
|
|
|
|
class MyAlwaysReplaceBehaviorContainer : public MyContainer
|
|
{
|
|
Q_OBJECT
|
|
|
|
QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE
|
|
public:
|
|
MyAlwaysReplaceBehaviorContainer() {}
|
|
};
|
|
|
|
QML_DECLARE_TYPE(MyAlwaysReplaceBehaviorContainer);
|
|
|
|
class ListHolder : public QObject
|
|
{
|
|
Q_OBJECT
|
|
Q_PROPERTY(QVariantList varList READ varList NOTIFY varListChanged)
|
|
Q_PROPERTY(QList<double> doubleList READ doubleList WRITE setDoubleList NOTIFY doubleListChanged)
|
|
public:
|
|
explicit ListHolder(QObject *parent = nullptr) : QObject(parent) {}
|
|
|
|
QVariantList varList() const { return {1.1, 2.2, 3.3, 11, 5.25f, QStringLiteral("11")}; }
|
|
|
|
QList<double> doubleList() const { return m_doubleList; }
|
|
|
|
void setDoubleList(const QList<double> &newDoubleList)
|
|
{
|
|
if (m_doubleList == newDoubleList)
|
|
return;
|
|
m_doubleList = newDoubleList;
|
|
emit doubleListChanged();
|
|
}
|
|
|
|
signals:
|
|
void varListChanged();
|
|
void doubleListChanged();
|
|
|
|
private:
|
|
QList<double> m_doubleList;
|
|
};
|
|
|
|
|
|
class tst_qqmlproperty : public QQmlDataTest
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
tst_qqmlproperty() : QQmlDataTest(QT_QMLTEST_DATADIR) {}
|
|
|
|
private slots:
|
|
void initTestCase() override;
|
|
|
|
// 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();
|
|
void listOverrideBehavior();
|
|
|
|
//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();
|
|
|
|
void signalExpressionWithoutObject();
|
|
|
|
void dontRemoveQPropertyBinding();
|
|
void compatResolveUrls();
|
|
|
|
void initFlags_data();
|
|
void initFlags();
|
|
|
|
void constructFromPlainMetaObject();
|
|
|
|
void bindToNonQObjectTarget();
|
|
void assignVariantList();
|
|
|
|
void listAssignmentSignals();
|
|
|
|
private:
|
|
QQmlEngine engine;
|
|
};
|
|
|
|
void tst_qqmlproperty::qmlmetaproperty()
|
|
{
|
|
QQmlProperty prop;
|
|
|
|
QScopedPointer<QObject> obj(new QObject);
|
|
|
|
QQmlAbstractBinding::Ptr binding(QQmlBinding::create(nullptr, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext())));
|
|
QVERIFY(binding);
|
|
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(
|
|
obj.data(), QObjectPrivate::get(obj.data())->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.data(), SLOT(deleteLater())), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), 0), false);
|
|
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), -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());
|
|
}
|
|
|
|
// 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"));
|
|
QScopedPointer<QObject> obj(component.create());
|
|
QVERIFY(obj);
|
|
|
|
// create property accessors and check types.
|
|
QQmlProperty p1(obj.data(), "first");
|
|
QQmlProperty p2(obj.data(), "second");
|
|
QQmlProperty p3(obj.data(), "third");
|
|
QQmlProperty p1e(obj.data(), "first", &engine);
|
|
QQmlProperty p2e(obj.data(), "second", &engine);
|
|
QQmlProperty p3e(obj.data(), "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*>());
|
|
}
|
|
|
|
// List-of-composite-type type properties
|
|
{
|
|
QQmlEngine engine;
|
|
QQmlComponent component(&engine, testFileUrl("registeredCompositeTypeProperty.qml"));
|
|
QScopedPointer<QObject> obj(component.create());
|
|
QVERIFY(obj);
|
|
|
|
// create list property accessors and check types
|
|
QQmlProperty lp1e(obj.data(), "fclist", &engine);
|
|
QQmlProperty lp2e(obj.data(), "sclistOne", &engine);
|
|
QQmlProperty lp3e(obj.data(), "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.data(), "fclist");
|
|
QQmlListReference r2(obj.data(), "sclistOne");
|
|
QQmlListReference r3(obj.data(), "sclistTwo");
|
|
QCOMPARE(compareVariantAndListReference(lp1e.read(), r1), 1);
|
|
QCOMPARE(compareVariantAndListReference(lp2e.read(), r2), 1);
|
|
QCOMPARE(compareVariantAndListReference(lp3e.read(), r3), 1);
|
|
}
|
|
}
|
|
|
|
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 u'\u25cf'; /* 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());
|
|
|
|
QScopedPointer<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.data(), SLOT(deleteLater())), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), 0), false);
|
|
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), -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());
|
|
}
|
|
|
|
{
|
|
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());
|
|
|
|
QScopedPointer<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.data(), SLOT(deleteLater())), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), 0), false);
|
|
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), -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(), QMetaType::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());
|
|
}
|
|
}
|
|
|
|
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());
|
|
|
|
QScopedPointer<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.data(), SLOT(deleteLater())), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), 0), false);
|
|
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), -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());
|
|
}
|
|
|
|
{
|
|
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());
|
|
|
|
QScopedPointer<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.data(), SLOT(deleteLater())), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), 0), false);
|
|
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), -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(), QMetaType::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());
|
|
}
|
|
|
|
{
|
|
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());
|
|
|
|
QScopedPointer<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.data(), SLOT(deleteLater())), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), 0), false);
|
|
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), -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());
|
|
}
|
|
|
|
{
|
|
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());
|
|
|
|
QScopedPointer<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.data(), SLOT(deleteLater())), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), 0), false);
|
|
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), -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());
|
|
}
|
|
}
|
|
|
|
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());
|
|
|
|
QScopedPointer<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.data(), SLOT(deleteLater())), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), 0), false);
|
|
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), -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());
|
|
}
|
|
|
|
{
|
|
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());
|
|
|
|
QScopedPointer<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.data(), SLOT(deleteLater())), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), 0), false);
|
|
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), -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(), QMetaType::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());
|
|
}
|
|
}
|
|
|
|
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());
|
|
|
|
QScopedPointer<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.data(), SLOT(deleteLater())), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), 0), false);
|
|
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), -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());
|
|
}
|
|
|
|
{
|
|
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());
|
|
|
|
QScopedPointer<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.data(), SLOT(deleteLater())), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), 0), false);
|
|
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), -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(), QMetaType::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());
|
|
}
|
|
|
|
{
|
|
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());
|
|
|
|
QScopedPointer<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.data(), SLOT(deleteLater())), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), 0), false);
|
|
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), -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());
|
|
}
|
|
|
|
{
|
|
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());
|
|
|
|
QScopedPointer<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.data(), SLOT(deleteLater())), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), 0), false);
|
|
QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), obj->metaObject()->indexOfMethod(
|
|
"deleteLater()")), false);
|
|
QCOMPARE(prop.connectNotifySignal(obj.data(), -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());
|
|
}
|
|
}
|
|
|
|
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
|
|
{
|
|
QScopedPointer<PropertyObject> o(new PropertyObject);
|
|
QQmlProperty p(o.data(), "rectProperty.x");
|
|
QCOMPARE(p.read(), QVariant(10));
|
|
o.reset();
|
|
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(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(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.typeId(), 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.typeId(), 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.typeId(), 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
|
|
{
|
|
QScopedPointer<PropertyObject> o(new PropertyObject);
|
|
QQmlProperty p(o.data(), QString("objectName"));
|
|
QCOMPARE(p.write(QVariant(QString("myName"))), true);
|
|
QCOMPARE(o->objectName(), QString("myName"));
|
|
|
|
o.reset();
|
|
|
|
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");
|
|
const QUrl url = QUrl("main.qml");
|
|
|
|
QCOMPARE(p.write(url), true);
|
|
QCOMPARE(o.url(), url);
|
|
|
|
QQmlProperty p2(&o, "url", engine.rootContext());
|
|
|
|
QCOMPARE(p2.write(url), true);
|
|
QCOMPARE(o.url(), url);
|
|
}
|
|
{ // static
|
|
PropertyObject o;
|
|
const QUrl url = QUrl("main.qml");
|
|
|
|
QCOMPARE(QQmlProperty::write(&o, "url", url), true);
|
|
QCOMPARE(o.url(), url);
|
|
|
|
QCOMPARE(QQmlProperty::write(&o, "url", url, engine.rootContext()), true);
|
|
QCOMPARE(o.url(), url);
|
|
}
|
|
|
|
// Char/string-property
|
|
{
|
|
PropertyObject o;
|
|
QQmlProperty qcharProperty(&o, "qcharProperty");
|
|
QQmlProperty stringProperty(&o, "stringProperty");
|
|
|
|
const char16_t black_circle = 0x25cf;
|
|
|
|
QCOMPARE(qcharProperty.write(QString("foo")), false);
|
|
QCOMPARE(qcharProperty.write('Q'), true);
|
|
QCOMPARE(qcharProperty.read(), QChar('Q'));
|
|
QCOMPARE(qcharProperty.write(QChar(black_circle)), true);
|
|
QCOMPARE(qcharProperty.read(), 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(), QString(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());
|
|
QScopedPointer<QObject> object(component.create());
|
|
QVERIFY(object != nullptr);
|
|
|
|
QQmlProperty p(object.data(), "MyContainer.foo", qmlContext(object.data()));
|
|
p.write(QVariant(99));
|
|
QCOMPARE(p.read(), QVariant(99));
|
|
}
|
|
{
|
|
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()));
|
|
p.write(QVariant(99));
|
|
QCOMPARE(p.read(), QVariant(99));
|
|
}
|
|
// 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
|
|
{
|
|
QScopedPointer<PropertyObject> o(new PropertyObject);
|
|
|
|
QQmlProperty p(o.data(), QString("resettableProperty"));
|
|
|
|
QCOMPARE(p.isResettable(), true);
|
|
QCOMPARE(p.reset(), true);
|
|
|
|
o.reset();
|
|
|
|
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::listOverrideBehavior()
|
|
{
|
|
QQmlComponent alwaysAppendContainerComponent(&engine, testFileUrl("ListOverrideAlwaysAppendOverridenContainer.qml"));
|
|
QScopedPointer<QObject> alwaysAppendObject(alwaysAppendContainerComponent.create());
|
|
MyContainer *alwaysAppendContainer = qobject_cast<MyContainer*>(alwaysAppendObject.data());
|
|
QVERIFY(alwaysAppendContainer != nullptr);
|
|
QQmlListReference alwaysAppendChildrenList(alwaysAppendContainer, "children");
|
|
QCOMPARE(alwaysAppendChildrenList.count(), 5);
|
|
QQmlComponent replaceIfNotDefaultContainerComponent(&engine, testFileUrl("ListOverrideReplaceIfNotDefaultOverridenContainer.qml"));
|
|
QScopedPointer<QObject> replaceIfNotDefaultObject(replaceIfNotDefaultContainerComponent.create());
|
|
MyReplaceIfNotDefaultBehaviorContainer *replaceIfNotDefaultContainer = qobject_cast<MyReplaceIfNotDefaultBehaviorContainer*>(replaceIfNotDefaultObject.data());
|
|
QVERIFY(replaceIfNotDefaultContainer != nullptr);
|
|
QQmlListReference replaceIfNotDefaultDefaultList(replaceIfNotDefaultContainer, "defaultList");
|
|
QCOMPARE(replaceIfNotDefaultDefaultList.count(), 5);
|
|
QQmlListReference replaceIfNotDefaultChildrenList(replaceIfNotDefaultContainer, "children");
|
|
QCOMPARE(replaceIfNotDefaultChildrenList.count(), 2);
|
|
QQmlComponent alwaysReplaceContainerComponent(&engine, testFileUrl("ListOverrideAlwaysReplaceOverridenContainer.qml"));
|
|
QScopedPointer<QObject> alwaysReplaceObject(alwaysReplaceContainerComponent.create());
|
|
MyContainer *alwaysReplaceContainer = qobject_cast<MyContainer*>(alwaysReplaceObject.data());
|
|
QVERIFY(alwaysReplaceContainer != nullptr);
|
|
QQmlListReference alwaysReplaceChildrenList(alwaysReplaceContainer, "children");
|
|
QCOMPARE(alwaysReplaceChildrenList.count(), 2);
|
|
|
|
{
|
|
QQmlComponent appendQml(&engine, testFileUrl("listBehaviorAppendPragma.qml"));
|
|
QVERIFY2(appendQml.isReady(), qPrintable(appendQml.errorString()));
|
|
QScopedPointer<QObject> o(appendQml.create());
|
|
QVERIFY(o);
|
|
QCOMPARE(o->property("length1").toInt(), 2);
|
|
QCOMPARE(o->property("length2").toInt(), 1);
|
|
QCOMPARE(o->property("default1").toInt(), 2);
|
|
QCOMPARE(o->property("default2").toInt(), 1);
|
|
}
|
|
|
|
{
|
|
QQmlComponent replaceQml(&engine, testFileUrl("listBehaviorReplacePragma.qml"));
|
|
QVERIFY2(replaceQml.isReady(), qPrintable(replaceQml.errorString()));
|
|
QScopedPointer<QObject> o(replaceQml.create());
|
|
QVERIFY(o);
|
|
QCOMPARE(o->property("length1").toInt(), 1);
|
|
QCOMPARE(o->property("length2").toInt(), 1);
|
|
QCOMPARE(o->property("default1").toInt(), 1);
|
|
QCOMPARE(o->property("default2").toInt(), 1);
|
|
}
|
|
|
|
{
|
|
QQmlComponent replaceIfNotDefaultQml(
|
|
&engine, testFileUrl("listBehaviorReplaceIfNotDefaultPragma.qml"));
|
|
QVERIFY2(replaceIfNotDefaultQml.isReady(),
|
|
qPrintable(replaceIfNotDefaultQml.errorString()));
|
|
QScopedPointer<QObject> o(replaceIfNotDefaultQml.create());
|
|
QVERIFY(o);
|
|
QCOMPARE(o->property("length1").toInt(), 1);
|
|
QCOMPARE(o->property("length2").toInt(), 1);
|
|
QCOMPARE(o->property("default1").toInt(), 2);
|
|
QCOMPARE(o->property("default2").toInt(), 1);
|
|
}
|
|
|
|
{
|
|
QQmlComponent fail1(&engine, testFileUrl("listBehaviorFail1.qml"));
|
|
QVERIFY(fail1.isError());
|
|
QVERIFY(fail1.errorString().contains(
|
|
QStringLiteral("Unknown list property assign behavior 'Foo' in pragma")));
|
|
}
|
|
|
|
{
|
|
QQmlComponent fail2(&engine, testFileUrl("listBehaviorFail2.qml"));
|
|
QVERIFY(fail2.isError());
|
|
QVERIFY(fail2.errorString().contains(
|
|
QStringLiteral("Multiple list property assign behavior pragmas found")));
|
|
}
|
|
}
|
|
|
|
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?");
|
|
|
|
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%2Fqml&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()
|
|
{
|
|
QScopedPointer<QQmlEngine> engine(new QQmlEngine);
|
|
QQmlComponent component(engine.data());
|
|
|
|
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
|
|
engine.reset();
|
|
|
|
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));
|
|
|
|
QScopedPointer<QObject> object(component.create());
|
|
QVERIFY(object != nullptr);
|
|
|
|
// the object where realProperty lives
|
|
QObject *realPropertyObject = object.data();
|
|
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.data(), 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.);
|
|
}
|
|
|
|
void tst_qqmlproperty::copy()
|
|
{
|
|
PropertyObject object;
|
|
|
|
QScopedPointer<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(), QMetaType::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(), QMetaType::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(), QMetaType::Int);
|
|
|
|
property.reset();
|
|
|
|
QCOMPARE(p1.name(), QString("defaultProperty"));
|
|
QCOMPARE(p1.read(), QVariant(10));
|
|
QCOMPARE(p1.type(), QQmlProperty::Property);
|
|
QCOMPARE(p1.propertyTypeCategory(), QQmlProperty::Normal);
|
|
QCOMPARE(p1.propertyType(), QMetaType::Int);
|
|
|
|
QCOMPARE(p2.name(), QString("defaultProperty"));
|
|
QCOMPARE(p2.read(), QVariant(10));
|
|
QCOMPARE(p2.type(), QQmlProperty::Property);
|
|
QCOMPARE(p2.propertyTypeCategory(), QQmlProperty::Normal);
|
|
QCOMPARE(p2.propertyType(), QMetaType::Int);
|
|
|
|
p1 = QQmlProperty();
|
|
QQmlPropertyPrivate *p2d = QQmlPropertyPrivate::get(p2);
|
|
QCOMPARE(p2d->count(), 1);
|
|
|
|
// Use a pointer to avoid compiler warning about self-assignment.
|
|
QQmlProperty *p2p = &p2;
|
|
*p2p = p2;
|
|
|
|
QCOMPARE(p2d->count(), 1);
|
|
}
|
|
|
|
void tst_qqmlproperty::noContext()
|
|
{
|
|
QQmlComponent compA(&engine, testFileUrl("NoContextTypeA.qml"));
|
|
QQmlComponent compB(&engine, testFileUrl("NoContextTypeB.qml"));
|
|
|
|
QScopedPointer<QObject> a(compA.create());
|
|
QVERIFY(a != nullptr);
|
|
QScopedPointer<QObject> b(compB.create());
|
|
QVERIFY(b != nullptr);
|
|
|
|
QVERIFY(QQmlProperty::write(b.data(), "myTypeA", QVariant::fromValue(a.data()), &engine));
|
|
}
|
|
|
|
void tst_qqmlproperty::assignEmptyVariantMap()
|
|
{
|
|
PropertyObject o;
|
|
|
|
QVariantMap map;
|
|
map.insert("key", "value");
|
|
o.setVariantMap(map);
|
|
QCOMPARE(o.variantMap().size(), 1);
|
|
QCOMPARE(o.variantMap().isEmpty(), false);
|
|
|
|
|
|
QQmlComponent component(&engine, testFileUrl("assignEmptyVariantMap.qml"));
|
|
QScopedPointer<QObject> obj(
|
|
component.createWithInitialProperties({{"o", QVariant::fromValue(&o)}}));
|
|
QVERIFY(obj);
|
|
|
|
QCOMPARE(o.variantMap().size(), 0);
|
|
QCOMPARE(o.variantMap().isEmpty(), true);
|
|
}
|
|
|
|
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);
|
|
QScopedPointer<QObject> obj(component.create());
|
|
QVERIFY(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"));
|
|
QScopedPointer<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());
|
|
}
|
|
|
|
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", 1);
|
|
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());
|
|
QVERIFY2(root, qPrintable(component.errorString()));
|
|
|
|
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");
|
|
qmlRegisterType<MyReplaceIfNotDefaultBehaviorContainer>("Test",1,0,"MyReplaceIfNotDefaultBehaviorContainer");
|
|
qmlRegisterType<MyAlwaysReplaceBehaviorContainer>("Test",1,0,"MyAlwaysReplaceBehaviorContainer");
|
|
qmlRegisterType<ListHolder>("Test", 1, 0, "ListHolder");
|
|
}
|
|
|
|
// 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());
|
|
}
|
|
|
|
void tst_qqmlproperty::signalExpressionWithoutObject()
|
|
{
|
|
QQmlProperty invalid;
|
|
QQmlPropertyPrivate::setSignalExpression(invalid, nullptr);
|
|
QQmlBoundSignalExpression *expr = QQmlPropertyPrivate::signalExpression(invalid);
|
|
QVERIFY(!expr);
|
|
}
|
|
|
|
void tst_qqmlproperty::dontRemoveQPropertyBinding()
|
|
{
|
|
QObject object;
|
|
QQmlProperty objectName(&object, "objectName");
|
|
QVERIFY(objectName.isBindable());
|
|
QProperty<QString> name("hello");
|
|
object.bindableObjectName().setBinding(Qt::makePropertyBinding(name));
|
|
QVERIFY(object.bindableObjectName().hasBinding());
|
|
|
|
// A write with DontRemoveBinding preserves the binding
|
|
QQmlPropertyPrivate::write(objectName, u"goodbye"_s, QQmlPropertyData::DontRemoveBinding);
|
|
QVERIFY(object.bindableObjectName().hasBinding());
|
|
// but changes the value
|
|
QCOMPARE(object.objectName(), u"goodbye"_s);
|
|
// subsequent binding evaluations change the value again
|
|
name = u"hello, again"_s;
|
|
QCOMPARE(object.objectName(), name.value());
|
|
|
|
// The binding is only preserved by the write which had DontRemoveBinding set
|
|
// any further write will remove the binding
|
|
QQmlPropertyPrivate::write(objectName, u"goodbye"_s, QQmlPropertyData::WriteFlags{});
|
|
QCOMPARE(object.objectName(), u"goodbye"_s);
|
|
QVERIFY(!object.bindableObjectName().hasBinding());
|
|
}
|
|
|
|
void tst_qqmlproperty::compatResolveUrls()
|
|
{
|
|
QQmlEngine engine;
|
|
QQmlComponent c(&engine);
|
|
c.setData(R"(
|
|
import QtQml
|
|
QtObject {
|
|
property url a: "relative/url.png"
|
|
}
|
|
)", QUrl(QStringLiteral("qrc:/some/resource/path.qml")));
|
|
QVERIFY(c.isReady());
|
|
QScopedPointer<QObject> o(c.create());
|
|
QVERIFY(!o.isNull());
|
|
|
|
if (qEnvironmentVariableIsSet("QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT")) {
|
|
QCOMPARE(qvariant_cast<QUrl>(o->property("a")),
|
|
QUrl(QStringLiteral("qrc:/some/resource/relative/url.png")));
|
|
return;
|
|
}
|
|
|
|
QCOMPARE(qvariant_cast<QUrl>(o->property("a")), QUrl(QStringLiteral("relative/url.png")));
|
|
|
|
#ifdef Q_OS_ANDROID
|
|
QSKIP("Can't start QProcess to run a custom user binary on Android");
|
|
#endif
|
|
|
|
#if QT_CONFIG(process)
|
|
QProcess process;
|
|
process.setProgram(QCoreApplication::applicationFilePath());
|
|
process.setEnvironment(QProcess::systemEnvironment()
|
|
+ QStringList(u"QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT=1"_s));
|
|
process.setArguments({QStringLiteral("compatResolveUrls")});
|
|
process.start();
|
|
QVERIFY(process.waitForFinished());
|
|
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
|
|
QCOMPARE(process.exitCode(), 0);
|
|
#else
|
|
QSKIP("Testing the QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT "
|
|
"environment variable requires QProcess.");
|
|
#endif
|
|
}
|
|
|
|
void tst_qqmlproperty::initFlags_data()
|
|
{
|
|
QTest::addColumn<bool>("passObject");
|
|
QTest::addColumn<QString>("name");
|
|
QTest::addColumn<QQmlPropertyPrivate::InitFlags>("flags");
|
|
|
|
const QString names[] = {
|
|
QStringLiteral("foo"),
|
|
QStringLiteral("self.foo"),
|
|
QStringLiteral("onFoo"),
|
|
QStringLiteral("self.onFoo"),
|
|
QStringLiteral("bar"),
|
|
QStringLiteral("self.bar"),
|
|
QStringLiteral("abar"),
|
|
QStringLiteral("self.abar"),
|
|
};
|
|
|
|
const QQmlPropertyPrivate::InitFlags flagSets[] = {
|
|
QQmlPropertyPrivate::InitFlag::None,
|
|
QQmlPropertyPrivate::InitFlag::AllowId,
|
|
QQmlPropertyPrivate::InitFlag::AllowSignal,
|
|
QQmlPropertyPrivate::InitFlag::AllowId | QQmlPropertyPrivate::InitFlag::AllowSignal,
|
|
};
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
const bool passObject = (i != 0);
|
|
for (const QString &name : names) {
|
|
for (const auto &flagSet : flagSets) {
|
|
const QString rowName = QStringLiteral("%1,%2,%3")
|
|
.arg(passObject).arg(name).arg(flagSet.toInt());
|
|
QTest::addRow("%s", qPrintable(rowName)) << passObject << name << flagSet;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void tst_qqmlproperty::initFlags()
|
|
{
|
|
QFETCH(bool, passObject);
|
|
QFETCH(QString, name);
|
|
QFETCH(QQmlPropertyPrivate::InitFlags, flags);
|
|
|
|
QQmlEngine engine;
|
|
QQmlComponent c(&engine);
|
|
c.setData(R"(
|
|
import QtQml
|
|
QtObject {
|
|
id: self
|
|
signal foo()
|
|
property int bar: 12
|
|
property alias abar: self.bar
|
|
}
|
|
)", QUrl());
|
|
QVERIFY(c.isReady());
|
|
QScopedPointer<QObject> o(c.create());
|
|
QVERIFY(!o.isNull());
|
|
|
|
QQmlRefPointer<QQmlContextData> context = QQmlContextData::get(qmlContext(o.data()));
|
|
|
|
const QQmlProperty property = QQmlPropertyPrivate::create(
|
|
passObject ? o.data() : nullptr, name, context, flags);
|
|
|
|
const bool usesId = name.startsWith(QStringLiteral("self."));
|
|
const bool hasSignal = name.endsWith(QStringLiteral("foo"));
|
|
if (!passObject && !usesId) {
|
|
QVERIFY(!property.isValid());
|
|
} else if (passObject && usesId) {
|
|
QVERIFY(!property.isValid());
|
|
} else if (usesId && !(flags & QQmlPropertyPrivate::InitFlag::AllowId)) {
|
|
QVERIFY(!property.isValid());
|
|
} else if (hasSignal && !(flags & QQmlPropertyPrivate::InitFlag::AllowSignal)) {
|
|
QVERIFY(!property.isValid());
|
|
} else {
|
|
QVERIFY(property.isValid());
|
|
if (name.endsWith(QStringLiteral("bar"))) {
|
|
QVERIFY(property.isProperty());
|
|
QCOMPARE(property.name(), usesId ? name.mid(strlen("self.")) : name);
|
|
QCOMPARE(property.propertyMetaType(), QMetaType::fromType<int>());
|
|
} else {
|
|
QVERIFY(property.isSignalProperty());
|
|
QCOMPARE(property.name(), QStringLiteral("onFoo"));
|
|
QVERIFY(!property.propertyMetaType().isValid());
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void tst_qqmlproperty::constructFromPlainMetaObject()
|
|
{
|
|
QScopedPointer<PropertyObject> obj(new PropertyObject);
|
|
|
|
QQmlData *data = QQmlData::get(obj.data());
|
|
QVERIFY(data == nullptr);
|
|
|
|
QQmlProperty prop(obj.data(), "rectProperty");
|
|
QVERIFY(prop.isValid());
|
|
QVERIFY(prop.isProperty());
|
|
QCOMPARE(prop.propertyMetaType(), QMetaType::fromType<QRect>());
|
|
|
|
QQmlProperty sig(obj.data(), "onOddlyNamedNotifySignal");
|
|
QVERIFY(sig.isValid());
|
|
QVERIFY(sig.isSignalProperty());
|
|
QVERIFY(!sig.propertyMetaType().isValid());
|
|
|
|
data = QQmlData::get(obj.data());
|
|
QVERIFY(data == nullptr);
|
|
}
|
|
|
|
void tst_qqmlproperty::bindToNonQObjectTarget()
|
|
{
|
|
QQmlEngine engine;
|
|
const QUrl url = testFileUrl("bindToNonQObjectTarget.qml");
|
|
QQmlComponent component(&engine, url);
|
|
|
|
// Yes, we can still create the component. The result of the script expression will only be
|
|
// known once it's executed.
|
|
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
|
|
|
|
QTest::ignoreMessage(QtWarningMsg,
|
|
qPrintable(url.toString() + ":14:7: Unable to assign QFont to QObject*"));
|
|
QScopedPointer<QObject> o(component.create());
|
|
QVERIFY(!o.isNull());
|
|
}
|
|
|
|
void tst_qqmlproperty::assignVariantList()
|
|
{
|
|
QQmlEngine engine;
|
|
QQmlComponent component(&engine, testFileUrl("assignVariantList.qml"));
|
|
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
|
|
QScopedPointer<QObject> o(component.create());
|
|
QVERIFY(!o.isNull());
|
|
ListHolder *holder = qobject_cast<ListHolder *>(o.data());
|
|
const QList<double> doubleList = {1.1, 2.2, 3.3, 11, 5.25, 11};
|
|
QCOMPARE(holder->doubleList(), doubleList);
|
|
}
|
|
|
|
void tst_qqmlproperty::listAssignmentSignals()
|
|
{
|
|
QQmlEngine engine;
|
|
QQmlComponent component(&engine, testFileUrl("listAssignmentSignals.qml"));
|
|
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
|
|
QScopedPointer<QObject> root(component.create());
|
|
QVERIFY(!root.isNull());
|
|
|
|
QCOMPARE(root->property("signalCounter").toInt(), 1);
|
|
QMetaObject::invokeMethod(root.get(), "assignList");
|
|
QCOMPARE(root->property("signalCounter").toInt(), 2);
|
|
}
|
|
|
|
QTEST_MAIN(tst_qqmlproperty)
|
|
|
|
#include "tst_qqmlproperty.moc"
|