ListModel: Use PersistentValue to keep track of objects
This is the simplest way to prevent the garbage collector from
destroying the object while at the same time allowing a manual
destruction.
Task-number: QTBUG-95895
Task-number: QTBUG-96167
Fixes: QTBUG-91390
Change-Id: Ic3f3146bc555991068ce3367971e050f745d235d
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit 718f3469f6
)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
5e66a8c4fd
commit
3891106752
|
@ -98,7 +98,7 @@ const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::R
|
||||||
sizeof(double),
|
sizeof(double),
|
||||||
sizeof(bool),
|
sizeof(bool),
|
||||||
sizeof(ListModel *),
|
sizeof(ListModel *),
|
||||||
sizeof(ListElement::GuardedQObjectPointer),
|
sizeof(QV4::PersistentValue),
|
||||||
sizeof(QVariantMap),
|
sizeof(QVariantMap),
|
||||||
sizeof(QDateTime),
|
sizeof(QDateTime),
|
||||||
sizeof(QUrl),
|
sizeof(QUrl),
|
||||||
|
@ -618,10 +618,9 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles)
|
||||||
roleIndex = e->setFunctionProperty(r, jsv);
|
roleIndex = e->setFunctionProperty(r, jsv);
|
||||||
} else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
|
} else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
|
||||||
if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
|
if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
|
||||||
QObject *o = wrapper->object();
|
|
||||||
const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
|
const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
|
||||||
if (role.type == ListLayout::Role::QObject)
|
if (role.type == ListLayout::Role::QObject)
|
||||||
roleIndex = e->setQObjectProperty(role, o);
|
roleIndex = e->setQObjectProperty(role, wrapper);
|
||||||
} else if (QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
|
} else if (QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
|
||||||
o->asReturnedValue(), QMetaType::fromType<QUrl>(), true);
|
o->asReturnedValue(), QMetaType::fromType<QUrl>(), true);
|
||||||
maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
|
maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
|
||||||
|
@ -710,10 +709,9 @@ void ListModel::set(int elementIndex, QV4::Object *object, ListModel::SetElement
|
||||||
}
|
}
|
||||||
} else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
|
} else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
|
||||||
if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
|
if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
|
||||||
QObject *o = wrapper->object();
|
|
||||||
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
|
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
|
||||||
if (r.type == ListLayout::Role::QObject)
|
if (r.type == ListLayout::Role::QObject)
|
||||||
e->setQObjectPropertyFast(r, o);
|
e->setQObjectPropertyFast(r, wrapper);
|
||||||
} else {
|
} else {
|
||||||
QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
|
QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
|
||||||
o->asReturnedValue(), QMetaType::fromType<QUrl>(), true);
|
o->asReturnedValue(), QMetaType::fromType<QUrl>(), true);
|
||||||
|
@ -838,12 +836,11 @@ StringOrTranslation *ListElement::getStringProperty(const ListLayout::Role &role
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject *ListElement::getQObjectProperty(const ListLayout::Role &role)
|
QV4::QObjectWrapper *ListElement::getQObjectProperty(const ListLayout::Role &role)
|
||||||
{
|
{
|
||||||
char *mem = getPropertyMemory(role);
|
char *mem = getPropertyMemory(role);
|
||||||
GuardedQObjectPointer *o
|
QV4::PersistentValue *g = reinterpret_cast<QV4::PersistentValue *>(mem);
|
||||||
= reinterpret_cast<GuardedQObjectPointer *>(mem);
|
return g->as<QV4::QObjectWrapper>();
|
||||||
return o->data();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role)
|
QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role)
|
||||||
|
@ -890,13 +887,13 @@ QJSValue *ListElement::getFunctionProperty(const ListLayout::Role &role)
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
ListElement::GuardedQObjectPointer *
|
QV4::PersistentValue *
|
||||||
ListElement::getGuardProperty(const ListLayout::Role &role)
|
ListElement::getGuardProperty(const ListLayout::Role &role)
|
||||||
{
|
{
|
||||||
char *mem = getPropertyMemory(role);
|
char *mem = getPropertyMemory(role);
|
||||||
|
|
||||||
bool existingGuard = false;
|
bool existingGuard = false;
|
||||||
for (size_t i = 0; i < sizeof(GuardedQObjectPointer);
|
for (size_t i = 0; i < sizeof(QV4::PersistentValue);
|
||||||
++i) {
|
++i) {
|
||||||
if (mem[i] != 0) {
|
if (mem[i] != 0) {
|
||||||
existingGuard = true;
|
existingGuard = true;
|
||||||
|
@ -904,12 +901,12 @@ ListElement::getGuardProperty(const ListLayout::Role &role)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GuardedQObjectPointer *o = nullptr;
|
QV4::PersistentValue *g = nullptr;
|
||||||
|
|
||||||
if (existingGuard)
|
if (existingGuard)
|
||||||
o = reinterpret_cast<GuardedQObjectPointer *>(mem);
|
g = reinterpret_cast<QV4::PersistentValue *>(mem);
|
||||||
|
|
||||||
return o;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
ListModel *ListElement::getListProperty(const ListLayout::Role &role)
|
ListModel *ListElement::getListProperty(const ListLayout::Role &role)
|
||||||
|
@ -965,12 +962,8 @@ QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListMo
|
||||||
break;
|
break;
|
||||||
case ListLayout::Role::QObject:
|
case ListLayout::Role::QObject:
|
||||||
{
|
{
|
||||||
GuardedQObjectPointer *guard =
|
QV4::PersistentValue *guard = reinterpret_cast<QV4::PersistentValue *>(mem);
|
||||||
reinterpret_cast<GuardedQObjectPointer *>(
|
data = QVariant::fromValue(guard->as<QV4::QObjectWrapper>()->object());
|
||||||
mem);
|
|
||||||
QObject *object = guard->data();
|
|
||||||
if (object)
|
|
||||||
data = QVariant::fromValue(object);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ListLayout::Role::VariantMap:
|
case ListLayout::Role::VariantMap:
|
||||||
|
@ -1082,67 +1075,17 @@ int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m)
|
||||||
return roleIndex;
|
return roleIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
int ListElement::setQObjectProperty(const ListLayout::Role &role, QV4::QObjectWrapper *o)
|
||||||
restoreQObjectOwnership(ListElement::GuardedQObjectPointer *pointer)
|
|
||||||
{
|
|
||||||
if (QObject *o = pointer->data()) {
|
|
||||||
QQmlData *data = QQmlData::get(o, false);
|
|
||||||
Q_ASSERT(data);
|
|
||||||
|
|
||||||
// Only restore the previous state if the object hasn't become explicitly
|
|
||||||
// owned
|
|
||||||
if (!data->explicitIndestructibleSet)
|
|
||||||
data->indestructible = (pointer->tag() & ListElement::Indestructible);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setQObjectOwnership(char *mem, QObject *o)
|
|
||||||
{
|
|
||||||
QQmlData *ddata = QQmlData::get(o, false);
|
|
||||||
const int ownership = (!ddata || ddata->indestructible ? ListElement::Indestructible : 0)
|
|
||||||
| (ddata && ddata->explicitIndestructibleSet ? ListElement::ExplicitlySet : 0);
|
|
||||||
|
|
||||||
// If ddata didn't exist above, force its creation now
|
|
||||||
if (!ddata)
|
|
||||||
ddata = QQmlData::get(o, true);
|
|
||||||
|
|
||||||
if (!ddata->explicitIndestructibleSet)
|
|
||||||
ddata->indestructible = ownership != 0;
|
|
||||||
|
|
||||||
new (mem) ListElement::GuardedQObjectPointer(
|
|
||||||
o, static_cast<ListElement::ObjectIndestructible>(ownership));
|
|
||||||
}
|
|
||||||
|
|
||||||
int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o)
|
|
||||||
{
|
{
|
||||||
int roleIndex = -1;
|
int roleIndex = -1;
|
||||||
|
|
||||||
if (role.type == ListLayout::Role::QObject) {
|
if (role.type == ListLayout::Role::QObject) {
|
||||||
char *mem = getPropertyMemory(role);
|
char *mem = getPropertyMemory(role);
|
||||||
GuardedQObjectPointer *g =
|
if (isMemoryUsed<QVariantMap>(mem))
|
||||||
reinterpret_cast<GuardedQObjectPointer *>(mem);
|
reinterpret_cast<QV4::PersistentValue *>(mem)->set(o->engine(), *o);
|
||||||
bool existingGuard = false;
|
else
|
||||||
for (size_t i = 0; i < sizeof(GuardedQObjectPointer);
|
new (mem) QV4::PersistentValue(o->engine(), o);
|
||||||
++i) {
|
roleIndex = role.index;
|
||||||
if (mem[i] != 0) {
|
|
||||||
existingGuard = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool changed;
|
|
||||||
if (existingGuard) {
|
|
||||||
changed = g->data() != o;
|
|
||||||
if (changed)
|
|
||||||
restoreQObjectOwnership(g);
|
|
||||||
g->~GuardedQObjectPointer();
|
|
||||||
} else {
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
setQObjectOwnership(mem, o);
|
|
||||||
|
|
||||||
if (changed)
|
|
||||||
roleIndex = role.index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return roleIndex;
|
return roleIndex;
|
||||||
|
@ -1275,11 +1218,10 @@ void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b)
|
||||||
*value = b;
|
*value = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QObject *o)
|
void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QV4::QObjectWrapper *o)
|
||||||
{
|
{
|
||||||
char *mem = getPropertyMemory(role);
|
char *mem = getPropertyMemory(role);
|
||||||
|
new (mem) QV4::PersistentValue(o->engine(), o);
|
||||||
setQObjectOwnership(mem, o);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m)
|
void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m)
|
||||||
|
@ -1396,7 +1338,7 @@ QVector<int> ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElem
|
||||||
break;
|
break;
|
||||||
case ListLayout::Role::QObject:
|
case ListLayout::Role::QObject:
|
||||||
{
|
{
|
||||||
QObject *object = src->getQObjectProperty(srcRole);
|
QV4::QObjectWrapper *object = src->getQObjectProperty(srcRole);
|
||||||
roleIndex = target->setQObjectProperty(targetRole, object);
|
roleIndex = target->setQObjectProperty(targetRole, object);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1451,14 +1393,8 @@ void ListElement::destroy(ListLayout *layout)
|
||||||
break;
|
break;
|
||||||
case ListLayout::Role::QObject:
|
case ListLayout::Role::QObject:
|
||||||
{
|
{
|
||||||
GuardedQObjectPointer *guard =
|
if (QV4::PersistentValue *guard = getGuardProperty(r))
|
||||||
getGuardProperty(r);
|
guard->~PersistentValue();
|
||||||
|
|
||||||
if (guard) {
|
|
||||||
restoreQObjectOwnership(guard);
|
|
||||||
|
|
||||||
guard->~GuardedQObjectPointer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ListLayout::Role::VariantMap:
|
case ListLayout::Role::VariantMap:
|
||||||
|
@ -1595,8 +1531,7 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d
|
||||||
QV4::ScopedObject o(scope, d);
|
QV4::ScopedObject o(scope, d);
|
||||||
QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>();
|
QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>();
|
||||||
if (role.type == ListLayout::Role::QObject && wrapper) {
|
if (role.type == ListLayout::Role::QObject && wrapper) {
|
||||||
QObject *o = wrapper->object();
|
roleIndex = setQObjectProperty(role, wrapper);
|
||||||
roleIndex = setQObjectProperty(role, o);
|
|
||||||
} else if (role.type == ListLayout::Role::VariantMap) {
|
} else if (role.type == ListLayout::Role::VariantMap) {
|
||||||
roleIndex = setVariantMapProperty(role, o);
|
roleIndex = setVariantMapProperty(role, o);
|
||||||
} else if (role.type == ListLayout::Role::Url) {
|
} else if (role.type == ListLayout::Role::Url) {
|
||||||
|
|
|
@ -245,43 +245,6 @@ public:
|
||||||
enum ObjectIndestructible { Indestructible = 1, ExplicitlySet = 2 };
|
enum ObjectIndestructible { Indestructible = 1, ExplicitlySet = 2 };
|
||||||
enum { BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelNodeMetaObject *) };
|
enum { BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelNodeMetaObject *) };
|
||||||
|
|
||||||
// This is a basic guarded QObject pointer, with tag. It cannot be copied or moved.
|
|
||||||
class GuardedQObjectPointer
|
|
||||||
{
|
|
||||||
Q_DISABLE_COPY_MOVE(GuardedQObjectPointer)
|
|
||||||
|
|
||||||
using RefCountData = QtSharedPointer::ExternalRefCountData;
|
|
||||||
using Storage = QTaggedPointer<QObject, ObjectIndestructible>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
GuardedQObjectPointer(QObject *o, ObjectIndestructible ownership)
|
|
||||||
: storage(o, ownership)
|
|
||||||
, refCount(o ? RefCountData::getAndRef(o) : nullptr)
|
|
||||||
{}
|
|
||||||
|
|
||||||
~GuardedQObjectPointer()
|
|
||||||
{
|
|
||||||
if (refCount && !refCount->weakref.deref())
|
|
||||||
delete refCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject *data() const
|
|
||||||
{
|
|
||||||
return (refCount == nullptr || refCount->strongref.loadRelaxed() == 0)
|
|
||||||
? nullptr
|
|
||||||
: storage.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectIndestructible tag() const
|
|
||||||
{
|
|
||||||
return storage.tag();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Storage storage;
|
|
||||||
RefCountData *refCount = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
ListElement();
|
ListElement();
|
||||||
ListElement(int existingUid);
|
ListElement(int existingUid);
|
||||||
~ListElement();
|
~ListElement();
|
||||||
|
@ -300,7 +263,7 @@ private:
|
||||||
int setDoubleProperty(const ListLayout::Role &role, double n);
|
int setDoubleProperty(const ListLayout::Role &role, double n);
|
||||||
int setBoolProperty(const ListLayout::Role &role, bool b);
|
int setBoolProperty(const ListLayout::Role &role, bool b);
|
||||||
int setListProperty(const ListLayout::Role &role, ListModel *m);
|
int setListProperty(const ListLayout::Role &role, ListModel *m);
|
||||||
int setQObjectProperty(const ListLayout::Role &role, QObject *o);
|
int setQObjectProperty(const ListLayout::Role &role, QV4::QObjectWrapper *o);
|
||||||
int setVariantMapProperty(const ListLayout::Role &role, QV4::Object *o);
|
int setVariantMapProperty(const ListLayout::Role &role, QV4::Object *o);
|
||||||
int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m);
|
int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m);
|
||||||
int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt);
|
int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt);
|
||||||
|
@ -311,7 +274,7 @@ private:
|
||||||
void setStringPropertyFast(const ListLayout::Role &role, const QString &s);
|
void setStringPropertyFast(const ListLayout::Role &role, const QString &s);
|
||||||
void setDoublePropertyFast(const ListLayout::Role &role, double n);
|
void setDoublePropertyFast(const ListLayout::Role &role, double n);
|
||||||
void setBoolPropertyFast(const ListLayout::Role &role, bool b);
|
void setBoolPropertyFast(const ListLayout::Role &role, bool b);
|
||||||
void setQObjectPropertyFast(const ListLayout::Role &role, QObject *o);
|
void setQObjectPropertyFast(const ListLayout::Role &role, QV4::QObjectWrapper *o);
|
||||||
void setListPropertyFast(const ListLayout::Role &role, ListModel *m);
|
void setListPropertyFast(const ListLayout::Role &role, ListModel *m);
|
||||||
void setVariantMapFast(const ListLayout::Role &role, QV4::Object *o);
|
void setVariantMapFast(const ListLayout::Role &role, QV4::Object *o);
|
||||||
void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt);
|
void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt);
|
||||||
|
@ -323,8 +286,8 @@ private:
|
||||||
QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng);
|
QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng);
|
||||||
ListModel *getListProperty(const ListLayout::Role &role);
|
ListModel *getListProperty(const ListLayout::Role &role);
|
||||||
StringOrTranslation *getStringProperty(const ListLayout::Role &role);
|
StringOrTranslation *getStringProperty(const ListLayout::Role &role);
|
||||||
QObject *getQObjectProperty(const ListLayout::Role &role);
|
QV4::QObjectWrapper *getQObjectProperty(const ListLayout::Role &role);
|
||||||
GuardedQObjectPointer *getGuardProperty(const ListLayout::Role &role);
|
QV4::PersistentValue *getGuardProperty(const ListLayout::Role &role);
|
||||||
QVariantMap *getVariantMapProperty(const ListLayout::Role &role);
|
QVariantMap *getVariantMapProperty(const ListLayout::Role &role);
|
||||||
QDateTime *getDateTimeProperty(const ListLayout::Role &role);
|
QDateTime *getDateTimeProperty(const ListLayout::Role &role);
|
||||||
QUrl *getUrlProperty(const ListLayout::Role &role);
|
QUrl *getUrlProperty(const ListLayout::Role &role);
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import QtQml
|
||||||
|
import QtQml.Models
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: filesModel
|
||||||
|
property Component testComponent: Component {
|
||||||
|
id: testComponent
|
||||||
|
QtObject {
|
||||||
|
required property string name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Component.onCompleted: {
|
||||||
|
filesModel.clear()
|
||||||
|
for(let i = 0; i < 10; i++) {
|
||||||
|
filesModel.append({
|
||||||
|
path: testComponent.createObject(null, { name: "" + i })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
gc()
|
||||||
|
}
|
||||||
|
}
|
|
@ -118,6 +118,7 @@ private slots:
|
||||||
void destroyComponentObject();
|
void destroyComponentObject();
|
||||||
void objectOwnershipFlip();
|
void objectOwnershipFlip();
|
||||||
void enumsInListElement();
|
void enumsInListElement();
|
||||||
|
void protectQObjectFromGC();
|
||||||
};
|
};
|
||||||
|
|
||||||
bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object)
|
bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object)
|
||||||
|
@ -1855,7 +1856,7 @@ void tst_qqmllistmodel::destroyComponentObject()
|
||||||
Q_RETURN_ARG(QVariant, retVal));
|
Q_RETURN_ARG(QVariant, retVal));
|
||||||
QVERIFY(retVal.toBool());
|
QVERIFY(retVal.toBool());
|
||||||
QTRY_VERIFY(created.isNull());
|
QTRY_VERIFY(created.isNull());
|
||||||
QTRY_VERIFY(list->get(0).property("obj").isUndefined());
|
QTRY_VERIFY(list->get(0).property("obj").isNull());
|
||||||
QCOMPARE(list->count(), 1);
|
QCOMPARE(list->count(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1909,6 +1910,25 @@ void tst_qqmllistmodel::enumsInListElement()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_qqmllistmodel::protectQObjectFromGC()
|
||||||
|
{
|
||||||
|
QQmlEngine engine;
|
||||||
|
QQmlComponent component(&engine, testFileUrl("protectQObjectFromGC.qml"));
|
||||||
|
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
|
||||||
|
QScopedPointer<QObject> root(component.create());
|
||||||
|
QVERIFY(!root.isNull());
|
||||||
|
|
||||||
|
QQmlListModel *listModel = qobject_cast<QQmlListModel *>(root.data());
|
||||||
|
QVERIFY(listModel);
|
||||||
|
QCOMPARE(listModel->count(), 10);
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; ++i) {
|
||||||
|
QObject *element = qjsvalue_cast<QObject *>(listModel->get(i).property("path"));
|
||||||
|
QVERIFY(element);
|
||||||
|
QCOMPARE(element->property("name").toString(), QString::number(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_qqmllistmodel)
|
QTEST_MAIN(tst_qqmllistmodel)
|
||||||
|
|
||||||
#include "tst_qqmllistmodel.moc"
|
#include "tst_qqmllistmodel.moc"
|
||||||
|
|
Loading…
Reference in New Issue