1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28#include "../../shared/util.h"
29#include "../shared/visualtestutil.h"
30#include "../shared/viewtestutil.h"
31
32#include <qtest.h>
33#include <QtCore/qregularexpression.h>
34#include <QtTest/QSignalSpy>
35#include <QtQml/qqmlengine.h>
36#include <QtQml/qqmlcomponent.h>
37#include <QtQml/qqmlcontext.h>
38#include <QtQml/qqmlexpression.h>
39#include <QtQml/qqmlincubator.h>
40#include <QtQuick/qquickview.h>
41#include <private/qquicklistview_p.h>
42#include <QtQuick/private/qquicktext_p.h>
43#include <QtQmlModels/private/qqmldelegatemodel_p.h>
44#include <private/qqmlvaluetype_p.h>
45#include <private/qqmlchangeset_p.h>
46#include <private/qqmlengine_p.h>
47#include <math.h>
48#include <QtGui/qstandarditemmodel.h>
49
50using namespace QQuickVisualTestUtil;
51using namespace QQuickViewTestUtil;
52
53template <typename T, int N> int lengthOf(const T (&)[N]) { return N; }
54
55static void initStandardTreeModel(QStandardItemModel *model)
56{
57 QStandardItem *item;
58 item = new QStandardItem(QLatin1String("Row 1 Item"));
59 model->insertRow(arow: 0, aitem: item);
60
61 item = new QStandardItem(QLatin1String("Row 2 Item"));
62 item->setCheckable(true);
63 model->insertRow(arow: 1, aitem: item);
64
65 QStandardItem *childItem = new QStandardItem(QLatin1String("Row 2 Child Item"));
66 item->setChild(arow: 0, aitem: childItem);
67
68 item = new QStandardItem(QLatin1String("Row 3 Item"));
69 item->setIcon(QIcon());
70 model->insertRow(arow: 2, aitem: item);
71}
72
73class SingleRoleModel : public QAbstractItemModel
74{
75 Q_OBJECT
76 Q_PROPERTY(QStringList values READ getList WRITE setList)
77public:
78 struct Branch;
79 struct Node {
80 Node(const QString &display = QString()) : branch(nullptr), display(display) {}
81 Branch *branch;
82 QString display;
83 };
84
85 struct Branch {
86 Branch(Branch *parent = nullptr) : parent(parent) {}
87 ~Branch() { foreach (const Node &child, children) delete child.branch; }
88 int indexOf(Branch *branch) const {
89 for (int i = 0; i < children.count(); ++i) {
90 if (children.at(i).branch == branch)
91 return i;
92 }
93 return -1;
94 }
95 Branch *parent;
96 QVector<Node> children;
97
98 };
99
100 SingleRoleModel(const QStringList &list = QStringList(), const QByteArray &role = "name", QObject *parent = nullptr)
101 : QAbstractItemModel(parent), m_role(role)
102 {
103 foreach (const QString &string, list)
104 trunk.children.append(t: Node(string));
105 }
106 ~SingleRoleModel() {}
107
108 QHash<int,QByteArray> roleNames() const
109 {
110 QHash<int,QByteArray> roles;
111 roles.insert(key: Qt::DisplayRole, value: m_role);
112 return roles;
113 }
114
115 Branch *branchForIndex(const QModelIndex &index) const {
116 return index.isValid()
117 ? static_cast<Branch *>(index.internalPointer())->children.at(i: index.row()).branch
118 : const_cast<Branch *>(&trunk);
119 }
120
121 Branch *createBranchForIndex(const QModelIndex &index) const {
122 if (index.isValid()) {
123 Branch * const parentBranch = static_cast<Branch *>(index.internalPointer());
124 Node &node = parentBranch->children[index.row()];
125 if (!node.branch)
126 node.branch = new Branch(parentBranch);
127 return node.branch;
128 } else {
129 return const_cast<Branch *>(&trunk);
130 }
131 }
132
133 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const {
134 if (row < 0 || column != 0)
135 return QModelIndex();
136 Branch * const branch = branchForIndex(index: parent);
137 return branch && row < branch->children.count()
138 ? createIndex(arow: row, acolumn: column, adata: branch)
139 : QModelIndex();
140 }
141
142 QModelIndex parent(const QModelIndex &child) const {
143 Branch * const branch = static_cast<Branch *>(child.internalPointer());
144 return branch->parent
145 ? createIndex(arow: branch->parent->indexOf(branch), acolumn: 0, adata: branch->parent)
146 : QModelIndex();
147 }
148
149 int rowCount(const QModelIndex &parent) const {
150 Branch * const branch = branchForIndex(index: parent);
151 return branch ? branch->children.count() : 0;
152 }
153
154 int columnCount(const QModelIndex &parent) const {
155 Branch * const branch = branchForIndex(index: parent);
156 return branch ? 1 : 0;
157 }
158
159 QVariant data(const QModelIndex &index, int role) const {
160 return index.isValid() && role == Qt::DisplayRole
161 ? static_cast<Branch *>(index.internalPointer())->children.at(i: index.row()).display
162 : QVariant();
163 }
164
165 void insert(const QModelIndex &parent, int index, const QStringList &data) {
166 beginInsertRows(parent, first: index, last: index + data.count() - 1);
167 Branch * const branch = createBranchForIndex(index: parent);
168 for (int i = 0; i < data.count(); ++i)
169 branch->children.insert(i: index + i, t: Node(data.at(i)));
170 endInsertRows();
171 }
172
173 void remove(const QModelIndex &parent, int index, int count) {
174 beginRemoveRows(parent, first: index, last: index + count - 1);
175 Branch * const branch = branchForIndex(index: parent);
176 for (int i = 0; i < count; ++i) {
177 delete branch->children.at(i: index).branch;
178 branch->children.remove(i: index);
179 }
180 endRemoveRows();
181 }
182
183 void move(const QModelIndex &fromParent, int from, const QModelIndex &toParent, int to, int count) {
184 beginMoveRows(sourceParent: fromParent, sourceFirst: from, sourceLast: from + count - 1, destinationParent: toParent, destinationRow: to);
185 Branch * const fromBranch = branchForIndex(index: fromParent);
186 Branch * const toBranch = createBranchForIndex(index: toParent);
187
188 if (fromBranch == toBranch) {
189 qquickmodelviewstestutil_move(from, to, n: count, items: &fromBranch->children);
190 } else {
191 for (int i = 0; i < count; ++i) {
192 Node node = fromBranch->children.at(i: from);
193 fromBranch->children.remove(i: from);
194 if (node.branch)
195 node.branch->parent = toBranch;
196 toBranch->children.insert(i: to + i, t: node);
197
198 }
199 }
200 endMoveRows();
201 }
202
203 void emitMove(int sourceFirst, int sourceLast, int destinationChild) {
204 emit beginMoveRows(sourceParent: QModelIndex(), sourceFirst, sourceLast, destinationParent: QModelIndex(), destinationRow: destinationChild);
205 emit endMoveRows();
206 }
207
208 QStringList getList() const {
209 QStringList list;
210 foreach (const Node &node, trunk.children)
211 list.append(t: node.display);
212 return list;
213 }
214
215 void setList(const QStringList &l) {
216 if (trunk.children.count() > 0) {
217 beginRemoveRows(parent: QModelIndex(), first: 0, last: trunk.children.count() - 1);
218 foreach (const Node &child, trunk.children) delete child.branch;
219 trunk.children.clear();
220 endRemoveRows();
221 }
222 if (l.count() > 0) {
223 beginInsertRows(parent: QModelIndex(), first: 0, last: l.count() -1);
224 foreach (const QString &string, l)
225 trunk.children.append(t: Node(string));
226 endInsertRows();
227 }
228 }
229
230 QString at(int index) const { return trunk.children.at(i: index).display; }
231
232public slots:
233 void set(int idx, QString string) {
234 trunk.children[idx].display = string;
235 emit dataChanged(topLeft: createIndex(arow: idx, acolumn: 0, adata: &trunk), bottomRight: createIndex(arow: idx, acolumn: 0, adata: &trunk));
236 }
237
238private:
239 const QByteArray m_role;
240 Branch trunk;
241};
242
243class StandardItem : public QObject, public QStandardItem
244{
245 Q_OBJECT
246 Q_PROPERTY(QString text READ readText WRITE setText)
247
248public:
249 QString readText() const { return text(); }
250 void writeText(const QString &text) { setText(text); }
251};
252
253class StandardItemModel : public QStandardItemModel
254{
255 Q_OBJECT
256 Q_PROPERTY(QQmlListProperty<StandardItem> items READ items CONSTANT)
257 Q_CLASSINFO("DefaultProperty", "items")
258public:
259 QQmlListProperty<StandardItem> items() { return QQmlListProperty<StandardItem>(this, nullptr, append, nullptr, nullptr, nullptr); }
260
261 static void append(QQmlListProperty<StandardItem> *property, StandardItem *item)
262 {
263 static_cast<QStandardItemModel *>(property->object)->appendRow(aitem: item);
264 }
265};
266
267class DataSubObject : public QObject
268{
269 Q_OBJECT
270
271 Q_PROPERTY(QString subName READ subName WRITE setSubName NOTIFY subNameChanged)
272
273public:
274 DataSubObject(QObject *parent=nullptr) : QObject(parent) {}
275
276 QString subName() const { return m_subName; }
277 void setSubName(const QString &name) {
278 if (name != m_subName) {
279 m_subName = name;
280 emit subNameChanged();
281 }
282 }
283
284signals:
285 void subNameChanged();
286
287private:
288 QString m_subName;
289};
290
291class DataObject : public QObject
292{
293 Q_OBJECT
294
295 Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
296 Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged)
297 Q_PROPERTY(QObject *object READ object)
298
299public:
300 DataObject(QObject *parent=nullptr) : QObject(parent) {}
301 DataObject(const QString &name, const QString &color, QObject *parent=nullptr)
302 : QObject(parent), m_name(name), m_color(color), m_object(new DataSubObject(this)) { }
303
304
305 QString name() const { return m_name; }
306 void setName(const QString &name) {
307 if (name != m_name) {
308 m_name = name;
309 emit nameChanged();
310 }
311 }
312
313 QString color() const { return m_color; }
314 void setColor(const QString &color) {
315 if (color != m_color) {
316 m_color = color;
317 emit colorChanged();
318 }
319 }
320
321 QObject *object() const { return m_object; }
322 void setSubName(const QString &sn) {
323 m_object->setSubName(sn);
324 }
325
326signals:
327 void nameChanged();
328 void colorChanged();
329
330private:
331 QString m_name;
332 QString m_color;
333 DataSubObject *m_object;
334};
335
336class ItemRequester : public QObject
337{
338 Q_OBJECT
339public:
340 ItemRequester(QObject *parent = nullptr)
341 : QObject(parent)
342 , itemInitialized(nullptr)
343 , itemCreated(nullptr)
344 , itemDestroyed(nullptr)
345 , indexInitialized(-1)
346 , indexCreated(-1)
347 {
348 }
349
350 QQuickItem *itemInitialized;
351 QQuickItem *itemCreated;
352 QQuickItem *itemDestroyed;
353 int indexInitialized;
354 int indexCreated;
355
356public Q_SLOTS:
357 void initItem(int index, QObject *item)
358 {
359 itemInitialized = qobject_cast<QQuickItem*>(object: item);
360 indexInitialized = index;
361 }
362
363 void createdItem(int index, QObject *item)
364 {
365 itemCreated = qobject_cast<QQuickItem*>(object: item);
366 indexCreated = index;
367 }
368
369 void destroyingItem(QObject *item)
370 {
371 itemDestroyed = qobject_cast<QQuickItem*>(object: item);
372 }
373};
374
375QML_DECLARE_TYPE(SingleRoleModel)
376QML_DECLARE_TYPE(DataObject)
377QML_DECLARE_TYPE(StandardItem)
378QML_DECLARE_TYPE(StandardItemModel)
379
380class tst_qquickvisualdatamodel : public QQmlDataTest
381{
382 Q_OBJECT
383public:
384 tst_qquickvisualdatamodel();
385
386private slots:
387 void initTestCase();
388 void cleanupTestCase();
389 void rootIndex();
390 void updateLayout_data();
391 void updateLayout();
392 void childChanged_data();
393 void childChanged();
394 void noDelegate_data();
395 void noDelegate();
396 void itemsDestroyed_data();
397 void itemsDestroyed();
398 void objectListModel();
399 void singleRole();
400 void modelProperties();
401 void packagesDestroyed();
402 void qaimRowsMoved();
403 void qaimRowsMoved_data();
404 void subtreeRowsMoved();
405 void watchedRoles();
406 void hasModelChildren();
407 void setValue();
408 void remove_data();
409 void remove();
410 void move_data();
411 void move();
412 void groups_data();
413 void groups();
414 void invalidGroups();
415 void get();
416 void onChanged_data();
417 void onChanged();
418 void create();
419 void incompleteModel();
420 void insert_data();
421 void insert();
422 void resolve_data();
423 void resolve();
424 void warnings_data();
425 void warnings();
426 void invalidAttachment();
427 void asynchronousInsert_data();
428 void asynchronousInsert();
429 void asynchronousRemove_data();
430 void asynchronousRemove();
431 void asynchronousMove();
432 void asynchronousMove_data();
433 void asynchronousCancel();
434 void invalidContext();
435 void externalManagedModel();
436 void delegateModelChangeDelegate();
437 void checkFilterGroupForDelegate();
438 void readFromProxyObject();
439
440private:
441 template <int N> void groups_verify(
442 const SingleRoleModel &model,
443 QQuickItem *contentItem,
444 const int (&mIndex)[N],
445 const int (&iIndex)[N],
446 const int (&vIndex)[N],
447 const int (&sIndex)[N],
448 const bool (&vMember)[N],
449 const bool (&sMember)[N]);
450
451 template <int N> void get_verify(
452 const SingleRoleModel &model,
453 QQmlDelegateModel *visualModel,
454 QQmlDelegateModelGroup *visibleItems,
455 QQmlDelegateModelGroup *selectedItems,
456 const int (&mIndex)[N],
457 const int (&iIndex)[N],
458 const int (&vIndex)[N],
459 const int (&sIndex)[N],
460 const bool (&vMember)[N],
461 const bool (&sMember)[N]);
462
463 bool failed;
464 QQmlIncubationController controller;
465 QQmlEngine engine;
466};
467
468Q_DECLARE_METATYPE(QQmlChangeSet)
469
470template <typename T> static T evaluate(QObject *scope, const QString &expression)
471{
472 QQmlExpression expr(qmlContext(scope), scope, expression);
473 T result = expr.evaluate().value<T>();
474 if (expr.hasError())
475 qWarning() << expr.error().toString();
476 return result;
477}
478
479template <> void evaluate<void>(QObject *scope, const QString &expression)
480{
481 QQmlExpression expr(qmlContext(scope), scope, expression);
482 expr.evaluate();
483 if (expr.hasError())
484 qWarning() << expr.error().toString();
485}
486
487void tst_qquickvisualdatamodel::initTestCase()
488{
489 QQmlDataTest::initTestCase();
490 qRegisterMetaType<QQmlChangeSet>();
491
492 qmlRegisterType<SingleRoleModel>(uri: "tst_qquickvisualdatamodel", versionMajor: 1, versionMinor: 0, qmlName: "SingleRoleModel");
493 qmlRegisterType<DataObject>(uri: "tst_qquickvisualdatamodel", versionMajor: 1, versionMinor: 0, qmlName: "DataObject");
494 qmlRegisterType<StandardItem>(uri: "tst_qquickvisualdatamodel", versionMajor: 1, versionMinor: 0, qmlName: "StandardItem");
495 qmlRegisterType<StandardItemModel>(uri: "tst_qquickvisualdatamodel", versionMajor: 1, versionMinor: 0, qmlName: "StandardItemModel");
496
497 engine.setIncubationController(&controller);
498}
499
500void tst_qquickvisualdatamodel::cleanupTestCase()
501{
502}
503
504tst_qquickvisualdatamodel::tst_qquickvisualdatamodel()
505{
506}
507
508void tst_qquickvisualdatamodel::rootIndex()
509{
510 QQmlEngine engine;
511 QQmlComponent c(&engine, testFileUrl(fileName: "visualdatamodel.qml"));
512
513 QStandardItemModel model;
514 initStandardTreeModel(model: &model);
515
516 engine.rootContext()->setContextProperty("myModel", &model);
517
518 QQmlDelegateModel *obj = qobject_cast<QQmlDelegateModel*>(object: c.create());
519 QVERIFY(obj != nullptr);
520
521 QMetaObject::invokeMethod(obj, member: "setRoot");
522 QCOMPARE(qvariant_cast<QModelIndex>(obj->rootIndex()), model.index(0,0));
523
524 QMetaObject::invokeMethod(obj, member: "setRootToParent");
525 QCOMPARE(qvariant_cast<QModelIndex>(obj->rootIndex()), QModelIndex());
526
527 QMetaObject::invokeMethod(obj, member: "setRoot");
528 QCOMPARE(qvariant_cast<QModelIndex>(obj->rootIndex()), model.index(0,0));
529 model.clear(); // will emit modelReset()
530 QCOMPARE(qvariant_cast<QModelIndex>(obj->rootIndex()), QModelIndex());
531
532 delete obj;
533}
534
535void tst_qquickvisualdatamodel::updateLayout_data()
536{
537 QTest::addColumn<QUrl>(name: "source");
538
539 QTest::newRow(dataTag: "item delegate") << testFileUrl(fileName: "datalist.qml");
540 QTest::newRow(dataTag: "package delegate") << testFileUrl(fileName: "datalist-package.qml");
541}
542
543void tst_qquickvisualdatamodel::updateLayout()
544{
545 QFETCH(QUrl, source);
546
547 QQuickView view;
548
549 QStandardItemModel model;
550 initStandardTreeModel(model: &model);
551
552 view.rootContext()->setContextProperty("myModel", &model);
553
554 view.setSource(source);
555
556 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
557 QVERIFY(listview != nullptr);
558
559 QQuickItem *contentItem = listview->contentItem();
560 QVERIFY(contentItem != nullptr);
561
562 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "display", index: 0);
563 QVERIFY(name);
564 QCOMPARE(name->text(), QString("Row 1 Item"));
565 name = findItem<QQuickText>(parent: contentItem, objectName: "display", index: 1);
566 QVERIFY(name);
567 QCOMPARE(name->text(), QString("Row 2 Item"));
568 name = findItem<QQuickText>(parent: contentItem, objectName: "display", index: 2);
569 QVERIFY(name);
570 QCOMPARE(name->text(), QString("Row 3 Item"));
571
572 model.invisibleRootItem()->sortChildren(column: 0, order: Qt::DescendingOrder);
573
574 name = findItem<QQuickText>(parent: contentItem, objectName: "display", index: 0);
575 QVERIFY(name);
576 QCOMPARE(name->text(), QString("Row 3 Item"));
577 name = findItem<QQuickText>(parent: contentItem, objectName: "display", index: 1);
578 QVERIFY(name);
579 QCOMPARE(name->text(), QString("Row 2 Item"));
580 name = findItem<QQuickText>(parent: contentItem, objectName: "display", index: 2);
581 QVERIFY(name);
582 QCOMPARE(name->text(), QString("Row 1 Item"));
583}
584
585void tst_qquickvisualdatamodel::childChanged_data()
586{
587 QTest::addColumn<QUrl>(name: "source");
588
589 QTest::newRow(dataTag: "item delegate") << testFileUrl(fileName: "datalist.qml");
590 QTest::newRow(dataTag: "package delegate") << testFileUrl(fileName: "datalist-package.qml");
591}
592
593void tst_qquickvisualdatamodel::childChanged()
594{
595 QFETCH(QUrl, source);
596
597 QQuickView view;
598
599 QStandardItemModel model;
600 initStandardTreeModel(model: &model);
601
602 view.rootContext()->setContextProperty("myModel", &model);
603
604 view.setSource(source);
605
606 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
607 QVERIFY(listview != nullptr);
608
609 QQuickItem *contentItem = listview->contentItem();
610 QVERIFY(contentItem != nullptr);
611
612 QQmlDelegateModel *vdm = listview->findChild<QQmlDelegateModel*>(aName: "visualModel");
613 vdm->setRootIndex(QVariant::fromValue(value: model.indexFromItem(item: model.item(row: 1,column: 0))));
614 QCOMPARE(listview->count(), 1);
615
616 listview->forceLayout();
617 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "display", index: 0);
618 QVERIFY(name);
619 QCOMPARE(name->text(), QString("Row 2 Child Item"));
620
621 model.item(row: 1,column: 0)->child(row: 0,column: 0)->setText("Row 2 updated child");
622
623 name = findItem<QQuickText>(parent: contentItem, objectName: "display", index: 0);
624 QVERIFY(name);
625 QCOMPARE(name->text(), QString("Row 2 updated child"));
626
627 model.item(row: 1,column: 0)->appendRow(aitem: new QStandardItem(QLatin1String("Row 2 Child Item 2")));
628 QCOMPARE(listview->count(), 2);
629
630 listview->forceLayout();
631 name = findItem<QQuickText>(parent: contentItem, objectName: "display", index: 1);
632 QVERIFY(name != nullptr);
633 QCOMPARE(name->text(), QString("Row 2 Child Item 2"));
634
635 model.item(row: 1,column: 0)->takeRow(row: 1);
636 name = findItem<QQuickText>(parent: contentItem, objectName: "display", index: 1);
637 QVERIFY(!name);
638
639 vdm->setRootIndex(QVariant::fromValue(value: QModelIndex()));
640 QCOMPARE(listview->count(), 3);
641
642 listview->forceLayout();
643 name = findItem<QQuickText>(parent: contentItem, objectName: "display", index: 0);
644 QVERIFY(name);
645 QCOMPARE(name->text(), QString("Row 1 Item"));
646 name = findItem<QQuickText>(parent: contentItem, objectName: "display", index: 1);
647 QVERIFY(name);
648 QCOMPARE(name->text(), QString("Row 2 Item"));
649 name = findItem<QQuickText>(parent: contentItem, objectName: "display", index: 2);
650 QVERIFY(name);
651 QCOMPARE(name->text(), QString("Row 3 Item"));
652}
653
654void tst_qquickvisualdatamodel::objectListModel()
655{
656 QQuickView view;
657
658 QList<QObject*> dataList;
659 dataList.append(t: new DataObject("Item 1", "red"));
660 dataList.append(t: new DataObject("Item 2", "green"));
661 dataList.append(t: new DataObject("Item 3", "blue"));
662 dataList.append(t: new DataObject("Item 4", "yellow"));
663
664 QQmlContext *ctxt = view.rootContext();
665 ctxt->setContextProperty("myModel", QVariant::fromValue(value: dataList));
666
667 view.setSource(testFileUrl(fileName: "objectlist.qml"));
668
669 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
670 QVERIFY(listview != nullptr);
671
672 QQuickItem *contentItem = listview->contentItem();
673 QVERIFY(contentItem != nullptr);
674
675 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "name", index: 0);
676 QCOMPARE(name->text(), QString("Item 1"));
677 QCOMPARE(name->property("modelName").toString(), QString("Item 1"));
678
679 QQuickText *section = findItem<QQuickText>(parent: contentItem, objectName: "section", index: 0);
680 QCOMPARE(section->text(), QString("Item 1"));
681
682 dataList[0]->setProperty(name: "name", value: QLatin1String("Changed"));
683 QCOMPARE(name->text(), QString("Changed"));
684 QCOMPARE(name->property("modelName").toString(), QString("Changed"));
685
686 // Test resolving nested section property
687 DataObject *obj = static_cast<DataObject*>(dataList[0]);
688 obj->setSubName("SubItem 1");
689
690 QMetaObject::invokeMethod(obj: listview, member: "changeSectionProperty");
691 section = findItem<QQuickText>(parent: contentItem, objectName: "section", index: 0);
692 QCOMPARE(section->text(), QString("SubItem 1"));
693}
694
695void tst_qquickvisualdatamodel::singleRole()
696{
697 QStringList list = QStringList() << "one" << "two" << "three" << "four";
698 {
699 QQuickView view;
700
701 SingleRoleModel model(list);
702
703 QQmlContext *ctxt = view.rootContext();
704 ctxt->setContextProperty("myModel", &model);
705
706 view.setSource(testFileUrl(fileName: "singlerole1.qml"));
707
708 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
709 QVERIFY(listview != nullptr);
710
711 QQuickItem *contentItem = listview->contentItem();
712 QVERIFY(contentItem != nullptr);
713
714 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "name", index: 1);
715 QCOMPARE(name->text(), QString("two"));
716
717 model.set(idx: 1, string: "Changed");
718 QCOMPARE(name->text(), QString("Changed"));
719 }
720 {
721 QQuickView view;
722
723 SingleRoleModel model(list);
724
725 QQmlContext *ctxt = view.rootContext();
726 ctxt->setContextProperty("myModel", &model);
727
728 view.setSource(testFileUrl(fileName: "singlerole2.qml"));
729
730 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
731 QVERIFY(listview != nullptr);
732
733 QQuickItem *contentItem = listview->contentItem();
734 QVERIFY(contentItem != nullptr);
735
736 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "name", index: 1);
737 QCOMPARE(name->text(), QString("two"));
738
739 model.set(idx: 1, string: "Changed");
740 QCOMPARE(name->text(), QString("Changed"));
741 }
742 {
743 QQuickView view;
744
745 SingleRoleModel model(list, "modelData");
746
747 QQmlContext *ctxt = view.rootContext();
748 ctxt->setContextProperty("myModel", &model);
749
750 view.setSource(testFileUrl(fileName: "singlerole2.qml"));
751
752 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
753 QVERIFY(listview != nullptr);
754
755 QQuickItem *contentItem = listview->contentItem();
756 QVERIFY(contentItem != nullptr);
757
758 QQuickText *name = findItem<QQuickText>(parent: contentItem, objectName: "name", index: 1);
759 QCOMPARE(name->text(), QString("two"));
760
761 model.set(idx: 1, string: "Changed");
762 QCOMPARE(name->text(), QString("Changed"));
763 }
764}
765
766void tst_qquickvisualdatamodel::modelProperties()
767{
768 {
769 QQuickView view;
770
771 SingleRoleModel model(QStringList() << "one" << "two" << "three" << "four");
772
773 QQmlContext *ctxt = view.rootContext();
774 ctxt->setContextProperty("myModel", &model);
775
776 view.setSource(testFileUrl(fileName: "modelproperties.qml"));
777
778 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
779 QVERIFY(listview != nullptr);
780
781 QQuickItem *contentItem = listview->contentItem();
782 QVERIFY(contentItem != nullptr);
783
784 QQuickItem *delegate = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: 1);
785 QVERIFY(delegate);
786 QCOMPARE(delegate->property("test1").toString(),QString("two"));
787 QCOMPARE(delegate->property("test2").toString(),QString("two"));
788 QCOMPARE(delegate->property("test3").toString(),QString("two"));
789 QCOMPARE(delegate->property("test4").toString(),QString("two"));
790 QVERIFY(!delegate->property("test9").isValid());
791 QCOMPARE(delegate->property("test5").toString(),QString(""));
792 QVERIFY(delegate->property("test6").value<QObject*>() != 0);
793 QCOMPARE(delegate->property("test7").toInt(),1);
794 QCOMPARE(delegate->property("test8").toInt(),1);
795 }
796
797 {
798 QQuickView view;
799
800 QList<QObject*> dataList;
801 dataList.append(t: new DataObject("Item 1", "red"));
802 dataList.append(t: new DataObject("Item 2", "green"));
803 dataList.append(t: new DataObject("Item 3", "blue"));
804 dataList.append(t: new DataObject("Item 4", "yellow"));
805
806 QQmlContext *ctxt = view.rootContext();
807 ctxt->setContextProperty("myModel", QVariant::fromValue(value: dataList));
808
809 view.setSource(testFileUrl(fileName: "modelproperties.qml"));
810
811 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
812 QVERIFY(listview != nullptr);
813
814 QQuickItem *contentItem = listview->contentItem();
815 QVERIFY(contentItem != nullptr);
816
817 QQuickItem *delegate = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: 1);
818 QVERIFY(delegate);
819 QCOMPARE(delegate->property("test1").toString(),QString("Item 2"));
820 QCOMPARE(delegate->property("test2").toString(),QString("Item 2"));
821 QVERIFY(qobject_cast<DataObject*>(delegate->property("test3").value<QObject*>()) != 0);
822 QVERIFY(qobject_cast<DataObject*>(delegate->property("test4").value<QObject*>()) != 0);
823 QCOMPARE(delegate->property("test5").toString(),QString("Item 2"));
824 QCOMPARE(delegate->property("test9").toString(),QString("Item 2"));
825 QVERIFY(delegate->property("test6").value<QObject*>() != 0);
826 QCOMPARE(delegate->property("test7").toInt(),1);
827 QCOMPARE(delegate->property("test8").toInt(),1);
828 }
829
830 {
831 QQuickView view;
832
833 DataObject object("Item 1", "red");
834
835 QQmlContext *ctxt = view.rootContext();
836 ctxt->setContextProperty("myModel", &object);
837
838 view.setSource(testFileUrl(fileName: "modelproperties.qml"));
839
840 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
841 QVERIFY(listview != nullptr);
842
843 QQuickItem *contentItem = listview->contentItem();
844 QVERIFY(contentItem != nullptr);
845
846 QQuickItem *delegate = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: 0);
847 QVERIFY(delegate);
848 QCOMPARE(delegate->property("test1").toString(),QString("Item 1"));
849 QCOMPARE(delegate->property("test2").toString(),QString("Item 1"));
850 QVERIFY(qobject_cast<DataObject*>(delegate->property("test3").value<QObject*>()) != 0);
851 QVERIFY(qobject_cast<DataObject*>(delegate->property("test4").value<QObject*>()) != 0);
852 QCOMPARE(delegate->property("test5").toString(),QString("Item 1"));
853 QCOMPARE(delegate->property("test9").toString(),QString("Item 1"));
854 QVERIFY(delegate->property("test6").value<QObject*>() != 0);
855 QCOMPARE(delegate->property("test7").toInt(), 0);
856 QCOMPARE(delegate->property("test8").toInt(), 0);
857 }
858
859 {
860 QQuickView view;
861
862 QStandardItemModel model;
863 initStandardTreeModel(model: &model);
864
865 view.rootContext()->setContextProperty("myModel", &model);
866
867 QUrl source(testFileUrl(fileName: "modelproperties2.qml"));
868
869 //3 items, 3 i each
870 QTest::ignoreMessage(type: QtWarningMsg, message: source.toString().toLatin1() + ":13: ReferenceError: modelData is not defined");
871 QTest::ignoreMessage(type: QtWarningMsg, message: source.toString().toLatin1() + ":13: ReferenceError: modelData is not defined");
872 QTest::ignoreMessage(type: QtWarningMsg, message: source.toString().toLatin1() + ":13: ReferenceError: modelData is not defined");
873 QTest::ignoreMessage(type: QtWarningMsg, message: source.toString().toLatin1() + ":11: ReferenceError: modelData is not defined");
874 QTest::ignoreMessage(type: QtWarningMsg, message: source.toString().toLatin1() + ":11: ReferenceError: modelData is not defined");
875 QTest::ignoreMessage(type: QtWarningMsg, message: source.toString().toLatin1() + ":11: ReferenceError: modelData is not defined");
876 QTest::ignoreMessage(type: QtWarningMsg, message: source.toString().toLatin1() + ":17: TypeError: Cannot read property 'display' of undefined");
877 QTest::ignoreMessage(type: QtWarningMsg, message: source.toString().toLatin1() + ":17: TypeError: Cannot read property 'display' of undefined");
878 QTest::ignoreMessage(type: QtWarningMsg, message: source.toString().toLatin1() + ":17: TypeError: Cannot read property 'display' of undefined");
879
880 view.setSource(source);
881
882 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
883 QVERIFY(listview != nullptr);
884
885 QQuickItem *contentItem = listview->contentItem();
886 QVERIFY(contentItem != nullptr);
887
888 QQuickItem *delegate = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: 1);
889 QVERIFY(delegate);
890 QCOMPARE(delegate->property("test1").toString(),QString("Row 2 Item"));
891 QCOMPARE(delegate->property("test2").toString(),QString("Row 2 Item"));
892 QVERIFY(!delegate->property("test3").isValid());
893 QVERIFY(!delegate->property("test4").isValid());
894 QVERIFY(!delegate->property("test5").isValid());
895 QVERIFY(!delegate->property("test9").isValid());
896 QVERIFY(delegate->property("test6").value<QObject*>() != 0);
897 QCOMPARE(delegate->property("test7").toInt(),1);
898 QCOMPARE(delegate->property("test8").toInt(),1);
899 }
900 //### should also test QStringList and QVariantList
901}
902
903void tst_qquickvisualdatamodel::noDelegate_data()
904{
905 QTest::addColumn<QUrl>(name: "source");
906
907 QTest::newRow(dataTag: "item delegate") << testFileUrl(fileName: "datalist.qml");
908 QTest::newRow(dataTag: "package delegate") << testFileUrl(fileName: "datalist-package.qml");
909}
910
911void tst_qquickvisualdatamodel::noDelegate()
912{
913 QFETCH(QUrl, source);
914
915 QQuickView view;
916
917 QStandardItemModel model;
918 initStandardTreeModel(model: &model);
919
920 view.rootContext()->setContextProperty("myModel", &model);
921
922 view.setSource(source);
923
924 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
925 QVERIFY(listview != nullptr);
926
927 QQmlDelegateModel *vdm = listview->findChild<QQmlDelegateModel*>(aName: "visualModel");
928 QVERIFY(vdm != nullptr);
929 QCOMPARE(vdm->count(), 3);
930
931 vdm->setDelegate(nullptr);
932 QCOMPARE(vdm->count(), 0);
933}
934
935void tst_qquickvisualdatamodel::itemsDestroyed_data()
936{
937 QTest::addColumn<QUrl>(name: "source");
938
939 QTest::newRow(dataTag: "listView") << testFileUrl(fileName: "itemsDestroyed_listView.qml");
940 QTest::newRow(dataTag: "package") << testFileUrl(fileName: "itemsDestroyed_package.qml");
941 QTest::newRow(dataTag: "pathView") << testFileUrl(fileName: "itemsDestroyed_pathView.qml");
942 QTest::newRow(dataTag: "repeater") << testFileUrl(fileName: "itemsDestroyed_repeater.qml");
943}
944
945void tst_qquickvisualdatamodel::itemsDestroyed()
946{
947 QFETCH(QUrl, source);
948
949 QQmlGuard<QQuickItem> delegate;
950
951 {
952 QQuickView view;
953 QStandardItemModel model;
954 initStandardTreeModel(model: &model);
955 view.rootContext()->setContextProperty("myModel", &model);
956 view.setSource(source);
957
958 view.show();
959 QVERIFY(QTest::qWaitForWindowExposed(&view));
960
961 QVERIFY(delegate = findItem<QQuickItem>(view.contentItem(), "delegate", 1));
962 }
963 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
964 QVERIFY(!delegate);
965}
966
967void tst_qquickvisualdatamodel::packagesDestroyed()
968{
969 QStringList list;
970 for (int i=0; i<30; i++)
971 list << (QLatin1String("item ") + i);
972 SingleRoleModel model(list);
973
974 QQuickView view;
975 view.rootContext()->setContextProperty("testModel", &model);
976
977 QString filename(testFile(fileName: "packageView.qml"));
978 view.setSource(QUrl::fromLocalFile(localfile: filename));
979
980 qApp->processEvents();
981
982 QQuickListView *leftview = findItem<QQuickListView>(parent: view.rootObject(), objectName: "leftList");
983 QTRY_VERIFY(leftview != nullptr);
984
985 QQuickListView *rightview = findItem<QQuickListView>(parent: view.rootObject(), objectName: "rightList");
986 QTRY_VERIFY(rightview != nullptr);
987
988 QQuickItem *leftContent = leftview->contentItem();
989 QTRY_VERIFY(leftContent != nullptr);
990
991 QQuickItem *rightContent = rightview->contentItem();
992 QTRY_VERIFY(rightContent != nullptr);
993
994 leftview->forceLayout();
995 rightview->forceLayout();
996 QCOMPARE(leftview->currentIndex(), 0);
997 QCOMPARE(rightview->currentIndex(), 0);
998
999 rightview->setCurrentIndex(20);
1000 QTRY_COMPARE(rightview->contentY(), 100.0);
1001
1002 QQmlGuard<QQuickItem> left;
1003 QQmlGuard<QQuickItem> right;
1004
1005 QVERIFY(findItem<QQuickItem>(leftContent, "wrapper", 1));
1006 QVERIFY(findItem<QQuickItem>(rightContent, "wrapper", 1));
1007
1008 QVERIFY(left = findItem<QQuickItem>(leftContent, "wrapper", 19));
1009 QVERIFY(right = findItem<QQuickItem>(rightContent, "wrapper", 19));
1010
1011 rightview->setCurrentIndex(0);
1012 QCOMPARE(rightview->currentIndex(), 0);
1013
1014 QTRY_COMPARE(rightview->contentY(), 0.0);
1015 QCoreApplication::sendPostedEvents();
1016
1017 QVERIFY(!left);
1018 QVERIFY(!right);
1019
1020 QVERIFY(left = findItem<QQuickItem>(leftContent, "wrapper", 1));
1021 QVERIFY(right = findItem<QQuickItem>(rightContent, "wrapper", 1));
1022
1023 rightview->setCurrentIndex(20);
1024 QTRY_COMPARE(rightview->contentY(), 100.0);
1025
1026 QVERIFY(left);
1027 QVERIFY(right);
1028
1029 QVERIFY(findItem<QQuickItem>(leftContent, "wrapper", 19));
1030 QVERIFY(findItem<QQuickItem>(rightContent, "wrapper", 19));
1031
1032 leftview->setCurrentIndex(20);
1033 QTRY_COMPARE(leftview->contentY(), 100.0);
1034
1035 QVERIFY(!left);
1036 QVERIFY(!right);
1037}
1038
1039void tst_qquickvisualdatamodel::qaimRowsMoved()
1040{
1041 // Test parameters passed in QAIM::rowsMoved() signal are converted correctly
1042 // when translated to (from, to count) semantics.
1043 QFETCH(int, sourceFirst);
1044 QFETCH(int, sourceLast);
1045 QFETCH(int, destinationChild);
1046 QFETCH(int, expectFrom);
1047 QFETCH(int, expectTo);
1048 QFETCH(int, expectCount);
1049
1050 QQmlEngine engine;
1051 QQmlComponent c(&engine, testFileUrl(fileName: "visualdatamodel.qml"));
1052
1053 QStringList list;
1054 for (int i=0; i<30; i++)
1055 list << (QLatin1String("item ") + i);
1056 SingleRoleModel model(list);
1057 engine.rootContext()->setContextProperty("myModel", &model);
1058
1059 QQmlDelegateModel *obj = qobject_cast<QQmlDelegateModel*>(object: c.create());
1060 QVERIFY(obj != nullptr);
1061
1062 QSignalSpy spy(obj, SIGNAL(modelUpdated(QQmlChangeSet,bool)));
1063 model.emitMove(sourceFirst, sourceLast, destinationChild);
1064 QCOMPARE(spy.count(), 1);
1065
1066 QCOMPARE(spy[0].count(), 2);
1067 QQmlChangeSet changeSet = spy[0][0].value<QQmlChangeSet>();
1068 QCOMPARE(changeSet.removes().count(), 1);
1069 QCOMPARE(changeSet.removes().at(0).index, expectFrom);
1070 QCOMPARE(changeSet.removes().at(0).count, expectCount);
1071 QCOMPARE(changeSet.inserts().count(), 1);
1072 QCOMPARE(changeSet.inserts().at(0).index, expectTo);
1073 QCOMPARE(changeSet.inserts().at(0).count, expectCount);
1074 QCOMPARE(changeSet.removes().at(0).moveId, changeSet.inserts().at(0).moveId);
1075 QCOMPARE(spy[0][1].toBool(), false);
1076
1077 delete obj;
1078}
1079
1080void tst_qquickvisualdatamodel::qaimRowsMoved_data()
1081{
1082 QTest::addColumn<int>(name: "sourceFirst");
1083 QTest::addColumn<int>(name: "sourceLast");
1084 QTest::addColumn<int>(name: "destinationChild");
1085 QTest::addColumn<int>(name: "expectFrom");
1086 QTest::addColumn<int>(name: "expectTo");
1087 QTest::addColumn<int>(name: "expectCount");
1088
1089 QTest::newRow(dataTag: "move 1 forward")
1090 << 1 << 1 << 6
1091 << 1 << 5 << 1;
1092
1093 QTest::newRow(dataTag: "move 1 backwards")
1094 << 4 << 4 << 1
1095 << 4 << 1 << 1;
1096
1097 QTest::newRow(dataTag: "move multiple forwards")
1098 << 0 << 2 << 13
1099 << 0 << 10 << 3;
1100
1101 QTest::newRow(dataTag: "move multiple forwards, with same to")
1102 << 0 << 1 << 3
1103 << 0 << 1 << 2;
1104
1105 QTest::newRow(dataTag: "move multiple backwards")
1106 << 10 << 14 << 1
1107 << 10 << 1 << 5;
1108}
1109
1110void tst_qquickvisualdatamodel::subtreeRowsMoved()
1111{
1112 SingleRoleModel model(QStringList() << "one" << "two" << "three" << "four");
1113 model.insert(parent: model.index(row: 0, column: 0), index: 0, data: QStringList() << "a" << "b" << "c" << "d" << "e");
1114 model.insert(parent: model.index(row: 2, column: 0), index: 0, data: QStringList() << "A" << "B" << "C");
1115
1116 QQmlEngine engine;
1117 engine.rootContext()->setContextProperty("myModel", &model);
1118
1119 QQmlComponent component(&engine, testFileUrl(fileName: "visualdatamodel.qml"));
1120
1121 QScopedPointer<QObject> object(component.create());
1122 QQmlDelegateModel *vdm = qobject_cast<QQmlDelegateModel*>(object: object.data());
1123 QVERIFY(vdm);
1124
1125 QSignalSpy spy(vdm, SIGNAL(modelUpdated(QQmlChangeSet,bool)));
1126 QQmlChangeSet changeSet;
1127
1128 QCOMPARE(vdm->count(), 4);
1129
1130 // Move items from the current root index to a sub tree.
1131 model.move(fromParent: QModelIndex(), from: 1, toParent: model.index(row: 0, column: 0), to: 3, count: 2);
1132 QCOMPARE(vdm->count(), 2);
1133 QCOMPARE(spy.count(), 1);
1134 changeSet = spy.last().at(i: 0).value<QQmlChangeSet>();
1135 QCOMPARE(changeSet.removes().count(), 1);
1136 QCOMPARE(changeSet.removes().at(0).index, 1);
1137 QCOMPARE(changeSet.removes().at(0).count, 2);
1138 QCOMPARE(changeSet.inserts().count(), 0);
1139
1140 // Move items from a sub tree to the current root index.
1141 model.move(fromParent: model.index(row: 0, column: 0), from: 4, toParent: QModelIndex(), to: 2, count: 1);
1142 QCOMPARE(vdm->count(), 3);
1143 QCOMPARE(spy.count(), 2);
1144 changeSet = spy.last().at(i: 0).value<QQmlChangeSet>();
1145 QCOMPARE(changeSet.removes().count(), 0);
1146 QCOMPARE(changeSet.inserts().count(), 1);
1147 QCOMPARE(changeSet.inserts().at(0).index, 2);
1148 QCOMPARE(changeSet.inserts().at(0).count, 1);
1149
1150 vdm->setRootIndex(QVariant::fromValue(value: model.index(row: 2, column: 0)));
1151 QCOMPARE(vdm->rootIndex().value<QModelIndex>(), model.index(2, 0));
1152 QCOMPARE(vdm->count(), 3);
1153 QCOMPARE(spy.count(), 4);
1154 changeSet = spy.at(i: 2).at(i: 0).value<QQmlChangeSet>();
1155 QCOMPARE(changeSet.removes().count(), 1);
1156 QCOMPARE(changeSet.removes().at(0).index, 0);
1157 QCOMPARE(changeSet.removes().at(0).count, 3);
1158 changeSet = spy.last().at(i: 0).value<QQmlChangeSet>();
1159 QCOMPARE(changeSet.inserts().count(), 1);
1160 QCOMPARE(changeSet.inserts().at(0).index, 0);
1161 QCOMPARE(changeSet.inserts().at(0).count, 3);
1162
1163 // Move the current root index without changing its parent.
1164 model.move(fromParent: QModelIndex(), from: 2, toParent: QModelIndex(), to: 0, count: 1);
1165 QCOMPARE(vdm->rootIndex().value<QModelIndex>(), model.index(0, 0));
1166 QCOMPARE(vdm->count(), 3);
1167 QCOMPARE(spy.count(), 4);
1168
1169 // Move the current root index, changing its parent.
1170 model.move(fromParent: QModelIndex(), from: 0, toParent: model.index(row: 1, column: 0), to: 0, count: 1);
1171 QCOMPARE(vdm->rootIndex().value<QModelIndex>(), model.index(0, 0, model.index(0, 0)));
1172 QCOMPARE(vdm->count(), 3);
1173 QCOMPARE(spy.count(), 4);
1174
1175 model.insert(parent: model.index(row: 0, column: 0), index: 0, data: QStringList() << "new1" << "new2");
1176 QCOMPARE(vdm->rootIndex().value<QModelIndex>(), model.index(2, 0, model.index(0, 0)));
1177 QCOMPARE(vdm->count(), 3);
1178 QCOMPARE(spy.count(), 4);
1179
1180 model.remove(parent: model.index(row: 0, column: 0), index: 1, count: 1);
1181 QCOMPARE(vdm->rootIndex().value<QModelIndex>(), model.index(1, 0, model.index(0, 0)));
1182 QCOMPARE(vdm->count(), 3);
1183 QCOMPARE(spy.count(), 4);
1184
1185 model.remove(parent: model.index(row: 0, column: 0), index: 1, count: 1);
1186 QCOMPARE(vdm->rootIndex().value<QModelIndex>(), QModelIndex());
1187 QCOMPARE(vdm->count(), 0);
1188 QCOMPARE(spy.count(), 5);
1189 changeSet = spy.last().at(i: 0).value<QQmlChangeSet>();
1190 QCOMPARE(changeSet.removes().count(), 1);
1191 QCOMPARE(changeSet.removes().at(0).index, 0);
1192 QCOMPARE(changeSet.removes().at(0).count, 3);
1193 QCOMPARE(changeSet.inserts().count(), 0);
1194
1195 vdm->setRootIndex(QVariant::fromValue(value: QModelIndex()));
1196 QCOMPARE(vdm->rootIndex().value<QModelIndex>(), QModelIndex());
1197 QCOMPARE(vdm->count(), 2);
1198 QCOMPARE(spy.count(), 6);
1199 changeSet = spy.last().at(i: 0).value<QQmlChangeSet>();
1200 QCOMPARE(changeSet.removes().count(), 0);
1201 QCOMPARE(changeSet.inserts().count(), 1);
1202 QCOMPARE(changeSet.inserts().at(0).index, 0);
1203 QCOMPARE(changeSet.inserts().at(0).count, 2);
1204}
1205
1206void tst_qquickvisualdatamodel::watchedRoles()
1207{
1208 QaimModel model;
1209 for (int i = 0; i < 30; i++)
1210 model.addItem(name: "Item" + QString::number(i), number: "");
1211
1212 QQmlEngine engine;
1213 engine.rootContext()->setContextProperty("myModel", &model);
1214
1215 QQmlComponent component(&engine, testFileUrl(fileName: "visualdatamodel.qml"));
1216
1217 QScopedPointer<QObject> object(component.create());
1218 QQmlDelegateModel *vdm = qobject_cast<QQmlDelegateModel*>(object: object.data());
1219 QVERIFY(vdm);
1220
1221 // DelegateModel doesn't initialize model data until the first item is requested.
1222 QQuickItem *item = qobject_cast<QQuickItem*>(object: vdm->object(index: 0));
1223 QVERIFY(item);
1224 vdm->release(object: item);
1225 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); // Ensure released items are deleted before test exits.
1226
1227 QSignalSpy spy(vdm, SIGNAL(modelUpdated(QQmlChangeSet,bool)));
1228 QQmlChangeSet changeSet;
1229
1230 QCOMPARE(vdm->count(), 30);
1231
1232 emit model.dataChanged(topLeft: model.index(row: 0), bottomRight: model.index(row: 4));
1233 QCOMPARE(spy.count(), 0);
1234
1235 emit model.dataChanged(topLeft: model.index(row: 0), bottomRight: model.index(row: 4), roles: QVector<int>() << QaimModel::Name);
1236 QCOMPARE(spy.count(), 0);
1237
1238 emit model.dataChanged(topLeft: model.index(row: 0), bottomRight: model.index(row: 4), roles: QVector<int>() << QaimModel::Number);
1239 QCOMPARE(spy.count(), 0);
1240
1241 vdm->setWatchedRoles(QList<QByteArray>() << "name" << "dummy");
1242
1243 emit model.dataChanged(topLeft: model.index(row: 0), bottomRight: model.index(row: 4));
1244 QCOMPARE(spy.count(), 1);
1245 changeSet = spy.last().at(i: 0).value<QQmlChangeSet>();
1246 QCOMPARE(changeSet.changes().at(0).index, 0);
1247 QCOMPARE(changeSet.changes().at(0).count, 5);
1248
1249 emit model.dataChanged(topLeft: model.index(row: 1), bottomRight: model.index(row: 6), roles: QVector<int>() << QaimModel::Name);
1250 QCOMPARE(spy.count(), 2);
1251 changeSet = spy.last().at(i: 0).value<QQmlChangeSet>();
1252 QCOMPARE(changeSet.changes().at(0).index, 1);
1253 QCOMPARE(changeSet.changes().at(0).count, 6);
1254
1255 emit model.dataChanged(topLeft: model.index(row: 8), bottomRight: model.index(row: 8), roles: QVector<int>() << QaimModel::Number);
1256 QCOMPARE(spy.count(), 2);
1257
1258 vdm->setWatchedRoles(QList<QByteArray>() << "number" << "dummy");
1259
1260 emit model.dataChanged(topLeft: model.index(row: 0), bottomRight: model.index(row: 4));
1261 QCOMPARE(spy.count(), 3);
1262 changeSet = spy.last().at(i: 0).value<QQmlChangeSet>();
1263 QCOMPARE(changeSet.changes().at(0).index, 0);
1264 QCOMPARE(changeSet.changes().at(0).count, 5);
1265
1266 emit model.dataChanged(topLeft: model.index(row: 1), bottomRight: model.index(row: 6), roles: QVector<int>() << QaimModel::Name);
1267 QCOMPARE(spy.count(), 3);
1268
1269 emit model.dataChanged(topLeft: model.index(row: 8), bottomRight: model.index(row: 8), roles: QVector<int>() << QaimModel::Number);
1270 QCOMPARE(spy.count(), 4);
1271 changeSet = spy.last().at(i: 0).value<QQmlChangeSet>();
1272 QCOMPARE(changeSet.changes().at(0).index, 8);
1273 QCOMPARE(changeSet.changes().at(0).count, 1);
1274
1275 vdm->setWatchedRoles(QList<QByteArray>() << "number" << "name");
1276
1277 emit model.dataChanged(topLeft: model.index(row: 0), bottomRight: model.index(row: 4));
1278 QCOMPARE(spy.count(), 5);
1279 changeSet = spy.last().at(i: 0).value<QQmlChangeSet>();
1280 QCOMPARE(changeSet.changes().at(0).index, 0);
1281 QCOMPARE(changeSet.changes().at(0).count, 5);
1282
1283 emit model.dataChanged(topLeft: model.index(row: 1), bottomRight: model.index(row: 6), roles: QVector<int>() << QaimModel::Name);
1284 QCOMPARE(spy.count(), 6);
1285 changeSet = spy.last().at(i: 0).value<QQmlChangeSet>();
1286 QCOMPARE(changeSet.changes().at(0).index, 1);
1287 QCOMPARE(changeSet.changes().at(0).count, 6);
1288
1289 emit model.dataChanged(topLeft: model.index(row: 8), bottomRight: model.index(row: 8), roles: QVector<int>() << QaimModel::Number);
1290 QCOMPARE(spy.count(), 7);
1291 changeSet = spy.last().at(i: 0).value<QQmlChangeSet>();
1292 QCOMPARE(changeSet.changes().at(0).index, 8);
1293 QCOMPARE(changeSet.changes().at(0).count, 1);
1294}
1295
1296void tst_qquickvisualdatamodel::hasModelChildren()
1297{
1298 SingleRoleModel model(QStringList() << "one" << "two" << "three" << "four");
1299 model.insert(parent: model.index(row: 0, column: 0), index: 0, data: QStringList() << "a" << "b" << "c" << "d" << "e");
1300 model.insert(parent: model.index(row: 2, column: 0), index: 0, data: QStringList() << "A" << "B" << "C");
1301
1302 QQmlEngine engine;
1303 engine.rootContext()->setContextProperty("myModel", &model);
1304
1305 QQmlComponent component(&engine, testFileUrl(fileName: "visualdatamodel.qml"));
1306
1307 QScopedPointer<QObject> object(component.create());
1308 QQmlDelegateModel *vdm = qobject_cast<QQmlDelegateModel*>(object: object.data());
1309 QVERIFY(vdm);
1310
1311 QCOMPARE(vdm->count(), 4);
1312
1313 QQuickItem *item = nullptr;
1314
1315 item = qobject_cast<QQuickItem*>(object: vdm->object(index: 0));
1316 QVERIFY(item);
1317 QCOMPARE(item->property("modelChildren").toBool(), true);
1318 vdm->release(object: item);
1319
1320 item = qobject_cast<QQuickItem*>(object: vdm->object(index: 1));
1321 QVERIFY(item);
1322 QCOMPARE(item->property("modelChildren").toBool(), false);
1323 vdm->release(object: item);
1324
1325 item = qobject_cast<QQuickItem*>(object: vdm->object(index: 2));
1326 QVERIFY(item);
1327 QCOMPARE(item->property("modelChildren").toBool(), true);
1328 vdm->release(object: item);
1329
1330 item = qobject_cast<QQuickItem*>(object: vdm->object(index: 3));
1331 QVERIFY(item);
1332 QCOMPARE(item->property("modelChildren").toBool(), false);
1333 vdm->release(object: item);
1334 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); // Ensure released items are deleted before test exits.
1335
1336 QCOMPARE(vdm->stringValue(0, QLatin1String("hasModelChildren")), QVariant(true).toString());
1337 QCOMPARE(vdm->stringValue(1, QLatin1String("hasModelChildren")), QVariant(false).toString());
1338 QCOMPARE(vdm->stringValue(2, QLatin1String("hasModelChildren")), QVariant(true).toString());
1339 QCOMPARE(vdm->stringValue(3, QLatin1String("hasModelChildren")), QVariant(false).toString());
1340
1341 QCOMPARE(evaluate<bool>(vdm, "items.get(0).model.hasModelChildren"), true);
1342 QCOMPARE(evaluate<bool>(vdm, "items.get(1).model.hasModelChildren"), false);
1343 QCOMPARE(evaluate<bool>(vdm, "items.get(2).model.hasModelChildren"), true);
1344 QCOMPARE(evaluate<bool>(vdm, "items.get(3).model.hasModelChildren"), false);
1345}
1346
1347void tst_qquickvisualdatamodel::setValue()
1348{
1349 QStandardItemModel model;
1350 initStandardTreeModel(model: &model);
1351
1352 QQmlEngine engine;
1353 engine.rootContext()->setContextProperty("myModel", &model);
1354
1355 QQmlComponent component(&engine, testFileUrl(fileName: "visualdatamodel.qml"));
1356
1357 QScopedPointer<QObject> object(component.create());
1358 QQmlDelegateModel *vdm = qobject_cast<QQmlDelegateModel*>(object: object.data());
1359 QVERIFY(vdm);
1360
1361 QCOMPARE(vdm->count(), 3);
1362
1363 QQuickItem *item = nullptr;
1364
1365 item = qobject_cast<QQuickItem*>(object: vdm->object(index: 0));
1366 QVERIFY(item);
1367 QCOMPARE(evaluate<QString>(item, "display"), QString("Row 1 Item"));
1368 evaluate<void>(scope: item, expression: "display = 'Changed Item 1'");
1369 QCOMPARE(evaluate<QString>(item, "display"), QString("Changed Item 1"));
1370 QCOMPARE(model.item(0)->text(), QString("Changed Item 1"));
1371
1372 vdm->release(object: item);
1373
1374 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); // Ensure released items are deleted before test exits.
1375}
1376
1377void tst_qquickvisualdatamodel::remove_data()
1378{
1379 QTest::addColumn<QUrl>(name: "source");
1380 QTest::addColumn<QString>(name: "package delegate");
1381
1382 QTest::newRow(dataTag: "item delegate")
1383 << testFileUrl(fileName: "groups.qml")
1384 << QString();
1385 QTest::newRow(dataTag: "package")
1386 << testFileUrl(fileName: "groups-package.qml")
1387 << QString("package.");
1388}
1389
1390void tst_qquickvisualdatamodel::remove()
1391{
1392 QQuickView view;
1393
1394 SingleRoleModel model(QStringList()
1395 << "one"
1396 << "two"
1397 << "three"
1398 << "four"
1399 << "five"
1400 << "six"
1401 << "seven"
1402 << "eight"
1403 << "nine"
1404 << "ten"
1405 << "eleven"
1406 << "twelve");
1407
1408 QQmlContext *ctxt = view.rootContext();
1409 ctxt->setContextProperty("myModel", &model);
1410
1411 view.setSource(testFileUrl(fileName: "groups.qml"));
1412
1413 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
1414 QVERIFY(listview != nullptr);
1415
1416 QQuickItem *contentItem = listview->contentItem();
1417 QVERIFY(contentItem != nullptr);
1418
1419 QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel *>(object: qvariant_cast<QObject *>(v: listview->model()));
1420 QVERIFY(visualModel);
1421
1422 {
1423 QCOMPARE(listview->count(), 12);
1424 QCOMPARE(visualModel->items()->count(), 12);
1425 static const int mIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1426 static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1427
1428 for (int i = 0; i < lengthOf(mIndex); ++i) {
1429 QQuickItem *delegate = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: mIndex[i]);
1430 QVERIFY(delegate);
1431 QCOMPARE(delegate->property("test1").toString(), model.at(mIndex[i]));
1432 QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
1433 QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
1434 }
1435 } {
1436 evaluate<void>(scope: visualModel, expression: "items.remove(2)");
1437 QCOMPARE(listview->count(), 11);
1438 QCOMPARE(visualModel->items()->count(), 11);
1439 static const int mIndex[] = { 0, 1, 3, 4, 5, 6, 7, 8, 9,10,11 };
1440 static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10 };
1441
1442 for (int i = 0; i < lengthOf(mIndex); ++i) {
1443 QQuickItem *delegate = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: mIndex[i]);
1444 QVERIFY(delegate);
1445 QCOMPARE(delegate->property("test1").toString(), model.at(mIndex[i]));
1446 QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
1447 QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
1448 }
1449 } {
1450 evaluate<void>(scope: visualModel, expression: "items.remove(1, 4)");
1451 QCOMPARE(listview->count(), 7);
1452 QCOMPARE(visualModel->items()->count(), 7);
1453 static const int mIndex[] = { 0, 6, 7, 8, 9,10,11 };
1454 static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6 };
1455
1456 for (int i = 0; i < lengthOf(mIndex); ++i) {
1457 QQuickItem *delegate = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: mIndex[i]);
1458 QVERIFY(delegate);
1459 QCOMPARE(delegate->property("test1").toString(), model.at(mIndex[i]));
1460 QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
1461 QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
1462 }
1463 } {
1464 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*remove: index out of range"));
1465 evaluate<void>(scope: visualModel, expression: "items.remove(-8, 4)");
1466 QCOMPARE(listview->count(), 7);
1467 QCOMPARE(visualModel->items()->count(), 7);
1468 } {
1469 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*remove: index out of range"));
1470 evaluate<void>(scope: visualModel, expression: "items.remove(12, 2)");
1471 QCOMPARE(listview->count(), 7);
1472 QCOMPARE(visualModel->items()->count(), 7);
1473 } {
1474 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*remove: invalid count"));
1475 evaluate<void>(scope: visualModel, expression: "items.remove(5, 3)");
1476 QCOMPARE(listview->count(), 7);
1477 QCOMPARE(visualModel->items()->count(), 7);
1478 } {
1479 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*remove: invalid count"));
1480 evaluate<void>(scope: visualModel, expression: "items.remove(5, -2)");
1481 QCOMPARE(listview->count(), 7);
1482 QCOMPARE(visualModel->items()->count(), 7);
1483 }
1484}
1485
1486void tst_qquickvisualdatamodel::move_data()
1487{
1488 QTest::addColumn<QUrl>(name: "source");
1489 QTest::addColumn<QString>(name: "package delegate");
1490
1491 QTest::newRow(dataTag: "item delegate")
1492 << testFileUrl(fileName: "groups.qml")
1493 << QString();
1494 QTest::newRow(dataTag: "package")
1495 << testFileUrl(fileName: "groups-package.qml")
1496 << QString("package.");
1497}
1498
1499void tst_qquickvisualdatamodel::move()
1500{
1501 QQuickView view;
1502
1503 SingleRoleModel model(QStringList()
1504 << "one"
1505 << "two"
1506 << "three"
1507 << "four"
1508 << "five"
1509 << "six"
1510 << "seven"
1511 << "eight"
1512 << "nine"
1513 << "ten"
1514 << "eleven"
1515 << "twelve");
1516
1517 QQmlContext *ctxt = view.rootContext();
1518 ctxt->setContextProperty("myModel", &model);
1519
1520 view.setSource(testFileUrl(fileName: "groups.qml"));
1521
1522 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
1523 QVERIFY(listview != nullptr);
1524
1525 QQuickItem *contentItem = listview->contentItem();
1526 QVERIFY(contentItem != nullptr);
1527
1528 QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel *>(object: qvariant_cast<QObject *>(v: listview->model()));
1529 QVERIFY(visualModel);
1530
1531 {
1532 QCOMPARE(listview->count(), 12);
1533 QCOMPARE(visualModel->items()->count(), 12);
1534 static const int mIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1535 static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1536
1537 for (int i = 0; i < lengthOf(mIndex); ++i) {
1538 QQuickItem *delegate = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: mIndex[i]);
1539 QVERIFY(delegate);
1540 QCOMPARE(delegate->property("test1").toString(), model.at(mIndex[i]));
1541 QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
1542 QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
1543 }
1544 } {
1545 evaluate<void>(scope: visualModel, expression: "items.move(2, 4)");
1546 QCOMPARE(listview->count(), 12);
1547 QCOMPARE(visualModel->items()->count(), 12);
1548 static const int mIndex[] = { 0, 1, 3, 4, 2, 5, 6, 7, 8, 9,10,11 };
1549 static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1550
1551 for (int i = 0; i < lengthOf(mIndex); ++i) {
1552 QQuickItem *delegate = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: mIndex[i]);
1553 QVERIFY(delegate);
1554 QCOMPARE(delegate->property("test1").toString(), model.at(mIndex[i]));
1555 QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
1556 QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
1557 }
1558 } {
1559 evaluate<void>(scope: visualModel, expression: "items.move(4, 2)");
1560 QCOMPARE(listview->count(), 12);
1561 QCOMPARE(visualModel->items()->count(), 12);
1562 static const int mIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1563 static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1564
1565 for (int i = 0; i < lengthOf(mIndex); ++i) {
1566 QQuickItem *delegate = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: mIndex[i]);
1567 QVERIFY(delegate);
1568 QCOMPARE(delegate->property("test1").toString(), model.at(mIndex[i]));
1569 QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
1570 QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
1571 }
1572 } {
1573 evaluate<void>(scope: visualModel, expression: "items.move(8, 0, 4)");
1574 QCOMPARE(listview->count(), 12);
1575 QCOMPARE(visualModel->items()->count(), 12);
1576 static const int mIndex[] = { 8, 9,10,11, 0, 1, 2, 3, 4, 5, 6, 7 };
1577 static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1578
1579 for (int i = 0; i < lengthOf(mIndex); ++i) {
1580 QQuickItem *delegate = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: mIndex[i]);
1581 QVERIFY(delegate);
1582 QCOMPARE(delegate->property("test1").toString(), model.at(mIndex[i]));
1583 QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
1584 QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
1585 }
1586 } {
1587 evaluate<void>(scope: visualModel, expression: "items.move(3, 4, 5)");
1588 QCOMPARE(listview->count(), 12);
1589 QCOMPARE(visualModel->items()->count(), 12);
1590 static const int mIndex[] = { 8, 9,10,4, 11, 0, 1, 2, 3, 5, 6, 7 };
1591 static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1592
1593 for (int i = 0; i < lengthOf(mIndex); ++i) {
1594 QQuickItem *delegate = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: mIndex[i]);
1595 QVERIFY(delegate);
1596 QCOMPARE(delegate->property("test1").toString(), model.at(mIndex[i]));
1597 QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
1598 QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
1599 }
1600 } {
1601 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*move: invalid count"));
1602 evaluate<void>(scope: visualModel, expression: "items.move(5, 2, -2)");
1603 QCOMPARE(listview->count(), 12);
1604 QCOMPARE(visualModel->items()->count(), 12);
1605 } {
1606 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*move: from index out of range"));
1607 evaluate<void>(scope: visualModel, expression: "items.move(-6, 2, 1)");
1608 QCOMPARE(listview->count(), 12);
1609 QCOMPARE(visualModel->items()->count(), 12);
1610 } {
1611 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*move: from index out of range"));
1612 evaluate<void>(scope: visualModel, expression: "items.move(15, 2, 1)");
1613 QCOMPARE(listview->count(), 12);
1614 QCOMPARE(visualModel->items()->count(), 12);
1615 } {
1616 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*move: from index out of range"));
1617 evaluate<void>(scope: visualModel, expression: "items.move(11, 1, 3)");
1618 QCOMPARE(listview->count(), 12);
1619 QCOMPARE(visualModel->items()->count(), 12);
1620 } {
1621 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*move: to index out of range"));
1622 evaluate<void>(scope: visualModel, expression: "items.move(2, -5, 1)");
1623 QCOMPARE(listview->count(), 12);
1624 QCOMPARE(visualModel->items()->count(), 12);
1625 } {
1626 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*move: to index out of range"));
1627 evaluate<void>(scope: visualModel, expression: "items.move(2, 14, 1)");
1628 QCOMPARE(listview->count(), 12);
1629 QCOMPARE(visualModel->items()->count(), 12);
1630 } {
1631 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*move: to index out of range"));
1632 evaluate<void>(scope: visualModel, expression: "items.move(2, 11, 4)");
1633 QCOMPARE(listview->count(), 12);
1634 QCOMPARE(visualModel->items()->count(), 12);
1635 }
1636}
1637
1638void tst_qquickvisualdatamodel::groups_data()
1639{
1640 QTest::addColumn<QUrl>(name: "source");
1641 QTest::addColumn<QString>(name: "part");
1642
1643 QTest::newRow(dataTag: "item delegate")
1644 << testFileUrl(fileName: "groups.qml")
1645 << QString();
1646 QTest::newRow(dataTag: "package")
1647 << testFileUrl(fileName: "groups-package.qml")
1648 << QString("visualModel.parts.package.");
1649}
1650
1651template <int N> void tst_qquickvisualdatamodel::groups_verify(
1652 const SingleRoleModel &model,
1653 QQuickItem *contentItem,
1654 const int (&mIndex)[N],
1655 const int (&iIndex)[N],
1656 const int (&vIndex)[N],
1657 const int (&sIndex)[N],
1658 const bool (&vMember)[N],
1659 const bool (&sMember)[N])
1660{
1661 failed = true;
1662 for (int i = 0; i < N; ++i) {
1663 QQuickItem *delegate = findItem<QQuickItem>(contentItem, "delegate", mIndex[i]);
1664 QVERIFY(delegate);
1665 QCOMPARE(evaluate<QString>(delegate, "test1"), model.at(mIndex[i]));
1666 QCOMPARE(evaluate<int>(delegate, "test2") , mIndex[i]);
1667 QCOMPARE(evaluate<int>(delegate, "test3") , iIndex[i]);
1668 QCOMPARE(evaluate<bool>(delegate, "test4"), true);
1669 QCOMPARE(evaluate<int>(delegate, "test5") , vIndex[i]);
1670 QCOMPARE(evaluate<bool>(delegate, "test6"), vMember[i]);
1671 QCOMPARE(evaluate<int>(delegate, "test7") , sIndex[i]);
1672 QCOMPARE(evaluate<bool>(delegate, "test8"), sMember[i]);
1673 QCOMPARE(evaluate<QStringList>(delegate, "test9").contains("items") , bool(true));
1674 QCOMPARE(evaluate<QStringList>(delegate, "test9").contains("visible") , bool(vMember[i]));
1675 QCOMPARE(evaluate<QStringList>(delegate, "test9").contains("selected"), bool(sMember[i]));
1676 }
1677 failed = false;
1678}
1679
1680#define VERIFY_GROUPS \
1681 groups_verify(model, contentItem, mIndex, iIndex, vIndex, sIndex, vMember, sMember); \
1682 QVERIFY(!failed)
1683
1684
1685void tst_qquickvisualdatamodel::groups()
1686{
1687 QFETCH(QUrl, source);
1688 QFETCH(QString, part);
1689
1690 QQuickView view;
1691
1692 SingleRoleModel model(QStringList()
1693 << "one"
1694 << "two"
1695 << "three"
1696 << "four"
1697 << "five"
1698 << "six"
1699 << "seven"
1700 << "eight"
1701 << "nine"
1702 << "ten"
1703 << "eleven"
1704 << "twelve");
1705
1706 QQmlContext *ctxt = view.rootContext();
1707 ctxt->setContextProperty("myModel", &model);
1708
1709 view.setSource(source);
1710
1711 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
1712 QVERIFY(listview != nullptr);
1713
1714 QQuickItem *contentItem = listview->contentItem();
1715 QVERIFY(contentItem != nullptr);
1716
1717 QQmlDelegateModel *visualModel = listview->findChild<QQmlDelegateModel *>(aName: "visualModel");
1718 QVERIFY(visualModel);
1719
1720 QQmlDelegateModelGroup *visibleItems = listview->findChild<QQmlDelegateModelGroup *>(aName: "visibleItems");
1721 QVERIFY(visibleItems);
1722
1723 QQmlDelegateModelGroup *selectedItems = listview->findChild<QQmlDelegateModelGroup *>(aName: "selectedItems");
1724 QVERIFY(selectedItems);
1725
1726 const bool f = false;
1727 const bool t = true;
1728
1729 {
1730 QCOMPARE(listview->count(), 12);
1731 QCOMPARE(visualModel->items()->count(), 12);
1732 QCOMPARE(visibleItems->count(), 12);
1733 QCOMPARE(selectedItems->count(), 0);
1734 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1735 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1736 static const int vIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1737 static const bool vMember[] = { t, t, t, t, t, t, t, t, t, t, t, t };
1738 static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1739 static const bool sMember[] = { f, f, f, f, f, f, f, f, f, f, f, f };
1740 VERIFY_GROUPS;
1741 } {
1742 evaluate<void>(scope: visualModel, expression: "items.addGroups(8, \"selected\")");
1743 QCOMPARE(listview->count(), 12);
1744 QCOMPARE(visualModel->items()->count(), 12);
1745 QCOMPARE(visibleItems->count(), 12);
1746 QCOMPARE(selectedItems->count(), 1);
1747 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1748 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1749 static const int vIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1750 static const bool vMember[] = { t, t, t, t, t, t, t, t, t, t, t, t };
1751 static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 };
1752 static const bool sMember[] = { f, f, f, f, f, f, f, f, t, f, f, f };
1753 VERIFY_GROUPS;
1754 } {
1755 evaluate<void>(scope: visualModel, expression: "items.addGroups(6, 4, [\"visible\", \"selected\"])");
1756 QCOMPARE(listview->count(), 12);
1757 QCOMPARE(visualModel->items()->count(), 12);
1758 QCOMPARE(visibleItems->count(), 12);
1759 QCOMPARE(selectedItems->count(), 4);
1760 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1761 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1762 static const int vIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1763 static const bool vMember[] = { t, t, t, t, t, t, t, t, t, t, t, t };
1764 static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 4 };
1765 static const bool sMember[] = { f, f, f, f, f, f, t, t, t, t, f, f };
1766 VERIFY_GROUPS;
1767 } {
1768 evaluate<void>(scope: visualModel, expression: "items.setGroups(2, [\"items\", \"selected\"])");
1769 QCOMPARE(listview->count(), 12);
1770 QCOMPARE(visualModel->items()->count(), 12);
1771 QCOMPARE(visibleItems->count(), 11);
1772 QCOMPARE(selectedItems->count(), 5);
1773 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1774 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1775 static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9,10 };
1776 static const bool vMember[] = { t, t, f, t, t, t, t, t, t, t, t, t };
1777 static const int sIndex [] = { 0, 0, 0, 1, 1, 1, 1, 2, 3, 4, 5, 5 };
1778 static const bool sMember[] = { f, f, t, f, f, f, t, t, t, t, f, f };
1779 VERIFY_GROUPS;
1780 } {
1781 evaluate<void>(scope: selectedItems, expression: "setGroups(0, 3, \"items\")");
1782 QCOMPARE(listview->count(), 12);
1783 QCOMPARE(visualModel->items()->count(), 12);
1784 QCOMPARE(visibleItems->count(), 9);
1785 QCOMPARE(selectedItems->count(), 2);
1786 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1787 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1788 static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 5, 5, 5, 6, 7, 8 };
1789 static const bool vMember[] = { t, t, f, t, t, t, f, f, t, t, t, t };
1790 static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2 };
1791 static const bool sMember[] = { f, f, f, f, f, f, f, f, t, t, f, f };
1792 VERIFY_GROUPS;
1793 } {
1794 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*addGroups: invalid count"));
1795 evaluate<void>(scope: visualModel, expression: "items.addGroups(11, -4, \"items\")");
1796 QCOMPARE(listview->count(), 12);
1797 QCOMPARE(visualModel->items()->count(), 12);
1798 QCOMPARE(visibleItems->count(), 9);
1799 QCOMPARE(selectedItems->count(), 2);
1800 } {
1801 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*addGroups: index out of range"));
1802 evaluate<void>(scope: visualModel, expression: "items.addGroups(-1, 3, \"items\")");
1803 QCOMPARE(listview->count(), 12);
1804 QCOMPARE(visualModel->items()->count(), 12);
1805 QCOMPARE(visibleItems->count(), 9);
1806 QCOMPARE(selectedItems->count(), 2);
1807 } {
1808 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*addGroups: index out of range"));
1809 evaluate<void>(scope: visualModel, expression: "items.addGroups(14, 3, \"items\")");
1810 QCOMPARE(listview->count(), 12);
1811 QCOMPARE(visualModel->items()->count(), 12);
1812 QCOMPARE(visibleItems->count(), 9);
1813 QCOMPARE(selectedItems->count(), 2);
1814 } {
1815 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*addGroups: invalid count"));
1816 evaluate<void>(scope: visualModel, expression: "items.addGroups(11, 5, \"items\")");
1817 QCOMPARE(listview->count(), 12);
1818 QCOMPARE(visualModel->items()->count(), 12);
1819 QCOMPARE(visibleItems->count(), 9);
1820 QCOMPARE(selectedItems->count(), 2);
1821 } {
1822 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*setGroups: invalid count"));
1823 evaluate<void>(scope: visualModel, expression: "items.setGroups(11, -4, \"items\")");
1824 QCOMPARE(listview->count(), 12);
1825 QCOMPARE(visualModel->items()->count(), 12);
1826 QCOMPARE(visibleItems->count(), 9);
1827 QCOMPARE(selectedItems->count(), 2);
1828 } {
1829 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*setGroups: index out of range"));
1830 evaluate<void>(scope: visualModel, expression: "items.setGroups(-1, 3, \"items\")");
1831 QCOMPARE(listview->count(), 12);
1832 QCOMPARE(visualModel->items()->count(), 12);
1833 QCOMPARE(visibleItems->count(), 9);
1834 QCOMPARE(selectedItems->count(), 2);
1835 } {
1836 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*setGroups: index out of range"));
1837 evaluate<void>(scope: visualModel, expression: "items.setGroups(14, 3, \"items\")");
1838 QCOMPARE(listview->count(), 12);
1839 QCOMPARE(visualModel->items()->count(), 12);
1840 QCOMPARE(visibleItems->count(), 9);
1841 QCOMPARE(selectedItems->count(), 2);
1842 } {
1843 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*setGroups: invalid count"));
1844 evaluate<void>(scope: visualModel, expression: "items.setGroups(11, 5, \"items\")");
1845 QCOMPARE(listview->count(), 12);
1846 QCOMPARE(visualModel->items()->count(), 12);
1847 QCOMPARE(visibleItems->count(), 9);
1848 QCOMPARE(selectedItems->count(), 2);
1849 } {
1850 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*removeGroups: invalid count"));
1851 evaluate<void>(scope: visualModel, expression: "items.removeGroups(11, -4, \"items\")");
1852 QCOMPARE(listview->count(), 12);
1853 QCOMPARE(visualModel->items()->count(), 12);
1854 } {
1855 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*removeGroups: index out of range"));
1856 evaluate<void>(scope: visualModel, expression: "items.removeGroups(-1, 3, \"items\")");
1857 QCOMPARE(listview->count(), 12);
1858 QCOMPARE(visualModel->items()->count(), 12);
1859 QCOMPARE(visibleItems->count(), 9);
1860 QCOMPARE(selectedItems->count(), 2);
1861 } {
1862 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*removeGroups: index out of range"));
1863 evaluate<void>(scope: visualModel, expression: "items.removeGroups(14, 3, \"items\")");
1864 QCOMPARE(listview->count(), 12);
1865 QCOMPARE(visualModel->items()->count(), 12);
1866 QCOMPARE(visibleItems->count(), 9);
1867 QCOMPARE(selectedItems->count(), 2);
1868 } {
1869 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*removeGroups: invalid count"));
1870 evaluate<void>(scope: visualModel, expression: "items.removeGroups(11, 5, \"items\")");
1871 QCOMPARE(listview->count(), 12);
1872 QCOMPARE(visualModel->items()->count(), 12);
1873 QCOMPARE(visibleItems->count(), 9);
1874 QCOMPARE(selectedItems->count(), 2);
1875 } {
1876 evaluate<void>(scope: visualModel, expression: part + "filterOnGroup = \"visible\"");
1877 QCOMPARE(listview->count(), 9);
1878 QCOMPARE(visualModel->items()->count(), 12);
1879 QCOMPARE(visibleItems->count(), 9);
1880 QCOMPARE(selectedItems->count(), 2);
1881 QCOMPARE(evaluate<QString>(visualModel, part + "filterOnGroup"), QString("visible"));
1882 } {
1883 evaluate<void>(scope: visualModel, expression: part + "filterOnGroup = \"selected\"");
1884 QCOMPARE(listview->count(), 2);
1885 QCOMPARE(visualModel->items()->count(), 12);
1886 QCOMPARE(visibleItems->count(), 9);
1887 QCOMPARE(selectedItems->count(), 2);
1888 QCOMPARE(evaluate<QString>(visualModel, part + "filterOnGroup"), QString("selected"));
1889 } {
1890 evaluate<void>(scope: visualModel, expression: part + "filterOnGroup = undefined");
1891 QCOMPARE(listview->count(), 12);
1892 QCOMPARE(visualModel->items()->count(), 12);
1893 QCOMPARE(visibleItems->count(), 9);
1894 QCOMPARE(selectedItems->count(), 2);
1895 QCOMPARE(evaluate<QString>(visualModel, part + "filterOnGroup"), QString("items"));
1896 } {
1897 QQuickItem *delegate = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: 5);
1898 QVERIFY(delegate);
1899
1900 evaluate<void>(scope: delegate, expression: "hide()");
1901 QCOMPARE(listview->count(), 12);
1902 QCOMPARE(visualModel->items()->count(), 12);
1903 QCOMPARE(visibleItems->count(), 8);
1904 QCOMPARE(selectedItems->count(), 2);
1905 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1906 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1907 static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 4, 4, 4, 5, 6, 7 };
1908 static const bool vMember[] = { t, t, f, t, t, f, f, f, t, t, t, t };
1909 static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2 };
1910 static const bool sMember[] = { f, f, f, f, f, f, f, f, t, t, f, f };
1911 VERIFY_GROUPS;
1912 } {
1913 QQuickItem *delegate = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: 5);
1914 QVERIFY(delegate);
1915
1916 evaluate<void>(scope: delegate, expression: "select()");
1917 QCOMPARE(listview->count(), 12);
1918 QCOMPARE(visualModel->items()->count(), 12);
1919 QCOMPARE(visibleItems->count(), 8);
1920 QCOMPARE(selectedItems->count(), 3);
1921 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1922 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1923 static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 4, 4, 4, 5, 6, 7 };
1924 static const bool vMember[] = { t, t, f, t, t, f, f, f, t, t, t, t };
1925 static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3 };
1926 static const bool sMember[] = { f, f, f, f, f, t, f, f, t, t, f, f };
1927 VERIFY_GROUPS;
1928 } {
1929 evaluate<void>(scope: visualModel, expression: "items.move(2, 6, 3)");
1930 QCOMPARE(listview->count(), 12);
1931 QCOMPARE(visualModel->items()->count(), 12);
1932 QCOMPARE(visibleItems->count(), 8);
1933 QCOMPARE(selectedItems->count(), 3);
1934 static const int mIndex [] = { 0, 1, 5, 6, 7, 8, 2, 3, 4, 9,10,11 };
1935 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
1936 static const int vIndex [] = { 0, 1, 2, 2, 2, 2, 3, 3, 4, 5, 6, 7 };
1937 static const bool vMember[] = { t, t, f, f, f, t, f, t, t, t, t, t };
1938 static const int sIndex [] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3 };
1939 static const bool sMember[] = { f, f, t, f, f, t, f, f, f, t, f, f };
1940 VERIFY_GROUPS;
1941 }
1942}
1943
1944template <int N> void tst_qquickvisualdatamodel::get_verify(
1945 const SingleRoleModel &model,
1946 QQmlDelegateModel *visualModel,
1947 QQmlDelegateModelGroup *visibleItems,
1948 QQmlDelegateModelGroup *selectedItems,
1949 const int (&mIndex)[N],
1950 const int (&iIndex)[N],
1951 const int (&vIndex)[N],
1952 const int (&sIndex)[N],
1953 const bool (&vMember)[N],
1954 const bool (&sMember)[N])
1955{
1956 failed = true;
1957 for (int i = 0; i < N; ++i) {
1958 QCOMPARE(evaluate<QString>(visualModel, QString("items.get(%1).model.name").arg(i)), model.at(mIndex[i]));
1959 QCOMPARE(evaluate<QString>(visualModel, QString("items.get(%1).model.modelData").arg(i)), model.at(mIndex[i]));
1960 QCOMPARE(evaluate<int>(visualModel, QString("items.get(%1).model.index").arg(i)), mIndex[i]);
1961 QCOMPARE(evaluate<int>(visualModel, QString("items.get(%1).itemsIndex").arg(i)), iIndex[i]);
1962 QCOMPARE(evaluate<bool>(visualModel, QString("items.get(%1).inItems").arg(i)), true);
1963 QCOMPARE(evaluate<int>(visualModel, QString("items.get(%1).visibleIndex").arg(i)), vIndex[i]);
1964 QCOMPARE(evaluate<bool>(visualModel, QString("items.get(%1).inVisible").arg(i)), vMember[i]);
1965 QCOMPARE(evaluate<int>(visualModel, QString("items.get(%1).selectedIndex").arg(i)), sIndex[i]);
1966 QCOMPARE(evaluate<bool>(visualModel, QString("items.get(%1).inSelected").arg(i)), sMember[i]);
1967 QCOMPARE(evaluate<bool>(visualModel, QString("contains(items.get(%1).groups, \"items\")").arg(i)), true);
1968 QCOMPARE(evaluate<bool>(visualModel, QString("contains(items.get(%1).groups, \"visible\")").arg(i)), vMember[i]);
1969 QCOMPARE(evaluate<bool>(visualModel, QString("contains(items.get(%1).groups, \"selected\")").arg(i)), sMember[i]);
1970
1971 if (vMember[i]) {
1972 QCOMPARE(evaluate<QString>(visibleItems, QString("get(%1).model.name").arg(vIndex[i])), model.at(mIndex[i]));
1973 QCOMPARE(evaluate<QString>(visibleItems, QString("get(%1).model.modelData").arg(vIndex[i])), model.at(mIndex[i]));
1974 QCOMPARE(evaluate<int>(visibleItems, QString("get(%1).model.index").arg(vIndex[i])), mIndex[i]);
1975 QCOMPARE(evaluate<int>(visibleItems, QString("get(%1).itemsIndex").arg(vIndex[i])), iIndex[i]);
1976 QCOMPARE(evaluate<bool>(visibleItems, QString("get(%1).inItems").arg(vIndex[i])), true);
1977 QCOMPARE(evaluate<int>(visibleItems, QString("get(%1).visibleIndex").arg(vIndex[i])), vIndex[i]);
1978 QCOMPARE(evaluate<bool>(visibleItems, QString("get(%1).inVisible").arg(vIndex[i])), vMember[i]);
1979 QCOMPARE(evaluate<int>(visibleItems, QString("get(%1).selectedIndex").arg(vIndex[i])), sIndex[i]);
1980 QCOMPARE(evaluate<bool>(visibleItems, QString("get(%1).inSelected").arg(vIndex[i])), sMember[i]);
1981
1982 QCOMPARE(evaluate<bool>(visibleItems, QString("contains(get(%1).groups, \"items\")").arg(vIndex[i])), true);
1983 QCOMPARE(evaluate<bool>(visibleItems, QString("contains(get(%1).groups, \"visible\")").arg(vIndex[i])), vMember[i]);
1984 QCOMPARE(evaluate<bool>(visibleItems, QString("contains(get(%1).groups, \"selected\")").arg(vIndex[i])), sMember[i]);
1985 }
1986 if (sMember[i]) {
1987 QCOMPARE(evaluate<QString>(selectedItems, QString("get(%1).model.name").arg(sIndex[i])), model.at(mIndex[i]));
1988 QCOMPARE(evaluate<QString>(selectedItems, QString("get(%1).model.modelData").arg(sIndex[i])), model.at(mIndex[i]));
1989 QCOMPARE(evaluate<int>(selectedItems, QString("get(%1).model.index").arg(sIndex[i])), mIndex[i]);
1990 QCOMPARE(evaluate<int>(selectedItems, QString("get(%1).itemsIndex").arg(sIndex[i])), iIndex[i]);
1991 QCOMPARE(evaluate<bool>(selectedItems, QString("get(%1).inItems").arg(sIndex[i])), true);
1992 QCOMPARE(evaluate<int>(selectedItems, QString("get(%1).visibleIndex").arg(sIndex[i])), vIndex[i]);
1993 QCOMPARE(evaluate<bool>(selectedItems, QString("get(%1).inVisible").arg(sIndex[i])), vMember[i]);
1994 QCOMPARE(evaluate<int>(selectedItems, QString("get(%1).selectedIndex").arg(sIndex[i])), sIndex[i]);
1995 QCOMPARE(evaluate<bool>(selectedItems, QString("get(%1).inSelected").arg(sIndex[i])), sMember[i]);
1996 QCOMPARE(evaluate<bool>(selectedItems, QString("contains(get(%1).groups, \"items\")").arg(sIndex[i])), true);
1997 QCOMPARE(evaluate<bool>(selectedItems, QString("contains(get(%1).groups, \"visible\")").arg(sIndex[i])), vMember[i]);
1998 QCOMPARE(evaluate<bool>(selectedItems, QString("contains(get(%1).groups, \"selected\")").arg(sIndex[i])), sMember[i]);
1999 }
2000 }
2001 failed = false;
2002}
2003
2004#define VERIFY_GET \
2005 get_verify(model, visualModel, visibleItems, selectedItems, mIndex, iIndex, vIndex, sIndex, vMember, sMember); \
2006 QVERIFY(!failed)
2007
2008void tst_qquickvisualdatamodel::get()
2009{
2010 QQuickView view;
2011
2012 SingleRoleModel model(QStringList()
2013 << "one"
2014 << "two"
2015 << "three"
2016 << "four"
2017 << "five"
2018 << "six"
2019 << "seven"
2020 << "eight"
2021 << "nine"
2022 << "ten"
2023 << "eleven"
2024 << "twelve");
2025
2026 QQmlContext *ctxt = view.rootContext();
2027 ctxt->setContextProperty("myModel", &model);
2028
2029 view.setSource(testFileUrl(fileName: "groups.qml"));
2030
2031 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
2032 QVERIFY(listview != nullptr);
2033
2034 QQuickItem *contentItem = listview->contentItem();
2035 QVERIFY(contentItem != nullptr);
2036
2037 QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel *>(object: qvariant_cast<QObject *>(v: listview->model()));
2038 QVERIFY(visualModel);
2039
2040 QQmlDelegateModelGroup *visibleItems = visualModel->findChild<QQmlDelegateModelGroup *>(aName: "visibleItems");
2041 QVERIFY(visibleItems);
2042
2043 QQmlDelegateModelGroup *selectedItems = visualModel->findChild<QQmlDelegateModelGroup *>(aName: "selectedItems");
2044 QVERIFY(selectedItems);
2045
2046 const bool f = false;
2047 const bool t = true;
2048
2049 {
2050 QCOMPARE(listview->count(), 12);
2051 QCOMPARE(visualModel->items()->count(), 12);
2052 QCOMPARE(visibleItems->count(), 12);
2053 QCOMPARE(selectedItems->count(), 0);
2054 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2055 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2056 static const int vIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2057 static const bool vMember[] = { t, t, t, t, t, t, t, t, t, t, t, t };
2058 static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
2059 static const bool sMember[] = { f, f, f, f, f, f, f, f, f, f, f, f };
2060 VERIFY_GET;
2061 } {
2062 evaluate<void>(scope: visualModel, expression: "items.addGroups(8, \"selected\")");
2063 QCOMPARE(listview->count(), 12);
2064 QCOMPARE(visualModel->items()->count(), 12);
2065 QCOMPARE(visibleItems->count(), 12);
2066 QCOMPARE(selectedItems->count(), 1);
2067 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2068 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2069 static const int vIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2070 static const bool vMember[] = { t, t, t, t, t, t, t, t, t, t, t, t };
2071 static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 };
2072 static const bool sMember[] = { f, f, f, f, f, f, f, f, t, f, f, f };
2073 VERIFY_GET;
2074 } {
2075 evaluate<void>(scope: visualModel, expression: "items.addGroups(6, 4, [\"visible\", \"selected\"])");
2076 QCOMPARE(listview->count(), 12);
2077 QCOMPARE(visualModel->items()->count(), 12);
2078 QCOMPARE(visibleItems->count(), 12);
2079 QCOMPARE(selectedItems->count(), 4);
2080 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2081 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2082 static const int vIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2083 static const bool vMember[] = { t, t, t, t, t, t, t, t, t, t, t, t };
2084 static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 4 };
2085 static const bool sMember[] = { f, f, f, f, f, f, t, t, t, t, f, f };
2086 VERIFY_GET;
2087 } {
2088 evaluate<void>(scope: visualModel, expression: "items.setGroups(2, [\"items\", \"selected\"])");
2089 QCOMPARE(listview->count(), 12);
2090 QCOMPARE(visualModel->items()->count(), 12);
2091 QCOMPARE(visibleItems->count(), 11);
2092 QCOMPARE(selectedItems->count(), 5);
2093 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2094 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2095 static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9,10 };
2096 static const bool vMember[] = { t, t, f, t, t, t, t, t, t, t, t, t };
2097 static const int sIndex [] = { 0, 0, 0, 1, 1, 1, 1, 2, 3, 4, 5, 5 };
2098 static const bool sMember[] = { f, f, t, f, f, f, t, t, t, t, f, f };
2099 VERIFY_GET;
2100 } {
2101 evaluate<void>(scope: selectedItems, expression: "setGroups(0, 3, \"items\")");
2102 QCOMPARE(listview->count(), 12);
2103 QCOMPARE(visualModel->items()->count(), 12);
2104 QCOMPARE(visibleItems->count(), 9);
2105 QCOMPARE(selectedItems->count(), 2);
2106 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2107 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2108 static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 5, 5, 5, 6, 7, 8 };
2109 static const bool vMember[] = { t, t, f, t, t, t, f, f, t, t, t, t };
2110 static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2 };
2111 static const bool sMember[] = { f, f, f, f, f, f, f, f, t, t, f, f };
2112 VERIFY_GET;
2113 } {
2114 evaluate<void>(scope: visualModel, expression: "items.get(5).inVisible = false");
2115 QCOMPARE(listview->count(), 12);
2116 QCOMPARE(visualModel->items()->count(), 12);
2117 QCOMPARE(visibleItems->count(), 8);
2118 QCOMPARE(selectedItems->count(), 2);
2119 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2120 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2121 static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 4, 4, 4, 5, 6, 7 };
2122 static const bool vMember[] = { t, t, f, t, t, f, f, f, t, t, t, t };
2123 static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2 };
2124 static const bool sMember[] = { f, f, f, f, f, f, f, f, t, t, f, f };
2125 VERIFY_GET;
2126 } {
2127 evaluate<void>(scope: visualModel, expression: "items.get(5).inSelected = true");
2128 QCOMPARE(listview->count(), 12);
2129 QCOMPARE(visualModel->items()->count(), 12);
2130 QCOMPARE(visibleItems->count(), 8);
2131 QCOMPARE(selectedItems->count(), 3);
2132 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2133 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2134 static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 4, 4, 4, 5, 6, 7 };
2135 static const bool vMember[] = { t, t, f, t, t, f, f, f, t, t, t, t };
2136 static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3 };
2137 static const bool sMember[] = { f, f, f, f, f, t, f, f, t, t, f, f };
2138 VERIFY_GET;
2139 } {
2140 evaluate<void>(scope: visualModel, expression: "items.get(5).groups = [\"visible\", \"items\"]");
2141 QCOMPARE(listview->count(), 12);
2142 QCOMPARE(visualModel->items()->count(), 12);
2143 QCOMPARE(visibleItems->count(), 9);
2144 QCOMPARE(selectedItems->count(), 2);
2145 static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2146 static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
2147 static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 5, 5, 5, 6, 7, 8 };
2148 static const bool vMember[] = { t, t, f, t, t, t, f, f, t, t, t, t };
2149 static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2 };
2150 static const bool sMember[] = { f, f, f, f, f, f, f, f, t, t, f, f };
2151 VERIFY_GET;
2152 }
2153}
2154
2155void tst_qquickvisualdatamodel::invalidGroups()
2156{
2157 QUrl source = testFileUrl(fileName: "groups-invalid.qml");
2158 QTest::ignoreMessage(type: QtWarningMsg, message: (source.toString() + ":13:9: QML DelegateModelGroup: " + QQmlDelegateModelGroup::tr(s: "Group names must start with a lower case letter")).toUtf8());
2159
2160 QQmlComponent component(&engine, source);
2161 QScopedPointer<QObject> object(component.create());
2162 QVERIFY(object);
2163
2164 QCOMPARE(evaluate<int>(object.data(), "groups.length"), 4);
2165 QCOMPARE(evaluate<QString>(object.data(), "groups[0].name"), QString("items"));
2166 QCOMPARE(evaluate<QString>(object.data(), "groups[1].name"), QString("persistedItems"));
2167 QCOMPARE(evaluate<QString>(object.data(), "groups[2].name"), QString("visible"));
2168 QCOMPARE(evaluate<QString>(object.data(), "groups[3].name"), QString("selected"));
2169}
2170
2171void tst_qquickvisualdatamodel::onChanged_data()
2172{
2173 QTest::addColumn<QString>(name: "expression");
2174 QTest::addColumn<QStringList>(name: "tests");
2175
2176 QTest::newRow(dataTag: "item appended")
2177 << QString("listModel.append({\"number\": \"five\"})")
2178 << (QStringList()
2179 << "verify(vm.removed, [], [], [])"
2180 << "verify(vm.inserted, [4], [1], [undefined])"
2181 << "verify(vi.removed, [], [], [])"
2182 << "verify(vi.inserted, [4], [1], [undefined])"
2183 << "verify(si.removed, [], [], [])"
2184 << "verify(si.inserted, [], [], [])");
2185 QTest::newRow(dataTag: "item prepended")
2186 << QString("listModel.insert(0, {\"number\": \"five\"})")
2187 << (QStringList()
2188 << "verify(vm.removed, [], [], [])"
2189 << "verify(vm.inserted, [0], [1], [undefined])"
2190 << "verify(vi.removed, [], [], [])"
2191 << "verify(vi.inserted, [0], [1], [undefined])"
2192 << "verify(si.removed, [], [], [])"
2193 << "verify(si.inserted, [], [], [])");
2194 QTest::newRow(dataTag: "item inserted")
2195 << QString("listModel.insert(2, {\"number\": \"five\"})")
2196 << (QStringList()
2197 << "verify(vm.removed, [], [], [])"
2198 << "verify(vm.inserted, [2], [1], [undefined])"
2199 << "verify(vi.removed, [], [], [])"
2200 << "verify(vi.inserted, [2], [1], [undefined])"
2201 << "verify(si.removed, [], [], [])"
2202 << "verify(si.inserted, [], [], [])");
2203
2204 QTest::newRow(dataTag: "item removed tail")
2205 << QString("listModel.remove(3)")
2206 << (QStringList()
2207 << "verify(vm.removed, [3], [1], [undefined])"
2208 << "verify(vm.inserted, [], [], [])"
2209 << "verify(vi.removed, [3], [1], [undefined])"
2210 << "verify(vi.inserted, [], [], [])"
2211 << "verify(si.removed, [], [], [])"
2212 << "verify(si.inserted, [], [], [])");
2213 QTest::newRow(dataTag: "item removed head")
2214 << QString("listModel.remove(0)")
2215 << (QStringList()
2216 << "verify(vm.removed, [0], [1], [undefined])"
2217 << "verify(vm.inserted, [], [], [])"
2218 << "verify(vi.removed, [0], [1], [undefined])"
2219 << "verify(vi.inserted, [], [], [])"
2220 << "verify(si.removed, [], [], [])"
2221 << "verify(si.inserted, [], [], [])");
2222 QTest::newRow(dataTag: "item removed middle")
2223 << QString("listModel.remove(1)")
2224 << (QStringList()
2225 << "verify(vm.removed, [1], [1], [undefined])"
2226 << "verify(vm.inserted, [], [], [])"
2227 << "verify(vi.removed, [1], [1], [undefined])"
2228 << "verify(vi.inserted, [], [], [])"
2229 << "verify(si.removed, [], [], [])"
2230 << "verify(si.inserted, [], [], [])");
2231
2232
2233 QTest::newRow(dataTag: "item moved from tail")
2234 << QString("listModel.move(3, 0, 1)")
2235 << (QStringList()
2236 << "verify(vm.removed, [3], [1], [vm.inserted[0].moveId])"
2237 << "verify(vm.inserted, [0], [1], [vm.removed[0].moveId])"
2238 << "verify(vi.removed, [3], [1], [vi.inserted[0].moveId])"
2239 << "verify(vi.inserted, [0], [1], [vi.removed[0].moveId])"
2240 << "verify(si.removed, [], [], [])"
2241 << "verify(si.inserted, [], [], [])");
2242 QTest::newRow(dataTag: "item moved from head")
2243 << QString("listModel.move(0, 2, 2)")
2244 << (QStringList()
2245 << "verify(vm.removed, [0], [2], [vm.inserted[0].moveId])"
2246 << "verify(vm.inserted, [2], [2], [vm.removed[0].moveId])"
2247 << "verify(vi.removed, [0], [2], [vi.inserted[0].moveId])"
2248 << "verify(vi.inserted, [2], [2], [vi.removed[0].moveId])"
2249 << "verify(si.removed, [], [], [])"
2250 << "verify(si.inserted, [], [], [])");
2251
2252 QTest::newRow(dataTag: "groups changed")
2253 << QString("items.setGroups(1, 2, [\"items\", \"selected\"])")
2254 << (QStringList()
2255 << "verify(vm.inserted, [], [], [])"
2256 << "verify(vm.removed, [], [], [])"
2257 << "verify(vi.removed, [1], [2], [undefined])"
2258 << "verify(vi.inserted, [], [], [])"
2259 << "verify(si.removed, [], [], [])"
2260 << "verify(si.inserted, [0], [2], [undefined])");
2261
2262 QTest::newRow(dataTag: "multiple removes")
2263 << QString("{ vi.remove(1, 1); "
2264 "vi.removeGroups(0, 2, \"items\") }")
2265 << (QStringList()
2266 << "verify(vm.removed, [0, 1], [1, 1], [undefined, undefined])"
2267 << "verify(vm.inserted, [], [], [])"
2268 << "verify(vi.removed, [1], [1], [undefined])"
2269 << "verify(vi.inserted, [], [], [])"
2270 << "verify(si.removed, [], [], [])"
2271 << "verify(si.inserted, [], [], [])");
2272}
2273
2274void tst_qquickvisualdatamodel::onChanged()
2275{
2276 QFETCH(QString, expression);
2277 QFETCH(QStringList, tests);
2278
2279 QQmlComponent component(&engine, testFileUrl(fileName: "onChanged.qml"));
2280 QScopedPointer<QObject> object(component.create());
2281 QVERIFY(object);
2282
2283 evaluate<void>(scope: object.data(), expression);
2284
2285 foreach (const QString &test, tests) {
2286 bool passed = evaluate<bool>(scope: object.data(), expression: test);
2287 if (!passed)
2288 qWarning() << test;
2289 QVERIFY(passed);
2290 }
2291}
2292
2293void tst_qquickvisualdatamodel::create()
2294{
2295 QQuickView view;
2296
2297 SingleRoleModel model(QStringList()
2298 << "one"
2299 << "two"
2300 << "three"
2301 << "four"
2302 << "five"
2303 << "six"
2304 << "seven"
2305 << "eight"
2306 << "nine"
2307 << "ten"
2308 << "eleven"
2309 << "twelve"
2310 << "thirteen"
2311 << "fourteen"
2312 << "fifteen"
2313 << "sixteen"
2314 << "seventeen"
2315 << "eighteen"
2316 << "nineteen"
2317 << "twenty");
2318
2319 QQmlContext *ctxt = view.rootContext();
2320 ctxt->setContextProperty("myModel", &model);
2321
2322 view.setSource(testFileUrl(fileName: "create.qml"));
2323
2324 QQuickListView *listview = qobject_cast<QQuickListView*>(object: view.rootObject());
2325 QVERIFY(listview != nullptr);
2326
2327 QQuickItem *contentItem = listview->contentItem();
2328 QVERIFY(contentItem != nullptr);
2329
2330 QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel *>(object: qvariant_cast<QObject *>(v: listview->model()));
2331 QVERIFY(visualModel);
2332
2333 QCOMPARE(listview->count(), 20);
2334
2335 QQmlGuard<QQuickItem> delegate;
2336
2337 // persistedItems.includeByDefault is true, so all items belong to persistedItems initially.
2338 QVERIFY(delegate = findItem<QQuickItem>(contentItem, "delegate", 1));
2339 QCOMPARE(evaluate<bool>(delegate, "DelegateModel.inPersistedItems"), true);
2340
2341 // changing include by default doesn't remove persistance.
2342 evaluate<void>(scope: visualModel, expression: "persistedItems.includeByDefault = false");
2343 QCOMPARE(evaluate<bool>(delegate, "DelegateModel.inPersistedItems"), true);
2344
2345 // removing from persistedItems does.
2346 evaluate<void>(scope: visualModel, expression: "persistedItems.remove(0, 20)");
2347 QCOMPARE(listview->count(), 20);
2348 QCOMPARE(evaluate<bool>(delegate, "DelegateModel.inPersistedItems"), false);
2349
2350 // Request an item instantiated by the view.
2351 QVERIFY(delegate = qobject_cast<QQuickItem *>(evaluate<QObject *>(visualModel, "items.create(1)")));
2352 QCOMPARE(delegate.data(), findItem<QQuickItem>(contentItem, "delegate", 1));
2353 QCOMPARE(evaluate<bool>(delegate, "DelegateModel.inPersistedItems"), true);
2354 QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 1);
2355
2356 evaluate<void>(scope: delegate, expression: "DelegateModel.inPersistedItems = false");
2357 QCOMPARE(listview->count(), 20);
2358 QCoreApplication::sendPostedEvents(receiver: delegate, event_type: QEvent::DeferredDelete);
2359 QVERIFY(delegate);
2360 QCOMPARE(evaluate<bool>(delegate, "DelegateModel.inPersistedItems"), false);
2361 QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 0);
2362
2363 // Request an item not instantiated by the view.
2364 QVERIFY(!findItem<QQuickItem>(contentItem, "delegate", 15));
2365 QVERIFY(delegate = qobject_cast<QQuickItem *>(evaluate<QObject *>(visualModel, "items.create(15)")));
2366 QCOMPARE(delegate.data(), findItem<QQuickItem>(contentItem, "delegate", 15));
2367 QCOMPARE(evaluate<bool>(delegate, "DelegateModel.inPersistedItems"), true);
2368 QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 1);
2369
2370 evaluate<void>(scope: visualModel, expression: "persistedItems.remove(0)");
2371 QCoreApplication::sendPostedEvents(receiver: delegate, event_type: QEvent::DeferredDelete);
2372 QVERIFY(!delegate);
2373 QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 0);
2374
2375 // Request an item not instantiated by the view, then scroll the view so it will request it.
2376 QVERIFY(!findItem<QQuickItem>(contentItem, "delegate", 16));
2377 QVERIFY(delegate = qobject_cast<QQuickItem *>(evaluate<QObject *>(visualModel, "items.create(16)")));
2378 QCOMPARE(delegate.data(), findItem<QQuickItem>(contentItem, "delegate", 16));
2379 QCOMPARE(evaluate<bool>(delegate, "DelegateModel.inPersistedItems"), true);
2380 QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 1);
2381
2382 evaluate<void>(scope: listview, expression: "positionViewAtIndex(19, ListView.End)");
2383 QCOMPARE(listview->count(), 20);
2384 evaluate<void>(scope: delegate, expression: "DelegateModel.groups = [\"items\"]");
2385 QCoreApplication::sendPostedEvents(receiver: delegate, event_type: QEvent::DeferredDelete);
2386 QVERIFY(delegate);
2387 QCOMPARE(evaluate<bool>(delegate, "DelegateModel.inPersistedItems"), false);
2388 QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 0);
2389
2390 // Request and release an item instantiated by the view, then scroll the view so it releases it.
2391 QVERIFY(findItem<QQuickItem>(contentItem, "delegate", 17));
2392 QVERIFY(delegate = qobject_cast<QQuickItem *>(evaluate<QObject *>(visualModel, "items.create(17)")));
2393 QCOMPARE(delegate.data(), findItem<QQuickItem>(contentItem, "delegate", 17));
2394 QCOMPARE(evaluate<bool>(delegate, "DelegateModel.inPersistedItems"), true);
2395 QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 1);
2396
2397 evaluate<void>(scope: visualModel, expression: "items.removeGroups(17, \"persistedItems\")");
2398 QCoreApplication::sendPostedEvents(receiver: delegate, event_type: QEvent::DeferredDelete);
2399 QVERIFY(delegate);
2400 QCOMPARE(evaluate<bool>(delegate, "DelegateModel.inPersistedItems"), false);
2401 QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 0);
2402 evaluate<void>(scope: listview, expression: "positionViewAtIndex(1, ListView.Beginning)");
2403 QCOMPARE(listview->count(), 20);
2404 QCoreApplication::sendPostedEvents(receiver: delegate, event_type: QEvent::DeferredDelete);
2405 QVERIFY(!delegate);
2406
2407 // Adding an item to the persistedItems group won't instantiate it, but if later requested by
2408 // the view it will be persisted.
2409 evaluate<void>(scope: visualModel, expression: "items.addGroups(18, \"persistedItems\")");
2410 QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 1);
2411 QVERIFY(!findItem<QQuickItem>(contentItem, "delegate", 18));
2412 evaluate<void>(scope: listview, expression: "positionViewAtIndex(19, ListView.End)");
2413 QCOMPARE(listview->count(), 20);
2414 QVERIFY(delegate = findItem<QQuickItem>(contentItem, "delegate", 18));
2415 QCOMPARE(evaluate<bool>(delegate, "DelegateModel.inPersistedItems"), true);
2416 QCoreApplication::sendPostedEvents(receiver: delegate, event_type: QEvent::DeferredDelete);
2417 QVERIFY(delegate);
2418 evaluate<void>(scope: listview, expression: "positionViewAtIndex(1, ListView.Beginning)");
2419 QCOMPARE(listview->count(), 20);
2420 QCoreApplication::sendPostedEvents(receiver: delegate, event_type: QEvent::DeferredDelete);
2421 QVERIFY(delegate);
2422
2423 // Remove an uninstantiated but cached item from the persistedItems group.
2424 evaluate<void>(scope: visualModel, expression: "items.addGroups(19, \"persistedItems\")");
2425 QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), 2);
2426 QVERIFY(!findItem<QQuickItem>(contentItem, "delegate", 19));
2427 // Store a reference to the item so it is retained in the cache.
2428 evaluate<void>(scope: visualModel, expression: "persistentHandle = items.get(19)");
2429 QCOMPARE(evaluate<bool>(visualModel, "persistentHandle.inPersistedItems"), true);
2430 evaluate<void>(scope: visualModel, expression: "items.removeGroups(19, \"persistedItems\")");
2431 QCOMPARE(evaluate<bool>(visualModel, "persistentHandle.inPersistedItems"), false);
2432}
2433
2434void tst_qquickvisualdatamodel::incompleteModel()
2435{
2436 // DelegateModel is first populated in componentComplete. Verify various functions are
2437 // harmlessly ignored until then.
2438
2439 QQmlComponent component(&engine);
2440 component.setData("import QtQuick 2.0\nimport QtQml.Models 2.2\nDelegateModel {}", baseUrl: testFileUrl(fileName: ""));
2441
2442 QScopedPointer<QObject> object(component.beginCreate(engine.rootContext()));
2443
2444 QQmlDelegateModel *model = qobject_cast<QQmlDelegateModel *>(object: object.data());
2445 QVERIFY(model);
2446
2447 QSignalSpy itemsSpy(model->items(), SIGNAL(countChanged()));
2448 QSignalSpy persistedItemsSpy(model->items(), SIGNAL(countChanged()));
2449
2450 evaluate<void>(scope: model, expression: "items.removeGroups(0, items.count, \"items\")");
2451 QCOMPARE(itemsSpy.count(), 0);
2452 QCOMPARE(persistedItemsSpy.count(), 0);
2453
2454 evaluate<void>(scope: model, expression: "items.setGroups(0, items.count, \"persistedItems\")");
2455 QCOMPARE(itemsSpy.count(), 0);
2456 QCOMPARE(persistedItemsSpy.count(), 0);
2457
2458 evaluate<void>(scope: model, expression: "items.addGroups(0, items.count, \"persistedItems\")");
2459 QCOMPARE(itemsSpy.count(), 0);
2460 QCOMPARE(persistedItemsSpy.count(), 0);
2461
2462 evaluate<void>(scope: model, expression: "items.remove(0, items.count)");
2463 QCOMPARE(itemsSpy.count(), 0);
2464 QCOMPARE(persistedItemsSpy.count(), 0);
2465
2466 evaluate<void>(scope: model, expression: "items.insert([ \"color\": \"blue\" ])");
2467 QCOMPARE(itemsSpy.count(), 0);
2468 QCOMPARE(persistedItemsSpy.count(), 0);
2469
2470 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*get: index out of range"));
2471 QVERIFY(evaluate<bool>(model, "items.get(0) === undefined"));
2472
2473 component.completeCreate();
2474}
2475
2476void tst_qquickvisualdatamodel::insert_data()
2477{
2478 QTest::addColumn<QUrl>(name: "source");
2479 QTest::addColumn<QString>(name: "expression");
2480 QTest::addColumn<int>(name: "modelCount");
2481 QTest::addColumn<int>(name: "visualCount");
2482 QTest::addColumn<int>(name: "index");
2483 QTest::addColumn<bool>(name: "inItems");
2484 QTest::addColumn<bool>(name: "persisted");
2485 QTest::addColumn<bool>(name: "visible");
2486 QTest::addColumn<bool>(name: "selected");
2487 QTest::addColumn<bool>(name: "modelData");
2488 QTest::addColumn<QString>(name: "property");
2489 QTest::addColumn<QStringList>(name: "propertyData");
2490
2491 const QUrl listModelSource[] = {
2492 testFileUrl(fileName: "listmodelproperties.qml"),
2493 testFileUrl(fileName: "listmodelproperties-package.qml") };
2494 const QUrl singleRoleSource[] = {
2495 testFileUrl(fileName: "singleroleproperties.qml"),
2496 testFileUrl(fileName: "singleroleproperties-package.qml") };
2497 const QUrl multipleRoleSource[] = {
2498 testFileUrl(fileName: "multipleroleproperties.qml"),
2499 testFileUrl(fileName: "multipleroleproperties-package.qml") };
2500 const QUrl stringListSource[] = {
2501 testFileUrl(fileName: "stringlistproperties.qml"),
2502 testFileUrl(fileName: "stringlistproperties-package.qml") };
2503 const QUrl objectListSource[] = {
2504 testFileUrl(fileName: "objectlistproperties.qml"),
2505 testFileUrl(fileName: "objectlistproperties-package.qml") };
2506
2507 for (int i = 0; i < 2; ++i) {
2508 // List Model.
2509 QTest::newRow(dataTag: "ListModel.items prepend")
2510 << listModelSource[i]
2511 << QString("items.insert(0, {\"number\": \"eight\"})")
2512 << 4 << 5 << 0 << true << false << false << false << true
2513 << QString("number")
2514 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
2515
2516 QTest::newRow(dataTag: "ListModel.items append")
2517 << listModelSource[i]
2518 << QString("items.insert({\"number\": \"eight\"})")
2519 << 4 << 5 << 4 << true << false << false << false << true
2520 << QString("number")
2521 << (QStringList() << "one" << "two" << "three" << "four" << "eight");
2522
2523 QTest::newRow(dataTag: "ListModel.items insert at 2")
2524 << listModelSource[i]
2525 << QString("items.insert(2, {\"number\": \"eight\"})")
2526 << 4 << 5 << 2 << true << false << false << false << true
2527 << QString("number")
2528 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2529
2530 QTest::newRow(dataTag: "ListModel.items insert at items.get(2)")
2531 << listModelSource[i]
2532 << QString("items.insert(items.get(2), {\"number\": \"eight\"})")
2533 << 4 << 5 << 2 << true << false << false << false << true
2534 << QString("number")
2535 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2536
2537 QTest::newRow(dataTag: "ListModel.items insert at visibleItems.get(2)")
2538 << listModelSource[i]
2539 << QString("items.insert(visibleItems.get(2), {\"number\": \"eight\"})")
2540 << 4 << 5 << 2 << true << false << false << false << true
2541 << QString("number")
2542 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2543
2544 QTest::newRow(dataTag: "ListModel.selectedItems insert at items.get(2)")
2545 << listModelSource[i]
2546 << QString("selectedItems.insert(items.get(2), {\"number\": \"eight\"})")
2547 << 4 << 5 << 2 << false << false << false << true << true
2548 << QString("number")
2549 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2550
2551 QTest::newRow(dataTag: "ListModel.selectedItems insert at visibleItems.get(2)")
2552 << listModelSource[i]
2553 << QString("selectedItems.insert(visibleItems.get(2), {\"number\": \"eight\"})")
2554 << 4 << 5 << 2 << false << false << false << true << true
2555 << QString("number")
2556 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2557
2558 QTest::newRow(dataTag: "ListModel.items prepend modelData")
2559 << listModelSource[i]
2560 << QString("items.insert(0, {\"modelData\": \"eight\"})")
2561 << 4 << 5 << 0 << true << false << false << false << true
2562 << QString("number")
2563 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
2564
2565 QTest::newRow(dataTag: "ListModel.items prepend, edit number")
2566 << listModelSource[i]
2567 << QString("{ "
2568 "items.insert(0, {\"number\": \"eight\"}); "
2569 "items.get(0).model.number = \"seven\"; }")
2570 << 4 << 5 << 0 << true << false << false << false << true
2571 << QString("number")
2572 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
2573
2574 QTest::newRow(dataTag: "ListModel.items prepend, edit modelData")
2575 << listModelSource[i]
2576 << QString("{ "
2577 "items.insert(0, {\"number\": \"eight\"}); "
2578 "items.get(0).model.modelData = \"seven\"; }")
2579 << 4 << 5 << 0 << true << false << false << false << true
2580 << QString("number")
2581 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
2582
2583 QTest::newRow(dataTag: "ListModel.items prepend, edit resolved")
2584 << listModelSource[i]
2585 << QString("{ "
2586 "items.insert(0, {\"number\": \"eight\"}); "
2587 "items.get(2).model.number = \"seven\"; }")
2588 << 4 << 5 << 0 << true << false << false << false << true
2589 << QString("number")
2590 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
2591
2592 QTest::newRow(dataTag: "ListModel.items prepend with groups")
2593 << listModelSource[i]
2594 << QString("items.insert(0, {\"number\": \"eight\"}, [\"visible\", \"truncheon\"])")
2595 << 4 << 5 << 0 << true << false << true << false << true
2596 << QString("number")
2597 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
2598
2599 QTest::newRow(dataTag: "ListModel.items append with groups")
2600 << listModelSource[i]
2601 << QString("items.insert({\"number\": \"eight\"}, [\"visible\", \"selected\"])")
2602 << 4 << 5 << 4 << true << false << true << true << true
2603 << QString("number")
2604 << (QStringList() << "one" << "two" << "three" << "four" << "eight");
2605
2606 QTest::newRow(dataTag: "ListModel.items insert at 2 with groups")
2607 << listModelSource[i]
2608 << QString("items.insert(2, {\"number\": \"eight\"}, \"visible\")")
2609 << 4 << 5 << 2 << true << false << true << false << true
2610 << QString("number")
2611 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2612
2613 // create ListModel
2614 QTest::newRow(dataTag: "ListModel.items prepend")
2615 << listModelSource[i]
2616 << QString("items.create(0, {\"number\": \"eight\"})")
2617 << 4 << 5 << 0 << true << true << false << false << true
2618 << QString("number")
2619 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
2620
2621 QTest::newRow(dataTag: "ListModel.items append")
2622 << listModelSource[i]
2623 << QString("items.create({\"number\": \"eight\"})")
2624 << 4 << 5 << 4 << true << true << false << false << true
2625 << QString("number")
2626 << (QStringList() << "one" << "two" << "three" << "four" << "eight");
2627
2628 QTest::newRow(dataTag: "ListModel.items create at 2")
2629 << listModelSource[i]
2630 << QString("items.create(2, {\"number\": \"eight\"})")
2631 << 4 << 5 << 2 << true << true << false << false << true
2632 << QString("number")
2633 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2634
2635 QTest::newRow(dataTag: "ListModel.items create at items.get(2)")
2636 << listModelSource[i]
2637 << QString("items.create(items.get(2), {\"number\": \"eight\"})")
2638 << 4 << 5 << 2 << true << true << false << false << true
2639 << QString("number")
2640 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2641
2642 QTest::newRow(dataTag: "ListModel.items create at visibleItems.get(2)")
2643 << listModelSource[i]
2644 << QString("items.create(visibleItems.get(2), {\"number\": \"eight\"})")
2645 << 4 << 5 << 2 << true << true << false << false << true
2646 << QString("number")
2647 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2648
2649 QTest::newRow(dataTag: "ListModel.selectedItems create at items.get(2)")
2650 << listModelSource[i]
2651 << QString("selectedItems.create(items.get(2), {\"number\": \"eight\"})")
2652 << 4 << 5 << 2 << false << true << false << true << true
2653 << QString("number")
2654 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2655
2656 QTest::newRow(dataTag: "ListModel.selectedItems create at visibleItems.get(2)")
2657 << listModelSource[i]
2658 << QString("selectedItems.create(visibleItems.get(2), {\"number\": \"eight\"})")
2659 << 4 << 5 << 2 << false << true << false << true << true
2660 << QString("number")
2661 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2662
2663 QTest::newRow(dataTag: "ListModel.items create prepended")
2664 << listModelSource[i]
2665 << QString("items.create(0, {\"number\": \"eight\"})")
2666 << 4 << 5 << 0 << true << true << false << false << true
2667 << QString("number")
2668 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
2669
2670 QTest::newRow(dataTag: "ListModel.items create appended")
2671 << listModelSource[i]
2672 << QString("items.create({\"number\": \"eight\"})")
2673 << 4 << 5 << 4 << true << true << false << false << true
2674 << QString("number")
2675 << (QStringList() << "one" << "two" << "three" << "four" << "eight");
2676
2677 QTest::newRow(dataTag: "ListModel.items create at 2")
2678 << listModelSource[i]
2679 << QString("items.create(2, {\"number\": \"eight\"})")
2680 << 4 << 5 << 2 << true << true << false << false << true
2681 << QString("number")
2682 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2683
2684 QTest::newRow(dataTag: "ListModel.items create at items.get(2)")
2685 << listModelSource[i]
2686 << QString("items.create(items.get(2), {\"number\": \"eight\"})")
2687 << 4 << 5 << 2 << true << true << false << false << true
2688 << QString("number")
2689 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2690
2691 QTest::newRow(dataTag: "ListModel.items create at visibleItems.get(2)")
2692 << listModelSource[i]
2693 << QString("items.create(visibleItems.get(2), {\"number\": \"eight\"})")
2694 << 4 << 5 << 2 << true << true << false << false << true
2695 << QString("number")
2696 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2697
2698 QTest::newRow(dataTag: "ListModel.create prepend modelData")
2699 << listModelSource[i]
2700 << QString("items.create(0, {\"modelData\": \"eight\"})")
2701 << 4 << 5 << 0 << true << true << false << false << true
2702 << QString("number")
2703 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
2704
2705 QTest::newRow(dataTag: "ListModel.items create prepended, edit number")
2706 << listModelSource[i]
2707 << QString("{ "
2708 "var item = items.create(0, {\"number\": \"eight\"}); "
2709 "item.setTest3(\"seven\"); }")
2710 << 4 << 5 << 0 << true << true << false << false << true
2711 << QString("number")
2712 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
2713
2714 QTest::newRow(dataTag: "ListModel.items create prepended, edit model.number")
2715 << listModelSource[i]
2716 << QString("{ "
2717 "var item = items.create(0, {\"number\": \"eight\"}); "
2718 "item.setTest4(\"seven\"); }")
2719 << 4 << 5 << 0 << true << true << false << false << true
2720 << QString("number")
2721 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
2722
2723 QTest::newRow(dataTag: "ListModel.items create prepended, edit modelData")
2724 << listModelSource[i]
2725 << QString("{ "
2726 "var item = items.create(0, {\"number\": \"eight\"}); "
2727 "item.setTest5(\"seven\"); }")
2728 << 4 << 5 << 0 << true << true << false << false << true
2729 << QString("number")
2730 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
2731
2732 QTest::newRow(dataTag: "ListModel.items create prepended, edit model.modelData")
2733 << listModelSource[i]
2734 << QString("{ "
2735 "var item = items.create(0, {\"number\": \"eight\"}); "
2736 "item.setTest6(\"seven\"); }")
2737 << 4 << 5 << 0 << true << true << false << false << true
2738 << QString("number")
2739 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
2740
2741 QTest::newRow(dataTag: "ListModel.items create prepended with groups")
2742 << listModelSource[i]
2743 << QString("items.create(0, {\"number\": \"eight\"}, [\"visible\", \"truncheon\"])")
2744 << 4 << 5 << 0 << true << true << true << false << true
2745 << QString("number")
2746 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
2747
2748 QTest::newRow(dataTag: "ListModel.items create appended with groups")
2749 << listModelSource[i]
2750 << QString("items.create({\"number\": \"eight\"}, [\"visible\", \"selected\"])")
2751 << 4 << 5 << 4 << true << true << true << true << true
2752 << QString("number")
2753 << (QStringList() << "one" << "two" << "three" << "four" << "eight");
2754
2755 QTest::newRow(dataTag: "ListModel.items create inserted with groups")
2756 << listModelSource[i]
2757 << QString("items.create(2, {\"number\": \"eight\"}, \"visible\")")
2758 << 4 << 5 << 2 << true << true << true << false << true
2759 << QString("number")
2760 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2761
2762 QTest::newRow(dataTag: "ListModel.items create prepended clear persistence")
2763 << listModelSource[i]
2764 << QString("{ items.create(0, {\"number\": \"eight\"}); "
2765 "items.get(0).inPersistedItems = false }")
2766 << 4 << 5 << 0 << true << false << false << false << true
2767 << QString("number")
2768 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
2769
2770 QTest::newRow(dataTag: "ListModel.items create appended clear persistence")
2771 << listModelSource[i]
2772 << QString("{ items.create({\"number\": \"eight\"}); "
2773 "items.get(4).inPersistedItems = false }")
2774 << 4 << 5 << 4 << true << false << false << false << true
2775 << QString("number")
2776 << (QStringList() << "one" << "two" << "three" << "four" << "eight");
2777
2778 QTest::newRow(dataTag: "ListModel.items create inserted clear persistence")
2779 << listModelSource[i]
2780 << QString("{ items.create(2, {\"number\": \"eight\"}); "
2781 "items.get(2).inPersistedItems = false }")
2782 << 4 << 5 << 2 << true << false << false << false << true
2783 << QString("number")
2784 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2785
2786 // AbstractItemModel (Single Role).
2787 QTest::newRow(dataTag: "AbstractItemModel.items prepend")
2788 << singleRoleSource[i]
2789 << QString("items.insert(0, {\"name\": \"eight\"})")
2790 << 4 << 5 << 0 << true << false << false << false << true
2791 << QString("name")
2792 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
2793
2794 QTest::newRow(dataTag: "AbstractItemModel.items append")
2795 << singleRoleSource[i]
2796 << QString("items.insert({\"name\": \"eight\"})")
2797 << 4 << 5 << 4 << true << false << false << false << true
2798 << QString("name")
2799 << (QStringList() << "one" << "two" << "three" << "four" << "eight");
2800
2801 QTest::newRow(dataTag: "AbstractItemModel.items insert at 2")
2802 << singleRoleSource[i]
2803 << QString("items.insert(2, {\"name\": \"eight\"})")
2804 << 4 << 5 << 2 << true << false << false << false << true
2805 << QString("name")
2806 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2807
2808 QTest::newRow(dataTag: "AbstractItemModel.items prepend modelData")
2809 << singleRoleSource[i]
2810 << QString("items.insert(0, {\"modelData\": \"eight\"})")
2811 << 4 << 5 << 0 << true << false << false << false << true
2812 << QString("name")
2813 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
2814
2815 QTest::newRow(dataTag: "AbstractItemModel.items prepend, edit name")
2816 << singleRoleSource[i]
2817 << QString("{ "
2818 "items.insert(0, {\"name\": \"eight\"}); "
2819 "items.get(0).model.name = \"seven\"; }")
2820 << 4 << 5 << 0 << true << false << false << false << true
2821 << QString("name")
2822 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
2823
2824 QTest::newRow(dataTag: "AbstractItemModel.items prepend, edit modelData")
2825 << singleRoleSource[i]
2826 << QString("{ "
2827 "items.insert(0, {\"name\": \"eight\"}); "
2828 "items.get(0).model.modelData = \"seven\"; }")
2829 << 4 << 5 << 0 << true << false << false << false << true
2830 << QString("name")
2831 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
2832
2833 QTest::newRow(dataTag: "AbstractItemModel.items prepend, edit resolved")
2834 << singleRoleSource[i]
2835 << QString("{ "
2836 "items.insert(0, {\"name\": \"eight\"}); "
2837 "items.get(2).model.name = \"seven\"; }")
2838 << 4 << 5 << 0 << true << false << false << false << true
2839 << QString("name")
2840 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
2841
2842 QTest::newRow(dataTag: "AbstractItemModel.create prepend modelData")
2843 << singleRoleSource[i]
2844 << QString("items.create(0, {\"modelData\": \"eight\"})")
2845 << 4 << 5 << 0 << true << true << false << false << true
2846 << QString("name")
2847 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
2848
2849 QTest::newRow(dataTag: "AbstractItemModel.items create prepended, edit name")
2850 << singleRoleSource[i]
2851 << QString("{ "
2852 "var item = items.create(0, {\"name\": \"eight\"}); "
2853 "item.setTest3(\"seven\"); }")
2854 << 4 << 5 << 0 << true << true << false << false << true
2855 << QString("name")
2856 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
2857
2858 QTest::newRow(dataTag: "AbstractItemModel.items create prepended, edit model.name")
2859 << singleRoleSource[i]
2860 << QString("{ "
2861 "var item = items.create(0, {\"name\": \"eight\"}); "
2862 "item.setTest4(\"seven\"); }")
2863 << 4 << 5 << 0 << true << true << false << false << true
2864 << QString("name")
2865 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
2866
2867 QTest::newRow(dataTag: "AbstractItemModel.items create prepended, edit modelData")
2868 << singleRoleSource[i]
2869 << QString("{ "
2870 "var item = items.create(0, {\"name\": \"eight\"}); "
2871 "item.setTest5(\"seven\"); }")
2872 << 4 << 5 << 0 << true << true << false << false << true
2873 << QString("name")
2874 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
2875
2876 QTest::newRow(dataTag: "AbstractItemModel.items create prepended, edit model.modelData")
2877 << singleRoleSource[i]
2878 << QString("{ "
2879 "var item = items.create(0, {\"name\": \"eight\"}); "
2880 "item.setTest6(\"seven\"); }")
2881 << 4 << 5 << 0 << true << true << false << false << true
2882 << QString("name")
2883 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
2884
2885 // AbstractItemModel (Multiple Roles).
2886 QTest::newRow(dataTag: "StandardItemModel.items prepend")
2887 << multipleRoleSource[i]
2888 << QString("items.insert(0, {\"display\": \"Row 8 Item\"})")
2889 << 4 << 5 << 0 << true << false << false << false << false
2890 << QString("display")
2891 << (QStringList() << "Row 8 Item" << "Row 1 Item" << "Row 2 Item" << "Row 3 Item" << "Row 4 Item");
2892
2893 QTest::newRow(dataTag: "StandardItemModel.items append")
2894 << multipleRoleSource[i]
2895 << QString("items.insert({\"display\": \"Row 8 Item\"})")
2896 << 4 << 5 << 4 << true << false << false << false << false
2897 << QString("display")
2898 << (QStringList() << "Row 1 Item" << "Row 2 Item" << "Row 3 Item" << "Row 4 Item" << "Row 8 Item");
2899
2900 QTest::newRow(dataTag: "StandardItemModel.items insert at 2")
2901 << multipleRoleSource[i]
2902 << QString("items.insert(2, {\"display\": \"Row 8 Item\"})")
2903 << 4 << 5 << 2 << true << false << false << false << false
2904 << QString("display")
2905 << (QStringList() << "Row 1 Item" << "Row 2 Item" << "Row 8 Item" << "Row 3 Item" << "Row 4 Item");
2906
2907 QTest::newRow(dataTag: "StandardItemModel.items prepend modelData")
2908 << multipleRoleSource[i]
2909 << QString("items.insert(0, {\"modelData\": \"Row 8 Item\"})")
2910 << 4 << 5 << 0 << true << false << false << false << false
2911 << QString("display")
2912 << (QStringList() << QString() << "Row 1 Item" << "Row 2 Item" << "Row 3 Item" << "Row 4 Item");
2913
2914 QTest::newRow(dataTag: "StandardItemModel.items prepend, edit display")
2915 << multipleRoleSource[i]
2916 << QString("{ "
2917 "items.insert(0, {\"display\": \"Row 8 Item\"}); "
2918 "items.get(0).model.display = \"Row 7 Item\"; }")
2919 << 4 << 5 << 0 << true << false << false << false << false
2920 << QString("display")
2921 << (QStringList() << "Row 7 Item" << "Row 1 Item" << "Row 2 Item" << "Row 3 Item" << "Row 4 Item");
2922
2923 QTest::newRow(dataTag: "StandardItemModel.items prepend, edit modelData")
2924 << multipleRoleSource[i]
2925 << QString("{ "
2926 "items.insert(0, {\"display\": \"Row 8 Item\"}); "
2927 "items.get(0).model.modelData = \"Row 7 Item\"; }")
2928 << 4 << 5 << 0 << true << false << false << false << false
2929 << QString("display")
2930 << (QStringList() << "Row 8 Item" << "Row 1 Item" << "Row 2 Item" << "Row 3 Item" << "Row 4 Item");
2931
2932 QTest::newRow(dataTag: "StandardItemModel.items prepend, edit resolved")
2933 << multipleRoleSource[i]
2934 << QString("{ "
2935 "items.insert(0, {\"display\": \"Row 8 Item\"}); "
2936 "items.get(2).model.display = \"Row 7 Item\"; }")
2937 << 4 << 5 << 0 << true << false << false << false << false
2938 << QString("display")
2939 << (QStringList() << "Row 8 Item" << "Row 1 Item" << "Row 2 Item" << "Row 3 Item" << "Row 4 Item");
2940
2941 QTest::newRow(dataTag: "StandardItemModel.create prepend modelData")
2942 << multipleRoleSource[i]
2943 << QString("items.create(0, {\"modelData\": \"Row 8 Item\"})")
2944 << 4 << 5 << 0 << true << true << false << false << false
2945 << QString("display")
2946 << (QStringList() << QString() << "Row 1 Item" << "Row 2 Item" << "Row 3 Item" << "Row 4 Item");
2947
2948 QTest::newRow(dataTag: "StandardItemModel.items create prepended, edit display")
2949 << multipleRoleSource[i]
2950 << QString("{ "
2951 "var item = items.create(0, {\"display\": \"Row 8 Item\"}); "
2952 "item.setTest3(\"Row 7 Item\"); }")
2953 << 4 << 5 << 0 << true << true << false << false << false
2954 << QString("display")
2955 << (QStringList() << "Row 7 Item" << "Row 1 Item" << "Row 2 Item" << "Row 3 Item" << "Row 4 Item");
2956
2957 QTest::newRow(dataTag: "StandardItemModel.items create prepended, edit model.display")
2958 << multipleRoleSource[i]
2959 << QString("{ "
2960 "var item = items.create(0, {\"display\": \"Row 8 Item\"}); "
2961 "item.setTest4(\"Row 7 Item\"); }")
2962 << 4 << 5 << 0 << true << true << false << false << false
2963 << QString("display")
2964 << (QStringList() << "Row 7 Item" << "Row 1 Item" << "Row 2 Item" << "Row 3 Item" << "Row 4 Item");
2965
2966 // StringList.
2967 QTest::newRow(dataTag: "StringList.items prepend")
2968 << stringListSource[i]
2969 << QString("items.insert(0, {\"modelData\": \"eight\"})")
2970 << 4 << 5 << 0 << true << false << false << false << false
2971 << QString("modelData")
2972 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
2973
2974 QTest::newRow(dataTag: "StringList.items append")
2975 << stringListSource[i]
2976 << QString("items.insert({\"modelData\": \"eight\"})")
2977 << 4 << 5 << 4 << true << false << false << false << false
2978 << QString("modelData")
2979 << (QStringList() << "one" << "two" << "three" << "four" << "eight");
2980
2981 QTest::newRow(dataTag: "StringList.items insert at 2")
2982 << stringListSource[i]
2983 << QString("items.insert(2, {\"modelData\": \"eight\"})")
2984 << 4 << 5 << 2 << true << false << false << false << false
2985 << QString("modelData")
2986 << (QStringList() << "one" << "two" << "eight" << "three" << "four");
2987
2988 QTest::newRow(dataTag: "StringList.items prepend, edit modelData")
2989 << stringListSource[i]
2990 << QString("{ "
2991 "items.insert(0, {\"modelData\": \"eight\"}); "
2992 "items.get(0).model.modelData = \"seven\"; }")
2993 << 4 << 5 << 0 << true << false << false << false << false
2994 << QString("modelData")
2995 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
2996
2997 QTest::newRow(dataTag: "StringList.items prepend, edit resolved")
2998 << stringListSource[i]
2999 << QString("{ "
3000 "items.insert(0, {\"modelData\": \"eight\"}); "
3001 "items.get(2).model.modelData = \"seven\"; }")
3002 << 4 << 5 << 0 << true << false << false << false << false
3003 << QString("modelData")
3004 << (QStringList() << "eight" << "one" << "seven" << "three" << "four");
3005
3006 QTest::newRow(dataTag: "StringList.create prepend modelData")
3007 << stringListSource[i]
3008 << QString("items.create(0, {\"modelData\": \"eight\"})")
3009 << 4 << 5 << 0 << true << true << false << false << false
3010 << QString("modelData")
3011 << (QStringList() << "eight" << "one" << "two" << "three" << "four");
3012
3013 QTest::newRow(dataTag: "StringList.items create prepended, edit modelData")
3014 << stringListSource[i]
3015 << QString("{ "
3016 "var item = items.create(0, {\"modelData\": \"eight\"}); "
3017 "item.setTest3(\"seven\"); }")
3018 << 4 << 5 << 0 << true << true << false << false << false
3019 << QString("modelData")
3020 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
3021
3022 QTest::newRow(dataTag: "StringList.items create prepended, edit model.modelData")
3023 << stringListSource[i]
3024 << QString("{ "
3025 "var item = items.create(0, {\"modelData\": \"eight\"}); "
3026 "item.setTest4(\"seven\"); }")
3027 << 4 << 5 << 0 << true << true << false << false << false
3028 << QString("modelData")
3029 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
3030
3031 // ObjectList
3032 QTest::newRow(dataTag: "ObjectList.items prepend")
3033 << objectListSource[i]
3034 << QString("items.insert(0, {\"name\": \"Item 8\"})")
3035 << 4 << 4 << 4 << false << false << false << false << false
3036 << QString("name")
3037 << (QStringList() << "Item 1" << "Item 2" << "Item 3" << "Item 4");
3038
3039 QTest::newRow(dataTag: "ObjectList.items append")
3040 << objectListSource[i]
3041 << QString("items.insert({\"name\": \"Item 8\"})")
3042 << 4 << 4 << 4 << false << false << false << false << false
3043 << QString("name")
3044 << (QStringList() << "Item 1" << "Item 2" << "Item 3" << "Item 4");
3045
3046 QTest::newRow(dataTag: "ObjectList.items insert at 2")
3047 << objectListSource[i]
3048 << QString("items.insert(2, {\"name\": \"Item 8\"})")
3049 << 4 << 4 << 4 << false << false << false << false << false
3050 << QString("name")
3051 << (QStringList() << "Item 1" << "Item 2" << "Item 3" << "Item 4");
3052 }
3053}
3054
3055void tst_qquickvisualdatamodel::insert()
3056{
3057 QFETCH(QUrl, source);
3058 QFETCH(QString, expression);
3059 QFETCH(int, modelCount);
3060 QFETCH(int, visualCount);
3061 QFETCH(int, index);
3062 QFETCH(bool, inItems);
3063 QFETCH(bool, persisted);
3064 QFETCH(bool, visible);
3065 QFETCH(bool, selected);
3066 QFETCH(bool, modelData);
3067 QFETCH(QString, property);
3068 QFETCH(QStringList, propertyData);
3069
3070 QQuickWindow window;
3071
3072 QQmlComponent component(&engine);
3073 component.loadUrl(url: source);
3074 QScopedPointer<QObject> object(component.create());
3075 QQuickListView *listView = qobject_cast<QQuickListView *>(object: object.data());
3076 QVERIFY(listView);
3077 listView->setParentItem(window.contentItem());
3078
3079 QQuickItem *contentItem = listView->contentItem();
3080 QVERIFY(contentItem);
3081
3082 QObject *visualModel = listView->findChild<QObject *>(aName: "visualModel");
3083 QVERIFY(visualModel);
3084
3085 evaluate<void>(scope: visualModel, expression);
3086
3087 QCOMPARE(evaluate<int>(listView, "count"), inItems ? visualCount : modelCount);
3088 QCOMPARE(evaluate<int>(visualModel, "count"), inItems ? visualCount : modelCount);
3089 QCOMPARE(evaluate<int>(visualModel, "items.count"), inItems ? visualCount : modelCount);
3090 QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), persisted ? 1 : 0);
3091 QCOMPARE(evaluate<int>(visualModel, "visibleItems.count"), visible ? visualCount : modelCount);
3092 QCOMPARE(evaluate<int>(visualModel, "selectedItems.count"), selected ? 1 : 0);
3093
3094 QCOMPARE(propertyData.count(), visualCount);
3095 for (int i = 0; i < visualCount; ++i) {
3096 int modelIndex = i;
3097 if (modelIndex > index)
3098 modelIndex -= 1;
3099 else if (modelIndex == index)
3100 modelIndex = -1;
3101
3102 const int itemsIndex = inItems || i <= index ? i : i - 1;
3103 QString get;
3104
3105 if (i != index) {
3106 get = QString("items.get(%1)").arg(a: itemsIndex);
3107
3108 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: modelIndex);
3109 QVERIFY(item);
3110
3111 QCOMPARE(evaluate<int>(item, "test1"), modelIndex);
3112 QCOMPARE(evaluate<int>(item, "test2"), modelIndex);
3113 QCOMPARE(evaluate<QString>(item, "test3"), propertyData.at(i));
3114 QCOMPARE(evaluate<QString>(item, "test4"), propertyData.at(i));
3115
3116 if (modelData) {
3117 QCOMPARE(evaluate<QString>(item, "test5"), propertyData.at(i));
3118 QCOMPARE(evaluate<QString>(item, "test6"), propertyData.at(i));
3119 }
3120
3121 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inItems"), true);
3122 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inPersistedItems"), false);
3123 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inVisible"), true);
3124 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inSelected"), false);
3125 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.isUnresolved"), false);
3126
3127 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.itemsIndex"), itemsIndex);
3128 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.persistedItemsIndex"), persisted && i > index ? 1 : 0);
3129 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.visibleIndex"), visible || i <= index ? i : i - 1);
3130 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.selectedIndex"), selected && i > index ? 1 : 0);
3131 } else if (inItems) {
3132 get = QString("items.get(%1)").arg(a: index);
3133 } else if (persisted) {
3134 get = "persistedItems.get(0)";
3135 } else if (visible) {
3136 get = QString("visibleItems.get(%1)").arg(a: index);
3137 } else if (selected) {
3138 get = "selectedItems.get(0)";
3139 } else {
3140 continue;
3141 }
3142
3143 QCOMPARE(evaluate<int>(visualModel, get + ".model.index"), modelIndex);
3144
3145 QCOMPARE(evaluate<QString>(visualModel, get + ".model." + property), propertyData.at(i));
3146
3147 QCOMPARE(evaluate<bool>(visualModel, get + ".inItems"), inItems || i != index);
3148 QCOMPARE(evaluate<bool>(visualModel, get + ".inPersistedItems"), persisted && i == index);
3149 QCOMPARE(evaluate<bool>(visualModel, get + ".inVisible"), visible || i != index);
3150 QCOMPARE(evaluate<bool>(visualModel, get + ".inSelected"), selected && i == index);
3151 QCOMPARE(evaluate<bool>(visualModel, get + ".isUnresolved"), i == index);
3152
3153 QCOMPARE(evaluate<int>(visualModel, get + ".itemsIndex"), inItems || i <= index ? i : i - 1);
3154 QCOMPARE(evaluate<int>(visualModel, get + ".persistedItemsIndex"), persisted && i > index ? 1 : 0);
3155 QCOMPARE(evaluate<int>(visualModel, get + ".visibleIndex"), visible || i <= index ? i : i - 1);
3156 QCOMPARE(evaluate<int>(visualModel, get + ".selectedIndex"), selected && i > index ? 1 : 0);
3157 }
3158
3159 QObject *item = nullptr;
3160
3161 if (inItems)
3162 item = evaluate<QObject *>(scope: visualModel, expression: QString("items.create(%1)").arg(a: index));
3163 else if (persisted)
3164 item = evaluate<QObject *>(scope: visualModel, expression: QString("persistedItems.create(%1)").arg(a: 0));
3165 else if (visible)
3166 item = evaluate<QObject *>(scope: visualModel, expression: QString("visibleItems.create(%1)").arg(a: index));
3167 else if (selected)
3168 item = evaluate<QObject *>(scope: visualModel, expression: QString("selectedItems.create(%1)").arg(a: 0));
3169 else
3170 return;
3171
3172 QVERIFY(item);
3173
3174 QCOMPARE(evaluate<int>(item, "test1"), -1);
3175 QCOMPARE(evaluate<int>(item, "test2"), -1);
3176 QCOMPARE(evaluate<QString>(item, "test3"), propertyData.at(index));
3177 QCOMPARE(evaluate<QString>(item, "test4"), propertyData.at(index));
3178
3179 if (modelData) {
3180 QCOMPARE(evaluate<QString>(item, "test5"), propertyData.at(index));
3181 QCOMPARE(evaluate<QString>(item, "test6"), propertyData.at(index));
3182 }
3183
3184 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inItems"), inItems);
3185 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inPersistedItems"), true);
3186 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inVisible"), visible);
3187 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inSelected"), selected);
3188 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.isUnresolved"), true);
3189
3190 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.itemsIndex"), index);
3191 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.persistedItemsIndex"), 0);
3192 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.visibleIndex"), index);
3193 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.selectedIndex"), 0);
3194}
3195
3196void tst_qquickvisualdatamodel::resolve_data()
3197{
3198 QTest::addColumn<QUrl>(name: "source");
3199 QTest::addColumn<QString>(name: "setupExpression");
3200 QTest::addColumn<QString>(name: "resolveExpression");
3201 QTest::addColumn<int>(name: "unresolvedCount");
3202 QTest::addColumn<int>(name: "modelCount");
3203 QTest::addColumn<int>(name: "visualCount");
3204 QTest::addColumn<int>(name: "index");
3205 QTest::addColumn<bool>(name: "inItems");
3206 QTest::addColumn<bool>(name: "persisted");
3207 QTest::addColumn<bool>(name: "visible");
3208 QTest::addColumn<bool>(name: "selected");
3209 QTest::addColumn<bool>(name: "modelData");
3210 QTest::addColumn<QString>(name: "property");
3211 QTest::addColumn<QStringList>(name: "propertyData");
3212
3213 const QUrl listModelSource[] = {
3214 testFileUrl(fileName: "listmodelproperties.qml"),
3215 testFileUrl(fileName: "listmodelproperties-package.qml") };
3216 const QUrl singleRoleSource[] = {
3217 testFileUrl(fileName: "singleroleproperties.qml"),
3218 testFileUrl(fileName: "singleroleproperties-package.qml") };
3219 const QUrl multipleRoleSource[] = {
3220 testFileUrl(fileName: "multipleroleproperties.qml"),
3221 testFileUrl(fileName: "multipleroleproperties-package.qml") };
3222 const QUrl stringListSource[] = {
3223 testFileUrl(fileName: "stringlistproperties.qml"),
3224 testFileUrl(fileName: "stringlistproperties-package.qml") };
3225
3226 for (int i = 0; i < 2; ++i) {
3227 // List Model.
3228 QTest::newRow(dataTag: "ListModel.items prepend, resolve prepended")
3229 << listModelSource[i]
3230 << QString("items.insert(0, {\"number\": \"eight\"})")
3231 << QString("{ listModel.insert(0, {\"number\": \"seven\"}); items.resolve(0, 1) }")
3232 << 5 << 5 << 5 << 0 << true << false << true << false << true
3233 << QString("number")
3234 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
3235
3236 QTest::newRow(dataTag: "ListModel.items prepend, resolve appended")
3237 << listModelSource[i]
3238 << QString("items.insert(0, {\"number\": \"eight\"})")
3239 << QString("{ listModel.append({\"number\": \"seven\"}); items.resolve(0, 5) }")
3240 << 5 << 5 << 5 << 4 << true << false << true << false << true
3241 << QString("number")
3242 << (QStringList() << "one" << "two" << "three" << "four" << "seven");
3243
3244 QTest::newRow(dataTag: "ListModel.items prepend, resolve inserted")
3245 << listModelSource[i]
3246 << QString("items.insert(0, {\"number\": \"eight\"})")
3247 << QString("{ listModel.insert(2, {\"number\": \"seven\"}); items.resolve(0, 3) }")
3248 << 5 << 5 << 5 << 2 << true << false << true << false << true
3249 << QString("number")
3250 << (QStringList() << "one" << "two" << "seven" << "three" << "four");
3251
3252 QTest::newRow(dataTag: "ListModel.items append, resolve prepended")
3253 << listModelSource[i]
3254 << QString("items.insert({\"number\": \"eight\"})")
3255 << QString("{ listModel.insert(0, {\"number\": \"seven\"}); items.resolve(5, 0) }")
3256 << 5 << 5 << 5 << 0 << true << false << true << false << true
3257 << QString("number")
3258 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
3259
3260 QTest::newRow(dataTag: "ListModel.items append, resolve appended")
3261 << listModelSource[i]
3262 << QString("items.insert({\"number\": \"eight\"})")
3263 << QString("{ listModel.append({\"number\": \"seven\"}); items.resolve(5, 4) }")
3264 << 5 << 5 << 5 << 4 << true << false << true << false << true
3265 << QString("number")
3266 << (QStringList() << "one" << "two" << "three" << "four" << "seven");
3267
3268 QTest::newRow(dataTag: "ListModel.items append, resolve inserted")
3269 << listModelSource[i]
3270 << QString("items.insert({\"number\": \"eight\"})")
3271 << QString("{ listModel.insert(2, {\"number\": \"seven\"}); items.resolve(5, 2) }")
3272 << 5 << 5 << 5 << 2 << true << false << true << false << true
3273 << QString("number")
3274 << (QStringList() << "one" << "two" << "seven" << "three" << "four");
3275
3276 QTest::newRow(dataTag: "ListModel.items insert, resolve prepended")
3277 << listModelSource[i]
3278 << QString("items.insert(2, {\"number\": \"eight\"})")
3279 << QString("{ listModel.insert(0, {\"number\": \"seven\"}); items.resolve(3, 0) }")
3280 << 5 << 5 << 5 << 0 << true << false << true << false << true
3281 << QString("number")
3282 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
3283
3284 QTest::newRow(dataTag: "ListModel.items insert, resolve appended")
3285 << listModelSource[i]
3286 << QString("items.insert(2, {\"number\": \"eight\"})")
3287 << QString("{ listModel.append({\"number\": \"seven\"}); items.resolve(2, 5) }")
3288 << 5 << 5 << 5 << 4 << true << false << true << false << true
3289 << QString("number")
3290 << (QStringList() << "one" << "two" << "three" << "four" << "seven");
3291
3292 QTest::newRow(dataTag: "ListModel.items insert, resolve inserted")
3293 << listModelSource[i]
3294 << QString("items.insert(2, {\"number\": \"eight\"})")
3295 << QString("{ listModel.insert(2, {\"number\": \"seven\"}); items.resolve(2, 3) }")
3296 << 5 << 5 << 5 << 2 << true << false << true << false << true
3297 << QString("number")
3298 << (QStringList() << "one" << "two" << "seven" << "three" << "four");
3299
3300 QTest::newRow(dataTag: "ListModel.items prepend, move resolved")
3301 << listModelSource[i]
3302 << QString("items.insert(0, {\"number\": \"eight\"})")
3303 << QString("{ listModel.insert(0, {\"number\": \"seven\"}); "
3304 "items.resolve(0, 1); "
3305 "listModel.move(0, 2, 1) }")
3306 << 5 << 5 << 5 << 2 << true << false << true << false << true
3307 << QString("number")
3308 << (QStringList() << "one" << "two" << "seven" << "three" << "four");
3309
3310 QTest::newRow(dataTag: "ListModel.items append, move resolved")
3311 << listModelSource[i]
3312 << QString("items.insert({\"number\": \"eight\"})")
3313 << QString("{ listModel.append({\"number\": \"seven\"}); "
3314 "items.resolve(5, 4); "
3315 "listModel.move(4, 2, 1) }")
3316 << 5 << 5 << 5 << 2 << true << false << true << false << true
3317 << QString("number")
3318 << (QStringList() << "one" << "two" << "seven" << "three" << "four");
3319
3320 QTest::newRow(dataTag: "ListModel.items insert, move resolved")
3321 << listModelSource[i]
3322 << QString("items.insert(2, {\"number\": \"eight\"})")
3323 << QString("{ listModel.insert(2, {\"number\": \"seven\"}); "
3324 "items.resolve(2, 3);"
3325 "listModel.move(2, 0, 1) }")
3326 << 5 << 5 << 5 << 0 << true << false << true << false << true
3327 << QString("number")
3328 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
3329
3330 QTest::newRow(dataTag: "ListModel.items prepend, remove resolved")
3331 << listModelSource[i]
3332 << QString("items.insert(0, {\"number\": \"eight\"})")
3333 << QString("{ listModel.insert(0, {\"number\": \"seven\"}); "
3334 "items.resolve(0, 1); "
3335 "listModel.remove(0, 1) }")
3336 << 5 << 4 << 4 << 4 << false << false << false << false << true
3337 << QString("number")
3338 << (QStringList() << "one" << "two" << "three" << "four");
3339
3340 QTest::newRow(dataTag: "ListModel.items append, remove resolved")
3341 << listModelSource[i]
3342 << QString("items.insert({\"number\": \"eight\"})")
3343 << QString("{ listModel.append({\"number\": \"seven\"}); "
3344 "items.resolve(5, 4); "
3345 "listModel.remove(4, 1) }")
3346 << 5 << 4 << 4 << 4 << false << false << false << false << true
3347 << QString("number")
3348 << (QStringList() << "one" << "two" << "three" << "four");
3349
3350 QTest::newRow(dataTag: "ListModel.items insert, remove resolved")
3351 << listModelSource[i]
3352 << QString("items.insert(2, {\"number\": \"eight\"})")
3353 << QString("{ listModel.insert(2, {\"number\": \"seven\"}); "
3354 "items.resolve(2, 3);"
3355 "listModel.remove(2, 1) }")
3356 << 5 << 4 << 4 << 4 << false << false << false << false << true
3357 << QString("number")
3358 << (QStringList() << "one" << "two" << "three" << "four");
3359
3360 QTest::newRow(dataTag: "ListModel.selectedItems prepend, resolve prepended")
3361 << listModelSource[i]
3362 << QString("selectedItems.insert(0, {\"number\": \"eight\"})")
3363 << QString("{ listModel.insert(0, {\"number\": \"seven\"}); "
3364 "selectedItems.resolve(selectedItems.get(0), items.get(0)) }")
3365 << 4 << 5 << 5 << 0 << true << false << true << true << true
3366 << QString("number")
3367 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
3368
3369 QTest::newRow(dataTag: "ListModel.selectedItems prepend, resolve appended")
3370 << listModelSource[i]
3371 << QString("selectedItems.insert(0, {\"number\": \"eight\"})")
3372 << QString("{ listModel.append({\"number\": \"seven\"}); "
3373 "selectedItems.resolve(selectedItems.get(0), items.get(4)) }")
3374 << 4 << 5 << 5 << 4 << true << false << true << true << true
3375 << QString("number")
3376 << (QStringList() << "one" << "two" << "three" << "four" << "seven");
3377
3378 QTest::newRow(dataTag: "ListModel.selectedItems prepend, resolve inserted")
3379 << listModelSource[i]
3380 << QString("selectedItems.insert(0, {\"number\": \"eight\"})")
3381 << QString("{ listModel.insert(2, {\"number\": \"seven\"}); "
3382 "selectedItems.resolve(selectedItems.get(0), items.get(2)) }")
3383 << 4 << 5 << 5 << 2 << true << false << true << true << true
3384 << QString("number")
3385 << (QStringList() << "one" << "two" << "seven" << "three" << "four");
3386
3387 QTest::newRow(dataTag: "ListModel.selectedItems append, resolve prepended")
3388 << listModelSource[i]
3389 << QString("selectedItems.insert({\"number\": \"eight\"})")
3390 << QString("{ listModel.insert(0, {\"number\": \"seven\"}); "
3391 "selectedItems.resolve(selectedItems.get(0), items.get(0)) }")
3392 << 4 << 5 << 5 << 0 << true << false << true << true << true
3393 << QString("number")
3394 << (QStringList() << "seven" << "one" << "two" << "three" << "four");
3395
3396 QTest::newRow(dataTag: "ListModel.selectedItems append, resolve appended")
3397 << listModelSource[i]
3398 << QString("selectedItems.insert({\"number\": \"eight\"})")
3399 << QString("{ listModel.append({\"number\": \"seven\"}); "
3400 "selectedItems.resolve(selectedItems.get(0), items.get(4)) }")
3401 << 4 << 5 << 5 << 4 << true << false << true << true << true
3402 << QString("number")
3403 << (QStringList() << "one" << "two" << "three" << "four" << "seven");
3404
3405 QTest::newRow(dataTag: "ListModel.selectedItems append, resolve inserted")
3406 << listModelSource[i]
3407 << QString("selectedItems.insert({\"number\": \"eight\"})")
3408 << QString("{ listModel.insert(2, {\"number\": \"seven\"}); "
3409 "selectedItems.resolve(selectedItems.get(0), items.get(2)) }")
3410 << 4 << 5 << 5 << 2 << true << false << true << true << true
3411 << QString("number")
3412 << (QStringList() << "one" << "two" << "seven" << "three" << "four");
3413
3414 // AbstractItemModel (Single Role)
3415 QTest::newRow(dataTag: "ListModel.items prepend, resolve prepended")
3416 << singleRoleSource[i]
3417 << QString("items.insert(0, {\"name\": \"eight\"})")
3418 << QString("{ items.resolve(0, 1) }")
3419 << 5 << 4 << 4 << 0 << true << false << true << false << true
3420 << QString("name")
3421 << (QStringList() << "one" << "two" << "three" << "four");
3422
3423
3424 QTest::newRow(dataTag: "ListModel.items append, resolve appended")
3425 << singleRoleSource[i]
3426 << QString("items.insert({\"name\": \"eight\"})")
3427 << QString("{ items.resolve(4, 3) }")
3428 << 5 << 4 << 4 << 3 << true << false << true << false << true
3429 << QString("name")
3430 << (QStringList() << "one" << "two" << "three" << "four");
3431
3432 QTest::newRow(dataTag: "ListModel.items insert, resolve inserted")
3433 << singleRoleSource[i]
3434 << QString("items.insert(2, {\"name\": \"eight\"})")
3435 << QString("{ items.resolve(2, 3) }")
3436 << 5 << 4 << 4 << 2 << true << false << true << false << true
3437 << QString("name")
3438 << (QStringList() << "one" << "two" << "three" << "four");
3439
3440 // AbstractItemModel (Single Role)
3441 QTest::newRow(dataTag: "AbstractItemModel.items prepend, resolve prepended")
3442 << singleRoleSource[i]
3443 << QString("items.insert(0, {\"name\": \"eight\"})")
3444 << QString("{ items.resolve(0, 1) }")
3445 << 5 << 4 << 4 << 0 << true << false << true << false << true
3446 << QString("name")
3447 << (QStringList() << "one" << "two" << "three" << "four");
3448
3449 QTest::newRow(dataTag: "AbstractItemModel.items append, resolve appended")
3450 << singleRoleSource[i]
3451 << QString("items.insert({\"name\": \"eight\"})")
3452 << QString("{ items.resolve(4, 3) }")
3453 << 5 << 4 << 4 << 3 << true << false << true << false << true
3454 << QString("name")
3455 << (QStringList() << "one" << "two" << "three" << "four");
3456
3457 QTest::newRow(dataTag: "AbstractItemModel.items insert, resolve inserted")
3458 << singleRoleSource[i]
3459 << QString("items.insert(2, {\"name\": \"eight\"})")
3460 << QString("{ items.resolve(2, 3) }")
3461 << 5 << 4 << 4 << 2 << true << false << true << false << true
3462 << QString("name")
3463 << (QStringList() << "one" << "two" << "three" << "four");
3464
3465 // AbstractItemModel (Multiple Roles)
3466 QTest::newRow(dataTag: "StandardItemModel.items prepend, resolve prepended")
3467 << multipleRoleSource[i]
3468 << QString("items.insert(0, {\"display\": \"Row 8 Item\"})")
3469 << QString("{ items.resolve(0, 1) }")
3470 << 5 << 4 << 4 << 0 << true << false << true << false << false
3471 << QString("display")
3472 << (QStringList() << "Row 1 Item" << "Row 2 Item" << "Row 3 Item" << "Row 4 Item");
3473
3474 QTest::newRow(dataTag: "StandardItemModel.items append, resolve appended")
3475 << multipleRoleSource[i]
3476 << QString("items.insert({\"display\": \"Row 8 Item\"})")
3477 << QString("{ items.resolve(4, 3) }")
3478 << 5 << 4 << 4 << 3 << true << false << true << false << false
3479 << QString("display")
3480 << (QStringList() << "Row 1 Item" << "Row 2 Item" << "Row 3 Item" << "Row 4 Item");
3481
3482 QTest::newRow(dataTag: "StandardItemModel.items insert, resolve inserted")
3483 << multipleRoleSource[i]
3484 << QString("items.insert(2, {\"display\": \"Row 8 Item\"})")
3485 << QString("{ items.resolve(2, 3) }")
3486 << 5 << 4 << 4 << 2 << true << false << true << false << false
3487 << QString("display")
3488 << (QStringList() << "Row 1 Item" << "Row 2 Item" << "Row 3 Item" << "Row 4 Item");
3489
3490 // StringList
3491 QTest::newRow(dataTag: "StringList.items prepend, resolve prepended")
3492 << stringListSource[i]
3493 << QString("items.insert(0, {\"modelData\": \"eight\"})")
3494 << QString("{ items.resolve(0, 1) }")
3495 << 5 << 4 << 4 << 0 << true << false << true << false << false
3496 << QString("modelData")
3497 << (QStringList() << "one" << "two" << "three" << "four");
3498
3499 QTest::newRow(dataTag: "StringList.items append, resolve appended")
3500 << stringListSource[i]
3501 << QString("items.insert({\"modelData\": \"eight\"})")
3502 << QString("{ items.resolve(4, 3) }")
3503 << 5 << 4 << 4 << 3 << true << false << true << false << false
3504 << QString("modelData")
3505 << (QStringList() << "one" << "two" << "three" << "four");
3506
3507 QTest::newRow(dataTag: "StringList.items insert, resolve inserted")
3508 << stringListSource[i]
3509 << QString("items.insert(2, {\"modelData\": \"eight\"})")
3510 << QString("{ items.resolve(2, 3) }")
3511 << 5 << 4 << 4 << 2 << true << false << true << false << false
3512 << QString("modelData")
3513 << (QStringList() << "one" << "two" << "three" << "four");
3514 }
3515}
3516
3517void tst_qquickvisualdatamodel::resolve()
3518{
3519 QFETCH(QUrl, source);
3520 QFETCH(QString, setupExpression);
3521 QFETCH(QString, resolveExpression);
3522 QFETCH(int, unresolvedCount);
3523 QFETCH(int, modelCount);
3524 QFETCH(int, visualCount);
3525 QFETCH(int, index);
3526 QFETCH(bool, inItems);
3527 QFETCH(bool, persisted);
3528 QFETCH(bool, visible);
3529 QFETCH(bool, selected);
3530 QFETCH(bool, modelData);
3531 QFETCH(QString, property);
3532 QFETCH(QStringList, propertyData);
3533
3534 QQuickWindow window;
3535
3536 QQmlComponent component(&engine);
3537 component.loadUrl(url: source);
3538 QScopedPointer<QObject> object(component.create());
3539 QQuickListView *listView = qobject_cast<QQuickListView *>(object: object.data());
3540 QVERIFY(listView);
3541 listView->setParentItem(window.contentItem());
3542
3543 QQuickItem *contentItem = listView->contentItem();
3544 QVERIFY(contentItem);
3545
3546 QObject *visualModel = listView->findChild<QObject *>(aName: "visualModel");
3547 QVERIFY(visualModel);
3548
3549 evaluate<void>(scope: visualModel, expression: setupExpression);
3550 QCOMPARE(evaluate<int>(listView, "count"), unresolvedCount);
3551
3552 listView->forceLayout();
3553 evaluate<void>(scope: visualModel, expression: resolveExpression);
3554
3555 QCOMPARE(evaluate<int>(listView, "count"), inItems ? visualCount : modelCount);
3556 QCOMPARE(evaluate<int>(visualModel, "count"), inItems ? visualCount : modelCount);
3557 QCOMPARE(evaluate<int>(visualModel, "items.count"), inItems ? visualCount : modelCount);
3558 QCOMPARE(evaluate<int>(visualModel, "persistedItems.count"), persisted ? 1 : 0);
3559 QCOMPARE(evaluate<int>(visualModel, "visibleItems.count"), visible ? visualCount : modelCount);
3560 QCOMPARE(evaluate<int>(visualModel, "selectedItems.count"), selected ? 1 : 0);
3561
3562 QCOMPARE(propertyData.count(), visualCount);
3563 for (int i = 0; i < visualCount; ++i) {
3564 int modelIndex = i;
3565
3566 const int itemsIndex = inItems || i <= index ? i : i - 1;
3567 QString get;
3568
3569 if (i != index) {
3570 get = QString("items.get(%1)").arg(a: itemsIndex);
3571
3572 QQuickItem *item = findItem<QQuickItem>(parent: contentItem, objectName: "delegate", index: modelIndex);
3573 QVERIFY(item);
3574
3575 QCOMPARE(evaluate<int>(item, "test1"), modelIndex);
3576 QCOMPARE(evaluate<int>(item, "test2"), modelIndex);
3577 QCOMPARE(evaluate<QString>(item, "test3"), propertyData.at(i));
3578 QCOMPARE(evaluate<QString>(item, "test4"), propertyData.at(i));
3579
3580 if (modelData) {
3581 QCOMPARE(evaluate<QString>(item, "test5"), propertyData.at(i));
3582 QCOMPARE(evaluate<QString>(item, "test6"), propertyData.at(i));
3583 }
3584
3585 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inItems"), true);
3586 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inPersistedItems"), false);
3587 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inVisible"), true);
3588 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inSelected"), false);
3589 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.isUnresolved"), false);
3590
3591 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.itemsIndex"), itemsIndex);
3592 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.persistedItemsIndex"), persisted && i > index ? 1 : 0);
3593 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.visibleIndex"), visible || i <= index ? i : i - 1);
3594 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.selectedIndex"), selected && i > index ? 1 : 0);
3595 } else if (inItems) {
3596 get = QString("items.get(%1)").arg(a: index);
3597 } else if (persisted) {
3598 get = "persistedItems.get(0)";
3599 } else if (visible) {
3600 get = QString("visibleItems.get(%1)").arg(a: index);
3601 } else if (selected) {
3602 get = "selectedItems.get(0)";
3603 } else {
3604 continue;
3605 }
3606
3607 QCOMPARE(evaluate<int>(visualModel, get + ".model.index"), modelIndex);
3608
3609 QCOMPARE(evaluate<QString>(visualModel, get + ".model." + property), propertyData.at(i));
3610
3611 QCOMPARE(evaluate<bool>(visualModel, get + ".inItems"), inItems || i != index);
3612 QCOMPARE(evaluate<bool>(visualModel, get + ".inPersistedItems"), persisted && i == index);
3613 QCOMPARE(evaluate<bool>(visualModel, get + ".inVisible"), visible || i != index);
3614 QCOMPARE(evaluate<bool>(visualModel, get + ".inSelected"), selected && i == index);
3615 QCOMPARE(evaluate<bool>(visualModel, get + ".isUnresolved"), false);
3616
3617 QCOMPARE(evaluate<int>(visualModel, get + ".itemsIndex"), inItems || i <= index ? i : i - 1);
3618 QCOMPARE(evaluate<int>(visualModel, get + ".persistedItemsIndex"), persisted && i > index ? 1 : 0);
3619 QCOMPARE(evaluate<int>(visualModel, get + ".visibleIndex"), visible || i <= index ? i : i - 1);
3620 QCOMPARE(evaluate<int>(visualModel, get + ".selectedIndex"), selected && i > index ? 1 : 0);
3621 }
3622
3623 QObject *item = nullptr;
3624
3625 if (inItems)
3626 item = evaluate<QObject *>(scope: visualModel, expression: QString("items.create(%1)").arg(a: index));
3627 else if (persisted)
3628 item = evaluate<QObject *>(scope: visualModel, expression: QString("persistedItems.create(%1)").arg(a: 0));
3629 else if (visible)
3630 item = evaluate<QObject *>(scope: visualModel, expression: QString("visibleItems.create(%1)").arg(a: index));
3631 else if (selected)
3632 item = evaluate<QObject *>(scope: visualModel, expression: QString("selectedItems.create(%1)").arg(a: 0));
3633 else
3634 return;
3635
3636 QVERIFY(item);
3637
3638 QCOMPARE(evaluate<int>(item, "test1"), index);
3639 QCOMPARE(evaluate<int>(item, "test2"), index);
3640 QCOMPARE(evaluate<QString>(item, "test3"), propertyData.at(index));
3641 QCOMPARE(evaluate<QString>(item, "test4"), propertyData.at(index));
3642
3643 if (modelData) {
3644 QCOMPARE(evaluate<QString>(item, "test5"), propertyData.at(index));
3645 QCOMPARE(evaluate<QString>(item, "test6"), propertyData.at(index));
3646 }
3647
3648 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inItems"), inItems);
3649 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inPersistedItems"), true);
3650 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inVisible"), visible);
3651 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.inSelected"), selected);
3652 QCOMPARE(evaluate<bool>(item, "delegate.DelegateModel.isUnresolved"), false);
3653
3654 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.itemsIndex"), index);
3655 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.persistedItemsIndex"), 0);
3656 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.visibleIndex"), index);
3657 QCOMPARE(evaluate<int>(item, "delegate.DelegateModel.selectedIndex"), 0);
3658}
3659
3660void tst_qquickvisualdatamodel::warnings_data()
3661{
3662 QTest::addColumn<QUrl>(name: "source");
3663 QTest::addColumn<QString>(name: "expression");
3664 QTest::addColumn<QRegularExpression>(name: "warning");
3665 QTest::addColumn<int>(name: "count");
3666
3667 QTest::newRow(dataTag: "insert < 0")
3668 << testFileUrl(fileName: "listmodelproperties.qml")
3669 << QString("items.insert(-2, {\"number\": \"eight\"})")
3670 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "insert: index out of range"))
3671 << 4;
3672
3673 QTest::newRow(dataTag: "insert > length")
3674 << testFileUrl(fileName: "listmodelproperties.qml")
3675 << QString("items.insert(8, {\"number\": \"eight\"})")
3676 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "insert: index out of range"))
3677 << 4;
3678
3679 QTest::newRow(dataTag: "create < 0")
3680 << testFileUrl(fileName: "listmodelproperties.qml")
3681 << QString("items.create(-2, {\"number\": \"eight\"})")
3682 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "create: index out of range"))
3683 << 4;
3684
3685 QTest::newRow(dataTag: "create > length")
3686 << testFileUrl(fileName: "listmodelproperties.qml")
3687 << QString("items.create(8, {\"number\": \"eight\"})")
3688 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "create: index out of range"))
3689 << 4;
3690
3691 QTest::newRow(dataTag: "resolve from < 0")
3692 << testFileUrl(fileName: "listmodelproperties.qml")
3693 << QString("items.resolve(-2, 3)")
3694 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "resolve: from index out of range"))
3695 << 4;
3696
3697 QTest::newRow(dataTag: "resolve from > length")
3698 << testFileUrl(fileName: "listmodelproperties.qml")
3699 << QString("items.resolve(8, 3)")
3700 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "resolve: from index out of range"))
3701 << 4;
3702
3703 QTest::newRow(dataTag: "resolve to < 0")
3704 << testFileUrl(fileName: "listmodelproperties.qml")
3705 << QString("items.resolve(3, -2)")
3706 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "resolve: to index out of range"))
3707 << 4;
3708
3709 QTest::newRow(dataTag: "resolve to > length")
3710 << testFileUrl(fileName: "listmodelproperties.qml")
3711 << QString("items.resolve(3, 8)")
3712 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "resolve: to index out of range"))
3713 << 4;
3714
3715 QTest::newRow(dataTag: "resolve from invalid index")
3716 << testFileUrl(fileName: "listmodelproperties.qml")
3717 << QString("items.resolve(\"two\", 3)")
3718 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "resolve: from index invalid"))
3719 << 4;
3720
3721 QTest::newRow(dataTag: "resolve to invalid index")
3722 << testFileUrl(fileName: "listmodelproperties.qml")
3723 << QString("items.resolve(3, \"two\")")
3724 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "resolve: to index invalid"))
3725 << 4;
3726
3727 QTest::newRow(dataTag: "resolve already resolved item")
3728 << testFileUrl(fileName: "listmodelproperties.qml")
3729 << QString("items.resolve(3, 2)")
3730 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "resolve: from is not an unresolved item"))
3731 << 4;
3732
3733 QTest::newRow(dataTag: "resolve already resolved item")
3734 << testFileUrl(fileName: "listmodelproperties.qml")
3735 << QString("{ items.insert(0, {\"number\": \"eight\"});"
3736 "items.insert(1, {\"number\": \"seven\"});"
3737 "items.resolve(0, 1)}")
3738 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "resolve: to is not a model item"))
3739 << 6;
3740
3741 QTest::newRow(dataTag: "remove index < 0")
3742 << testFileUrl(fileName: "listmodelproperties.qml")
3743 << QString("items.remove(-2, 1)")
3744 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "remove: index out of range"))
3745 << 4;
3746
3747 QTest::newRow(dataTag: "remove index == length")
3748 << testFileUrl(fileName: "listmodelproperties.qml")
3749 << QString("items.remove(4, 1)")
3750 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "remove: index out of range"))
3751 << 4;
3752
3753 QTest::newRow(dataTag: "remove index > length")
3754 << testFileUrl(fileName: "listmodelproperties.qml")
3755 << QString("items.remove(9, 1)")
3756 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "remove: index out of range"))
3757 << 4;
3758
3759 QTest::newRow(dataTag: "remove invalid index")
3760 << testFileUrl(fileName: "listmodelproperties.qml")
3761 << QString("items.remove(\"nine\", 1)")
3762 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "remove: invalid index"))
3763 << 4;
3764
3765 QTest::newRow(dataTag: "remove count < 0")
3766 << testFileUrl(fileName: "listmodelproperties.qml")
3767 << QString("items.remove(1, -2)")
3768 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "remove: invalid count"))
3769 << 4;
3770
3771 QTest::newRow(dataTag: "remove index + count > length")
3772 << testFileUrl(fileName: "listmodelproperties.qml")
3773 << QString("items.remove(2, 4, \"selected\")")
3774 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "remove: invalid count"))
3775 << 4;
3776
3777 QTest::newRow(dataTag: "addGroups index < 0")
3778 << testFileUrl(fileName: "listmodelproperties.qml")
3779 << QString("items.addGroups(-2, 1, \"selected\")")
3780 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "addGroups: index out of range"))
3781 << 4;
3782
3783 QTest::newRow(dataTag: "addGroups index == length")
3784 << testFileUrl(fileName: "listmodelproperties.qml")
3785 << QString("items.addGroups(4, 1, \"selected\")")
3786 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "addGroups: index out of range"))
3787 << 4;
3788
3789 QTest::newRow(dataTag: "addGroups index > length")
3790 << testFileUrl(fileName: "listmodelproperties.qml")
3791 << QString("items.addGroups(9, 1, \"selected\")")
3792 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "addGroups: index out of range"))
3793 << 4;
3794
3795 QTest::newRow(dataTag: "addGroups count < 0")
3796 << testFileUrl(fileName: "listmodelproperties.qml")
3797 << QString("items.addGroups(1, -2, \"selected\")")
3798 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "addGroups: invalid count"))
3799 << 4;
3800
3801 QTest::newRow(dataTag: "addGroups index + count > length")
3802 << testFileUrl(fileName: "listmodelproperties.qml")
3803 << QString("items.addGroups(2, 4, \"selected\")")
3804 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "addGroups: invalid count"))
3805 << 4;
3806
3807 QTest::newRow(dataTag: "removeGroups index < 0")
3808 << testFileUrl(fileName: "listmodelproperties.qml")
3809 << QString("items.removeGroups(-2, 1, \"selected\")")
3810 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "removeGroups: index out of range"))
3811 << 4;
3812
3813 QTest::newRow(dataTag: "removeGroups index == length")
3814 << testFileUrl(fileName: "listmodelproperties.qml")
3815 << QString("items.removeGroups(4, 1, \"selected\")")
3816 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "removeGroups: index out of range"))
3817 << 4;
3818
3819 QTest::newRow(dataTag: "removeGroups index > length")
3820 << testFileUrl(fileName: "listmodelproperties.qml")
3821 << QString("items.removeGroups(9, 1, \"selected\")")
3822 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "removeGroups: index out of range"))
3823 << 4;
3824
3825 QTest::newRow(dataTag: "removeGroups count < 0")
3826 << testFileUrl(fileName: "listmodelproperties.qml")
3827 << QString("items.removeGroups(1, -2, \"selected\")")
3828 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "removeGroups: invalid count"))
3829 << 4;
3830
3831 QTest::newRow(dataTag: "removeGroups index + count > length")
3832 << testFileUrl(fileName: "listmodelproperties.qml")
3833 << QString("items.removeGroups(2, 4, \"selected\")")
3834 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "removeGroups: invalid count"))
3835 << 4;
3836
3837 QTest::newRow(dataTag: "setGroups index < 0")
3838 << testFileUrl(fileName: "listmodelproperties.qml")
3839 << QString("items.setGroups(-2, 1, \"selected\")")
3840 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "setGroups: index out of range"))
3841 << 4;
3842
3843 QTest::newRow(dataTag: "setGroups index == length")
3844 << testFileUrl(fileName: "listmodelproperties.qml")
3845 << QString("items.setGroups(4, 1, \"selected\")")
3846 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "setGroups: index out of range"))
3847 << 4;
3848
3849 QTest::newRow(dataTag: "setGroups index > length")
3850 << testFileUrl(fileName: "listmodelproperties.qml")
3851 << QString("items.setGroups(9, 1, \"selected\")")
3852 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "setGroups: index out of range"))
3853 << 4;
3854
3855 QTest::newRow(dataTag: "setGroups count < 0")
3856 << testFileUrl(fileName: "listmodelproperties.qml")
3857 << QString("items.setGroups(1, -2, \"selected\")")
3858 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "setGroups: invalid count"))
3859 << 4;
3860
3861 QTest::newRow(dataTag: "setGroups index + count > length")
3862 << testFileUrl(fileName: "listmodelproperties.qml")
3863 << QString("items.setGroups(2, 4, \"selected\")")
3864 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "setGroups: invalid count"))
3865 << 4;
3866
3867 QTest::newRow(dataTag: "move from < 0")
3868 << testFileUrl(fileName: "listmodelproperties.qml")
3869 << QString("items.move(-2, 1, 1)")
3870 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "move: from index out of range"))
3871 << 4;
3872
3873 QTest::newRow(dataTag: "move from == length")
3874 << testFileUrl(fileName: "listmodelproperties.qml")
3875 << QString("items.move(4, 1, 1)")
3876 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "move: from index out of range"))
3877 << 4;
3878
3879 QTest::newRow(dataTag: "move from > length")
3880 << testFileUrl(fileName: "listmodelproperties.qml")
3881 << QString("items.move(9, 1, 1)")
3882 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "move: from index out of range"))
3883 << 4;
3884
3885 QTest::newRow(dataTag: "move invalid from")
3886 << testFileUrl(fileName: "listmodelproperties.qml")
3887 << QString("items.move(\"nine\", 1, 1)")
3888 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "move: invalid from index"))
3889 << 4;
3890
3891 QTest::newRow(dataTag: "move to < 0")
3892 << testFileUrl(fileName: "listmodelproperties.qml")
3893 << QString("items.move(1, -2, 1)")
3894 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "move: to index out of range"))
3895 << 4;
3896
3897 QTest::newRow(dataTag: "move to == length")
3898 << testFileUrl(fileName: "listmodelproperties.qml")
3899 << QString("items.move(1, 4, 1)")
3900 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "move: to index out of range"))
3901 << 4;
3902
3903 QTest::newRow(dataTag: "move to > length")
3904 << testFileUrl(fileName: "listmodelproperties.qml")
3905 << QString("items.move(1, 9, 1)")
3906 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "move: to index out of range"))
3907 << 4;
3908
3909 QTest::newRow(dataTag: "move invalid to")
3910 << testFileUrl(fileName: "listmodelproperties.qml")
3911 << QString("items.move(1, \"nine\", 1)")
3912 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "move: invalid to index"))
3913 << 4;
3914
3915 QTest::newRow(dataTag: "move count < 0")
3916 << testFileUrl(fileName: "listmodelproperties.qml")
3917 << QString("items.move(1, 1, -2)")
3918 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "move: invalid count"))
3919 << 4;
3920
3921 QTest::newRow(dataTag: "move from + count > length")
3922 << testFileUrl(fileName: "listmodelproperties.qml")
3923 << QString("items.move(2, 1, 4)")
3924 << QRegularExpression(".*" + QQmlDelegateModelGroup::tr(s: "move: from index out of range"))
3925 << 4;
3926}
3927
3928void tst_qquickvisualdatamodel::warnings()
3929{
3930 QFETCH(QUrl, source);
3931 QFETCH(QString, expression);
3932 QFETCH(QRegularExpression, warning);
3933 QFETCH(int, count);
3934
3935 QQuickWindow window;
3936
3937 QQmlComponent component(&engine);
3938 component.loadUrl(url: source);
3939 QScopedPointer<QObject> object(component.create());
3940 QQuickListView *listView = qobject_cast<QQuickListView *>(object: object.data());
3941 QVERIFY(listView);
3942 listView->setParentItem(window.contentItem());
3943
3944 QQuickItem *contentItem = listView->contentItem();
3945 QVERIFY(contentItem);
3946
3947 QObject *visualModel = evaluate<QObject *>(scope: listView, expression: "model");
3948 QVERIFY(visualModel);
3949
3950 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: warning);
3951
3952 evaluate<void>(scope: visualModel, expression);
3953 QCOMPARE(evaluate<int>(listView, "count"), count);
3954}
3955
3956void tst_qquickvisualdatamodel::invalidAttachment()
3957{
3958 QQmlComponent component(&engine);
3959 component.loadUrl(url: testFileUrl(fileName: "invalidAttachment.qml"));
3960
3961 QScopedPointer<QObject> object(component.create());
3962 QVERIFY(object);
3963 QCOMPARE(component.errors().count(), 0);
3964
3965 QVariant property = object->property(name: "invalidVdm");
3966 QCOMPARE(property.userType(), qMetaTypeId<QQmlDelegateModel *>());
3967 QVERIFY(!property.value<QQmlDelegateModel *>());
3968
3969 QQuickItem *item = findItem<QQuickItem>(parent: static_cast<QQuickItem *>(object.data()), objectName: "delegate");
3970 QVERIFY(item);
3971
3972 property = item->property(name: "validVdm");
3973 QCOMPARE(property.userType(), qMetaTypeId<QQmlDelegateModel *>());
3974 QVERIFY(property.value<QQmlDelegateModel *>());
3975
3976 property = item->property(name: "invalidVdm");
3977 QCOMPARE(property.userType(), qMetaTypeId<QQmlDelegateModel *>());
3978 QVERIFY(!property.value<QQmlDelegateModel *>());
3979}
3980
3981void tst_qquickvisualdatamodel::asynchronousInsert_data()
3982{
3983 QTest::addColumn<int>(name: "requestIndex");
3984 QTest::addColumn<int>(name: "insertIndex");
3985 QTest::addColumn<int>(name: "insertCount");
3986 QTest::addColumn<int>(name: "completeIndex");
3987
3988 QTest::newRow(dataTag: "insert before") << 4 << 1 << 3 << 7;
3989 QTest::newRow(dataTag: "insert after") << 4 << 6 << 3 << 4;
3990 QTest::newRow(dataTag: "insert at") << 4 << 4 << 3 << 7;
3991}
3992
3993void tst_qquickvisualdatamodel::asynchronousInsert()
3994{
3995 QFETCH(int, requestIndex);
3996 QFETCH(int, insertIndex);
3997 QFETCH(int, insertCount);
3998 QFETCH(int, completeIndex);
3999
4000 QCOMPARE(controller.incubatingObjectCount(), 0);
4001
4002 QQmlComponent c(&engine, testFileUrl(fileName: "visualdatamodel.qml"));
4003
4004 QaimModel model;
4005 for (int i = 0; i < 8; i++)
4006 model.addItem(name: "Original item" + QString::number(i), number: "");
4007
4008 engine.rootContext()->setContextProperty("myModel", &model);
4009
4010 QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(object: c.create());
4011 QVERIFY(visualModel);
4012
4013 ItemRequester requester;
4014 connect(sender: visualModel, SIGNAL(initItem(int,QObject*)), receiver: &requester, SLOT(initItem(int,QObject*)));
4015 connect(sender: visualModel, SIGNAL(createdItem(int,QObject*)), receiver: &requester, SLOT(createdItem(int,QObject*)));
4016 connect(sender: visualModel, SIGNAL(destroyingItem(QObject*)), receiver: &requester, SLOT(destroyingItem(QObject*)));
4017
4018 QQuickItem *item = qobject_cast<QQuickItem*>(object: visualModel->object(index: requestIndex, incubationMode: QQmlIncubator::Asynchronous));
4019 QVERIFY(!item);
4020
4021 QVERIFY(!requester.itemInitialized);
4022 QVERIFY(!requester.itemCreated);
4023 QVERIFY(!requester.itemDestroyed);
4024
4025 QList<QPair<QString, QString> > newItems;
4026 for (int i = 0; i < insertCount; i++)
4027 newItems.append(t: qMakePair(x: QLatin1String("New item") + QString::number(i), y: QString(QLatin1String(""))));
4028 model.insertItems(index: insertIndex, items: newItems);
4029
4030 item = qobject_cast<QQuickItem*>(object: visualModel->object(index: completeIndex));
4031 QVERIFY(item);
4032
4033 QCOMPARE(requester.itemInitialized, item);
4034 QCOMPARE(requester.indexInitialized, completeIndex);
4035 QCOMPARE(requester.itemCreated, item);
4036 QCOMPARE(requester.indexCreated, completeIndex);
4037 QVERIFY(!requester.itemDestroyed);
4038
4039 QCOMPARE(controller.incubatingObjectCount(), 0);
4040
4041 visualModel->release(object: item);
4042
4043 QCOMPARE(requester.itemDestroyed, item);
4044}
4045
4046void tst_qquickvisualdatamodel::asynchronousRemove_data()
4047{
4048 QTest::addColumn<int>(name: "requestIndex");
4049 QTest::addColumn<int>(name: "removeIndex");
4050 QTest::addColumn<int>(name: "removeCount");
4051 QTest::addColumn<int>(name: "completeIndex");
4052
4053 QTest::newRow(dataTag: "remove before") << 4 << 1 << 3 << 1;
4054 QTest::newRow(dataTag: "remove after") << 4 << 6 << 2 << 4;
4055 QTest::newRow(dataTag: "remove requested") << 4 << 4 << 3 << -1;
4056}
4057
4058void tst_qquickvisualdatamodel::asynchronousRemove()
4059{
4060 QFETCH(int, requestIndex);
4061 QFETCH(int, removeIndex);
4062 QFETCH(int, removeCount);
4063 QFETCH(int, completeIndex);
4064
4065 QCOMPARE(controller.incubatingObjectCount(), 0);
4066
4067 QQmlComponent c(&engine, testFileUrl(fileName: "visualdatamodel.qml"));
4068
4069 QaimModel model;
4070 for (int i = 0; i < 8; i++)
4071 model.addItem(name: "Original item" + QString::number(i), number: "");
4072
4073 engine.rootContext()->setContextProperty("myModel", &model);
4074
4075 QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(object: c.create());
4076 QVERIFY(visualModel);
4077
4078 ItemRequester requester;
4079 connect(sender: visualModel, SIGNAL(initItem(int,QObject*)), receiver: &requester, SLOT(initItem(int,QObject*)));
4080 connect(sender: visualModel, SIGNAL(createdItem(int,QObject*)), receiver: &requester, SLOT(createdItem(int,QObject*)));
4081 connect(sender: visualModel, SIGNAL(destroyingItem(QObject*)), receiver: &requester, SLOT(destroyingItem(QObject*)));
4082
4083 QQuickItem *item = qobject_cast<QQuickItem*>(object: visualModel->object(index: requestIndex, incubationMode: QQmlIncubator::Asynchronous));
4084 QVERIFY(!item);
4085
4086 QVERIFY(!requester.itemInitialized);
4087 QVERIFY(!requester.itemCreated);
4088 QVERIFY(!requester.itemDestroyed);
4089
4090 model.removeItems(index: removeIndex, count: removeCount);
4091
4092 if (completeIndex == -1) {
4093 QElapsedTimer timer;
4094 timer.start();
4095 do {
4096 controller.incubateFor(msecs: 50);
4097 } while (timer.elapsed() < 1000 && controller.incubatingObjectCount() > 0);
4098
4099 // The item was removed before incubation started. We should not have any item created.
4100 QVERIFY(!requester.itemInitialized);
4101 QVERIFY(!requester.itemCreated);
4102 QVERIFY(!requester.itemDestroyed);
4103 } else {
4104 item = qobject_cast<QQuickItem*>(object: visualModel->object(index: completeIndex));
4105 QVERIFY(item);
4106
4107 QCOMPARE(requester.itemInitialized, item);
4108 QCOMPARE(requester.indexInitialized, completeIndex);
4109 QCOMPARE(requester.itemCreated, item);
4110 QCOMPARE(requester.indexCreated, completeIndex);
4111 QVERIFY(!requester.itemDestroyed);
4112
4113 QCOMPARE(controller.incubatingObjectCount(), 0);
4114
4115 visualModel->release(object: item);
4116
4117 QCOMPARE(requester.itemDestroyed, item);
4118 }
4119}
4120
4121void tst_qquickvisualdatamodel::asynchronousMove_data()
4122{
4123 QTest::addColumn<int>(name: "requestIndex");
4124 QTest::addColumn<int>(name: "from");
4125 QTest::addColumn<int>(name: "to");
4126 QTest::addColumn<int>(name: "count");
4127 QTest::addColumn<int>(name: "completeIndex");
4128
4129 QTest::newRow(dataTag: "move before") << 4 << 1 << 0 << 3 << 4;
4130 QTest::newRow(dataTag: "move after") << 4 << 6 << 5 << 2 << 4;
4131 QTest::newRow(dataTag: "move requested") << 4 << 4 << 3 << 2 << 3;
4132 QTest::newRow(dataTag: "move before to after") << 4 << 1 << 4 << 3 << 1;
4133 QTest::newRow(dataTag: "move after to before") << 4 << 6 << 2 << 2 << 6;
4134}
4135
4136void tst_qquickvisualdatamodel::asynchronousMove()
4137{
4138 QFETCH(int, requestIndex);
4139 QFETCH(int, from);
4140 QFETCH(int, to);
4141 QFETCH(int, count);
4142 QFETCH(int, completeIndex);
4143
4144 QCOMPARE(controller.incubatingObjectCount(), 0);
4145
4146 QQmlComponent c(&engine, testFileUrl(fileName: "visualdatamodel.qml"));
4147
4148 QaimModel model;
4149 for (int i = 0; i < 8; i++)
4150 model.addItem(name: "Original item" + QString::number(i), number: "");
4151
4152 engine.rootContext()->setContextProperty("myModel", &model);
4153
4154 QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(object: c.create());
4155 QVERIFY(visualModel);
4156
4157 ItemRequester requester;
4158 connect(sender: visualModel, SIGNAL(initItem(int,QObject*)), receiver: &requester, SLOT(initItem(int,QObject*)));
4159 connect(sender: visualModel, SIGNAL(createdItem(int,QObject*)), receiver: &requester, SLOT(createdItem(int,QObject*)));
4160 connect(sender: visualModel, SIGNAL(destroyingItem(QObject*)), receiver: &requester, SLOT(destroyingItem(QObject*)));
4161
4162 QQuickItem *item = qobject_cast<QQuickItem*>(object: visualModel->object(index: requestIndex, incubationMode: QQmlIncubator::Asynchronous));
4163 QVERIFY(!item);
4164
4165 QVERIFY(!requester.itemInitialized);
4166 QVERIFY(!requester.itemCreated);
4167 QVERIFY(!requester.itemDestroyed);
4168
4169 model.moveItems(from, to, count);
4170
4171 item = qobject_cast<QQuickItem*>(object: visualModel->object(index: completeIndex));
4172 QVERIFY(item);
4173
4174
4175 QCOMPARE(requester.itemInitialized, item);
4176 QCOMPARE(requester.indexInitialized, completeIndex);
4177 QCOMPARE(requester.itemCreated, item);
4178 QCOMPARE(requester.indexCreated, completeIndex);
4179 QVERIFY(!requester.itemDestroyed);
4180
4181 QCOMPARE(controller.incubatingObjectCount(), 0);
4182
4183 visualModel->release(object: item);
4184
4185 QCOMPARE(requester.itemDestroyed, item);
4186}
4187
4188void tst_qquickvisualdatamodel::asynchronousCancel()
4189{
4190 const int requestIndex = 4;
4191
4192 QCOMPARE(controller.incubatingObjectCount(), 0);
4193
4194 QQmlComponent c(&engine, testFileUrl(fileName: "visualdatamodel.qml"));
4195
4196 QaimModel model;
4197 for (int i = 0; i < 8; i++)
4198 model.addItem(name: "Original item" + QString::number(i), number: "");
4199
4200 engine.rootContext()->setContextProperty("myModel", &model);
4201
4202 QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(object: c.create());
4203 QVERIFY(visualModel);
4204
4205 QQuickItem *item = qobject_cast<QQuickItem*>(object: visualModel->object(index: requestIndex, incubationMode: QQmlIncubator::Asynchronous));
4206 QVERIFY(!item);
4207 QCOMPARE(controller.incubatingObjectCount(), 1);
4208
4209 visualModel->cancel(index: requestIndex);
4210 QCOMPARE(controller.incubatingObjectCount(), 0);
4211}
4212
4213void tst_qquickvisualdatamodel::invalidContext()
4214{
4215 QQmlEngine engine;
4216 QaimModel model;
4217 for (int i = 0; i < 8; i++)
4218 model.addItem(name: "Original item" + QString::number(i), number: "");
4219
4220 engine.rootContext()->setContextProperty("myModel", &model);
4221
4222 QScopedPointer<QQmlContext> context(new QQmlContext(engine.rootContext()));
4223
4224 QQmlComponent c(&engine, testFileUrl(fileName: "visualdatamodel.qml"));
4225
4226
4227 QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(object: c.create(context: context.data()));
4228 QVERIFY(visualModel);
4229
4230 QQuickItem *item = qobject_cast<QQuickItem*>(object: visualModel->object(index: 4));
4231 QVERIFY(item);
4232 visualModel->release(object: item);
4233
4234 delete context.take();
4235
4236 model.insertItem(index: 4, name: "new item", number: "");
4237
4238 item = qobject_cast<QQuickItem*>(object: visualModel->object(index: 4));
4239 QVERIFY(!item);
4240}
4241
4242class ObjectsProvider : public QObject
4243{
4244 Q_OBJECT
4245 Q_PROPERTY(QQmlListProperty<QObject> objects READ objects NOTIFY objectsChanged)
4246
4247public:
4248 explicit ObjectsProvider(QObject *parent = nullptr) : QObject(parent) {}
4249
4250 Q_INVOKABLE void rebuild()
4251 {
4252 for (auto old: m_objects)
4253 old->deleteLater();
4254
4255 m_objects.clear();
4256 emit objectsChanged();
4257
4258 const int size = std::rand() & 0xff;
4259 for (int i = 0; i < size; ++i) {
4260 auto newElement = new QObject(this);
4261 QQmlEngine::setObjectOwnership(newElement, QQmlEngine::CppOwnership);
4262 m_objects.push_back(t: newElement);
4263 }
4264 emit objectsChanged();
4265 }
4266
4267 Q_INVOKABLE QQmlListProperty<QObject> objects()
4268 {
4269 return QQmlListProperty<QObject>(this, nullptr, &ObjectsProvider::listLength,
4270 &ObjectsProvider::listAt);
4271 }
4272
4273 static int listLength(QQmlListProperty<QObject> *property)
4274 {
4275 auto objectsProvider = qobject_cast<ObjectsProvider*>(object: property->object);
4276 return objectsProvider ? objectsProvider->m_objects.length() : 0;
4277 }
4278
4279 static QObject* listAt(QQmlListProperty<QObject> *property, int index)
4280 {
4281 auto objectsProvider = qobject_cast<ObjectsProvider*>(object: property->object);
4282 return objectsProvider ? objectsProvider->m_objects.at(i: index) : nullptr;
4283 }
4284
4285signals:
4286 void objectsChanged();
4287
4288private:
4289 QList<QObject *> m_objects;
4290};
4291
4292void tst_qquickvisualdatamodel::externalManagedModel()
4293{
4294 qmlRegisterType<ObjectsProvider>(uri: "example", versionMajor: 1, versionMinor: 0, qmlName: "ObjectsProvider");
4295
4296 QQmlEngine engine;
4297 QQmlComponent component(&engine, testFileUrl(fileName: "externalManagedModel.qml"));
4298 QVERIFY(component.isReady());
4299
4300 QScopedPointer<QObject> object(component.create());
4301 QVERIFY(!object.isNull());
4302
4303 QVERIFY(object->property("running").toBool());
4304
4305 // Make sure it runs to completion without crashing.
4306 QTRY_VERIFY(!object->property("running").toBool());
4307}
4308
4309void tst_qquickvisualdatamodel::delegateModelChangeDelegate()
4310{
4311 // Verify that QTBUG-63477 is fixed.
4312 // Changing the delegate would not update existing items.
4313 QQmlEngine engine;
4314 QScopedPointer<QQmlContext> context(new QQmlContext(engine.rootContext()));
4315
4316 QQmlComponent c(&engine);
4317 c.setData("import QtQml.Models 2.2\nDelegateModel {}\n", baseUrl: QUrl());
4318 QCOMPARE(c.status(), QQmlComponent::Ready);
4319
4320 QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(object: c.create(context: context.data()));
4321 QVERIFY(visualModel);
4322 visualModel->setModel(QVariant(3));
4323
4324 QQmlComponent first(&engine);
4325 first.setData("import QtQuick 2.0\nItem { objectName: \"old\" }\n", baseUrl: QUrl());
4326 QCOMPARE(first.status(), QQmlComponent::Ready);
4327
4328 // Without delegate, claim to have an item count of 0
4329 QCOMPARE(visualModel->count(), 0);
4330
4331 visualModel->setDelegate(&first);
4332 // The first delegate has been set, verify we get it
4333 QObject* old = visualModel->object(index: 0, incubationMode: QQmlIncubator::Synchronous);
4334 QVERIFY(old);
4335 QCOMPARE(visualModel->object(0, QQmlIncubator::Synchronous)->objectName(), QStringLiteral("old"));
4336 QCOMPARE(visualModel->count(), 3);
4337
4338 QQmlComponent second(&engine);
4339 second.setData("import QtQuick 2.0\nItem { objectName: \"new\" }\n", baseUrl: QUrl());
4340 QCOMPARE(second.status(), QQmlComponent::Ready);
4341
4342 visualModel->setDelegate(&second);
4343 // After changing the delegate, expect the existing item to have the new delegate
4344 QCOMPARE(visualModel->object(0, QQmlIncubator::Synchronous)->objectName(), QStringLiteral("new"));
4345 QCOMPARE(visualModel->count(), 3);
4346}
4347
4348void tst_qquickvisualdatamodel::checkFilterGroupForDelegate()
4349{
4350 QQuickView view;
4351 view.setSource(testFileUrl(fileName: "filterGroupForDelegate.qml"));
4352 view.show();
4353
4354 QQuickItem *obj = view.rootObject();
4355 QVERIFY(obj);
4356
4357 QTRY_VERIFY(obj->property("numChanges").toInt() > 100);
4358 QVERIFY(obj->property("ok").toBool());
4359}
4360
4361void tst_qquickvisualdatamodel::readFromProxyObject()
4362{
4363 QQmlEngine engine;
4364 QQmlComponent component(&engine, testFileUrl(fileName: "readFromProxyObject.qml"));
4365
4366 QScopedPointer<QObject> obj(component.create());
4367 QVERIFY(obj);
4368
4369 auto *window = qobject_cast<QQuickWindow *>(object: obj.get());
4370 QVERIFY(window);
4371
4372 QCOMPARE(window->property("name").type(), QMetaType::QString);
4373 QTRY_VERIFY(window->property("name").toString() != QLatin1String("wrong"));
4374}
4375
4376QTEST_MAIN(tst_qquickvisualdatamodel)
4377
4378#include "tst_qquickvisualdatamodel.moc"
4379

source code of qtdeclarative/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp