1/****************************************************************************
2**
3** Copyright (C) 2017 Ford Motor Company
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtRemoteObjects module 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
29#include "modeltest.h"
30#include "../shared/model_utilities.h"
31
32#include <QtTest/QtTest>
33#include <QMetaType>
34#include <QRemoteObjectReplica>
35#include <QRemoteObjectNode>
36#include <QAbstractItemModelReplica>
37#include <QStandardItemModel>
38#include <QSortFilterProxyModel>
39#include <QEventLoop>
40#include <QRandomGenerator>
41
42namespace {
43
44QList<QStandardItem*> createInsertionChildren(int num, const QString& name, const QColor &background)
45{
46 QList<QStandardItem*> children;
47 for (int i = 0; i < num; ++i) {
48 QScopedPointer<QStandardItem> item(new QStandardItem(QStringLiteral("%1 %2").arg(a: name).arg(a: i+1)));
49 item->setBackground(background);
50 children.append(t: item.take());
51 }
52 return children;
53}
54
55class RowsWatcher : public WaitHelper
56{
57public:
58 RowsWatcher(const QAbstractItemModel *model, int expectedRowsCount)
59 : WaitHelper(), m_model(model), m_expectedRowsCount(expectedRowsCount)
60 {
61 connect(sender: m_model, signal: &QAbstractItemModel::rowsInserted, context: this,
62 slot: [this](const QModelIndex &parent, int first, int last) {
63 const auto columnCount = m_model->columnCount(parent);
64 for (int row = first; row <= last; ++row) {
65 for (int column = 0; column < columnCount; ++column)
66 m_changedData.append(t: m_model->index(row, column, parent));
67 }
68 onNumRowsChanged(parent, first, last);
69 });
70
71 connect(sender: m_model, signal: &QAbstractItemModel::rowsRemoved, receiver: this, slot: &RowsWatcher::onNumRowsChanged);
72 }
73
74 void onNumRowsChanged(const QModelIndex &parent, int first, int last)
75 {
76 const auto compare = [=](const RowData &row) {
77 return (row.m_start == first && row.m_end == last
78 && compareIndices(lhs: row.m_index, rhs: parent));
79 };
80 QVERIFY(std::find_if(m_pendingRows.begin(), m_pendingRows.end(), compare)
81 != m_pendingRows.end());
82
83 m_currentRowsCount += last - first + 1;
84 if (m_currentRowsCount == m_expectedRowsCount)
85 finish();
86 }
87
88 void scheduleRowsToWatch(const QModelIndex &index, int start, int end)
89 {
90 m_pendingRows.push_back(t: RowData(index, start, end));
91 }
92
93 QVector<QModelIndex> changedData() const { return m_changedData; }
94
95private:
96 struct RowData
97 {
98 RowData(const QModelIndex &idx = QModelIndex(), int s = -1, int e = -1)
99 : m_index(idx), m_start(s), m_end(e) {}
100 QModelIndex m_index;
101 int m_start;
102 int m_end;
103 };
104
105 const QAbstractItemModel *m_model;
106 QVector<RowData> m_pendingRows;
107 QVector<QModelIndex> m_changedData;
108 int m_currentRowsCount = 0;
109 const int m_expectedRowsCount;
110};
111
112QTextStream cout(stdout, QIODevice::WriteOnly);
113
114//Keep in case we need detailed debugging in the future...
115#if 0
116inline QDebug operator <<(QDebug dbg, const InsertedRow &row)
117{
118 return dbg.nospace() << "Index(" << row.m_index << ", " << row.m_start << ", " << row.m_end << ")";
119}
120
121inline void dumpModel(const QAbstractItemModel *model, const QModelIndex &parent = QModelIndex())
122{
123 int level = 0;
124 for (QModelIndex idx = parent; idx.isValid(); idx = model->parent(idx), ++level);
125 const QHash<int,QByteArray> roles = model->roleNames();
126 for (int i = 0; i < model->rowCount(parent); ++i) {
127 for (int j = 0; j < model->columnCount(parent); ++j) {
128 const QModelIndex index = model->index(i, j);
129 Q_ASSERT(index.isValid());
130 QString s;
131 s.fill(QChar(' '), level*2);
132 cout << qPrintable(s);
133 cout << QString(QLatin1String("%1:%2")).arg(i).arg(j);
134 for (auto it = roles.cbegin(), end = roles.cend(); it != end; ++it) {
135 const QVariant v = model->data(index, it.key());
136 if (!v.isValid()) continue;
137 QString t;
138 QDebug(&t) << v;
139 cout << " " << QString::fromUtf8(it.value()) << "=" << t.trimmed();
140 }
141
142 {
143 QString t;
144 QDebug(&t) << model->flags(index);
145 cout << " flags=" << t;
146 }
147
148 cout << "\n";
149 cout.flush();
150 dumpModel(model, index); // recursive
151 }
152 }
153}
154#endif
155
156void compareData(const QAbstractItemModel *sourceModel, const QAbstractItemModelReplica *replica)
157{
158 QVERIFY(sourceModel);
159 QVERIFY(replica);
160
161 QCOMPARE(replica->rowCount(), sourceModel->rowCount());
162 QCOMPARE(replica->columnCount(), sourceModel->columnCount());
163 QCOMPARE(replica->roleNames(), sourceModel->roleNames());
164
165 for (int i = 0; i < sourceModel->rowCount(); ++i) {
166 for (int j = 0; j < sourceModel->columnCount(); ++j) {
167 const auto roles = replica->availableRoles();
168 for (int role : roles) {
169 QCOMPARE(replica->index(i, j).data(role), sourceModel->index(i, j).data(role));
170 }
171 }
172 }
173}
174
175void compareIndex(const QModelIndex &sourceIndex, const QModelIndex &replicaIndex,
176 const QVector<int> &roles)
177{
178 QVERIFY(sourceIndex.isValid());
179 QVERIFY(replicaIndex.isValid());
180 for (int role : roles) {
181 QCOMPARE(replicaIndex.data(role), sourceIndex.data(role));
182 }
183 const QAbstractItemModel *sourceModel = sourceIndex.model();
184 const QAbstractItemModel *replicaModel = replicaIndex.model();
185 const int sourceRowCount = sourceModel->rowCount(parent: sourceIndex);
186 const int replicaRowCount = replicaModel->rowCount(parent: replicaIndex);
187 QCOMPARE(replicaRowCount, sourceRowCount);
188 const int sourceColumnCount = sourceModel->columnCount(parent: sourceIndex);
189 const int replicaColumnCount = replicaModel->columnCount(parent: replicaIndex);
190 // only test the column count if the row count is larger than zero, because we
191 // assume the column count is constant over a tree model and it doesn't make a
192 // difference in the view.
193 if (sourceRowCount)
194 QCOMPARE(replicaColumnCount, sourceColumnCount);
195 for (int i = 0; i < sourceRowCount; ++i) {
196 for (int j = 0; j < sourceColumnCount; ++j) {
197 auto sourceChild = sourceModel->index(row: i, column: j, parent: sourceIndex);
198 auto replicaChild = replicaModel->index(row: i, column: j, parent: replicaIndex);
199 compareIndex(sourceIndex: sourceChild, replicaIndex: replicaChild, roles);
200
201 }
202 }
203}
204
205void compareTreeData(const QAbstractItemModel *sourceModel, const QAbstractItemModelReplica *replica)
206{
207 QVERIFY(sourceModel);
208 QVERIFY(replica);
209
210 QCOMPARE(replica->rowCount(), sourceModel->rowCount());
211 QCOMPARE(replica->columnCount(), sourceModel->columnCount());
212
213 for (int i = 0; i < sourceModel->rowCount(); ++i) {
214 for (int j = 0; j < sourceModel->columnCount(); ++j) {
215 const QModelIndex replicaIndex = replica->index(row: i, column: j);
216 const QModelIndex sourceIndex = sourceModel->index(row: i, column: j);
217 compareIndex(sourceIndex, replicaIndex, roles: replica->availableRoles());
218 }
219 }
220}
221
222void compareTreeData(const QAbstractItemModel *sourceModel, const QAbstractItemModel *replica, const QVector<int> &roles)
223{
224 QVERIFY(sourceModel);
225 QVERIFY(replica);
226
227 QCOMPARE(replica->rowCount(), sourceModel->rowCount());
228 QCOMPARE(replica->columnCount(), sourceModel->columnCount());
229
230 for (int i = 0; i < sourceModel->rowCount(); ++i) {
231 for (int j = 0; j < sourceModel->columnCount(); ++j) {
232 const QModelIndex replicaIndex = replica->index(row: i, column: j);
233 const QModelIndex sourceIndex = sourceModel->index(row: i, column: j);
234 compareIndex(sourceIndex, replicaIndex, roles);
235 }
236 }
237}
238
239void compareFlags(const QAbstractItemModel *sourceModel, const QAbstractItemModelReplica *replica)
240{
241 QVERIFY(sourceModel);
242 QVERIFY(replica);
243
244 QCOMPARE(replica->rowCount(), sourceModel->rowCount());
245 QCOMPARE(replica->columnCount(), sourceModel->columnCount());
246
247 for (int i = 0; i < sourceModel->rowCount(); ++i) {
248 for (int j = 0; j < sourceModel->columnCount(); ++j) {
249 if (replica->index(row: i, column: j).flags() != sourceModel->index(row: i, column: j).flags())
250 qWarning() << sourceModel->index(row: i, column: j).flags() << replica->index(row: i, column: j).flags() << i << j;
251 QCOMPARE(replica->index(i, j).flags(), sourceModel->index(i, j).flags());
252 }
253 }
254}
255
256// class to test cutom role names
257class RolenamesListModel : public QAbstractListModel
258{
259public:
260 explicit RolenamesListModel(QObject *parent = 0) : QAbstractListModel(parent) { }
261 int rowCount(const QModelIndex &) const override{return m_list.length(); }
262 QVariant data(const QModelIndex &index, int role) const override
263 {
264 if (role == Qt::UserRole)
265 return m_list.at(i: index.row()).second;
266 else if (role == Qt::UserRole+1)
267 return m_list.at(i: index.row()).first;
268 else
269 return QVariant();
270 }
271 QHash<int, QByteArray> roleNames() const override
272 {
273 QHash<int, QByteArray> roles;
274 roles[Qt::UserRole] = "name";
275 roles[Qt::UserRole+1] = "pid";
276 return roles;
277 }
278 void addPair(const QVariant pid,const QVariant name) {
279 m_list.append(t: qMakePair(x: pid, y: name));
280 }
281 void clearList() {
282 m_list.clear();
283 }
284private:
285 QVector<QPair<QVariant, QVariant> > m_list;
286};
287
288QList<QStandardItem*> addChild(int numChilds, int nestingLevel)
289{
290 QList<QStandardItem*> result;
291 if (nestingLevel == 0)
292 return result;
293 for (int i = 0; i < numChilds; ++i) {
294 QStandardItem *child = new QStandardItem(QStringLiteral("Child num %1, nesting Level %2").arg(a: i+1).arg(a: nestingLevel));
295 if (i == 0) {
296 QList<QStandardItem*> res = addChild(numChilds, nestingLevel: nestingLevel -1);
297 if (res.size() > 0)
298 child->appendRow(aitems: res);
299 }
300 result.push_back(t: child);
301 }
302 return result;
303}
304
305int getRandomNumber(int min, int max)
306{
307 int res = QRandomGenerator::global()->generate() & INT_MAX;
308 const int diff = (max - min);
309 res = res % diff;
310 res += min;
311 return res;
312}
313
314class FetchData : public WaitHelper
315{
316public:
317 FetchData(const QAbstractItemModelReplica *replica) : WaitHelper(), m_replica(replica)
318 {
319 if (!m_replica->isInitialized()) {
320 QEventLoop l;
321 connect(sender: m_replica, signal: &QAbstractItemModelReplica::initialized, receiver: &l, slot: &QEventLoop::quit);
322 l.exec();
323 }
324
325 connect(sender: m_replica, signal: &QAbstractItemModelReplica::dataChanged, receiver: this, slot: &FetchData::dataChanged);
326 connect(sender: m_replica, signal: &QAbstractItemModelReplica::rowsInserted, receiver: this, slot: &FetchData::rowsInserted);
327 }
328
329 bool fetchAndWait(int timeout = 15000)
330 {
331 addAll();
332 fetch();
333 return wait(timeout);
334 }
335
336private:
337 const QAbstractItemModelReplica *m_replica;
338 QHash<QPersistentModelIndex, QVector<int>> m_pending;
339 QSet<QPersistentModelIndex> m_waitForInsertion;
340
341 void addData(const QModelIndex &index, const QVector<int> &roles)
342 {
343 for (int role : roles) {
344 const bool cached = m_replica->hasData(index, role);
345 if (cached)
346 continue;
347 if (!m_pending.contains(key: index))
348 m_pending[index] = QVector<int>() << role;
349 else {
350 if (!m_pending[index].contains(t: role))
351 m_pending[index].append(t: role);
352 }
353 }
354 }
355
356 void addIndex(const QModelIndex &parent, const QVector<int> &roles)
357 {
358 if (parent.isValid())
359 addData(index: parent, roles);
360 for (int i = 0; i < m_replica->rowCount(parent); ++i) {
361 for (int j = 0; j < m_replica->columnCount(parent); ++j) {
362 const QModelIndex index = m_replica->index(row: i, column: j, parent);
363 Q_ASSERT(index.isValid());
364 addIndex(parent: index, roles);
365 }
366 }
367 }
368
369 void addAll()
370 {
371 addIndex(parent: QModelIndex(), roles: m_replica->availableRoles());
372 }
373
374 void fetch()
375 {
376 if (m_pending.isEmpty() && m_waitForInsertion.isEmpty()) {
377 finish();
378 return;
379 }
380 QHash<QPersistentModelIndex, QVector<int> > pending(m_pending);
381 pending.detach();
382 QHash<QPersistentModelIndex, QVector<int> >::ConstIterator it(pending.constBegin()), end(pending.constEnd());
383 for (; it != end; ++it) {
384 for (int role : it.value()) {
385 QVariant v = m_replica->data(index: it.key(), role);
386 Q_UNUSED(v);
387 }
388 }
389 }
390
391 void rowsInserted(const QModelIndex &parent, int first, int last)
392 {
393 static QVector<int> rolesV;
394 if (rolesV.isEmpty())
395 rolesV << Qt::DisplayRole << Qt::BackgroundRole;
396 m_waitForInsertion.remove(value: parent);
397 const int columnCount = m_replica->columnCount(parent);
398 if (!(m_replica->hasChildren(parent) && columnCount > 0 && m_replica->rowCount(parent) > 0))
399 qWarning() << m_replica->hasChildren(parent) << columnCount << m_replica->rowCount(parent) << parent.data();
400 QVERIFY(m_replica->hasChildren(parent) && columnCount > 0 && m_replica->rowCount(parent) > 0);
401 for (int i = first; i <= last; ++i) {
402 for (int j = 0; j < columnCount; ++j) {
403 const QModelIndex index = m_replica->index(row: i, column: j, parent);
404 const int childRowCount = m_replica->rowCount(parent: index);
405
406 QVERIFY(index.isValid());
407 if (m_replica->hasChildren(parent: index) && childRowCount == 0) {
408 if (index.column() == 0)
409 m_waitForInsertion.insert(value: index);
410 }
411 addIndex(parent: index, roles: rolesV);
412 }
413 }
414 if (m_replica->hasChildren(parent))
415 fetch();
416 }
417
418 void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>())
419 {
420 Q_ASSERT(topLeft.isValid());
421 Q_ASSERT(bottomRight.isValid());
422 Q_ASSERT(topLeft.parent() == bottomRight.parent());
423
424 const QModelIndex parent = topLeft.parent();
425 for (int i = topLeft.row(); i <= bottomRight.row(); ++i) {
426 for (int j = topLeft.column(); j <= bottomRight.column(); ++j) {
427 const QModelIndex index = m_replica->index(row: i, column: j, parent);
428 Q_ASSERT(index.isValid());
429 QHash<QPersistentModelIndex, QVector<int> >::Iterator it = m_pending.find(key: index);
430 if (it == m_pending.end())
431 continue;
432
433#if 0
434 QVector<int> itroles = it.value();
435 itroles.detach();
436 if (roles.isEmpty()) {
437 itroles.clear();
438 } else {
439 for (int r : roles) {
440 itroles.removeAll(r);
441 }
442 }
443 if (itroles.isEmpty()) {
444 m_pending.erase(it);
445 } else {
446 m_pending[index] = itroles;
447 }
448#else
449 Q_UNUSED(roles);
450 m_pending.erase(it);
451 if (m_replica->hasChildren(parent: index)) {
452 // ask for the row count to get an update
453 const int rowCount = m_replica->rowCount(parent: index);
454 for (int i = 0; i < rowCount; ++i) {
455 const QModelIndex cIndex = m_replica->index(row: i, column: 0, parent: index);
456 Q_ASSERT(cIndex.isValid());
457 addIndex(parent: cIndex, roles: m_replica->availableRoles());
458 }
459 if (rowCount)
460 fetch();
461 else if (index.column() == 0)
462 m_waitForInsertion.insert(value: index);
463 }
464#endif
465 }
466 }
467
468 if (m_pending.isEmpty() && m_waitForInsertion.isEmpty())
469 finish();
470 }
471};
472
473} // namespace
474
475#define _SETUP_TEST_ \
476 QRemoteObjectHost basicServer; \
477 QRemoteObjectNode client; \
478 QRemoteObjectRegistryHost registryServer; \
479 setup_models(basicServer, client, registryServer);
480
481class TestModelView: public QObject
482{
483 Q_OBJECT
484
485 QStandardItemModel m_sourceModel;
486 RolenamesListModel m_listModel;
487
488public:
489 void setup_models(QRemoteObjectHost &basicServer,
490 QRemoteObjectNode &client,
491 QRemoteObjectRegistryHost &registryServer);
492
493private slots:
494 // NB: The tests have side effects on the models used, and need to be run
495 // in order (they may depend on previous side effects).
496 void initTestCase();
497
498 void testEmptyModel();
499 void testInitialData();
500 void testInitialDataTree();
501 void testHeaderData();
502 void testHeaderDataChange();
503 void testFlags();
504 void testDataChanged();
505 void testDataChangedTree();
506 void testDataInsertion();
507 void testDataInsertionTree();
508 void testSetData();
509 void testSetDataTree();
510 void testDataRemoval();
511 void testDataRemovalTree();
512 void testServerInsertDataTree();
513
514 void testRoleNames();
515
516 void testModelTest();
517 void testSortFilterModel();
518
519 void testSelectionFromReplica();
520 void testSelectionFromSource();
521 void testChildSelection();
522
523 void testCacheData_data();
524 void testCacheData();
525
526 void cleanup();
527};
528
529void TestModelView::initTestCase()
530{
531 QLoggingCategory::setFilterRules("qt.remoteobjects.warning=false");
532
533 static const int modelSize = 20;
534
535 QHash<int,QByteArray> roleNames;
536 roleNames[Qt::DisplayRole] = "text";
537 roleNames[Qt::BackgroundRole] = "background";
538 m_sourceModel.setItemRoleNames(roleNames);
539
540 QStringList list;
541 list.reserve(size: modelSize);
542
543 QStringList hHeaderList;
544 hHeaderList << QStringLiteral("First Column with spacing") << QStringLiteral("Second Column with spacing");
545 m_sourceModel.setHorizontalHeaderLabels(hHeaderList);
546
547 for (int i = 0; i < modelSize; ++i) {
548 QStandardItem *firstItem = new QStandardItem(QStringLiteral("FancyTextNumber %1").arg(a: i));
549 QStandardItem *secondItem = new QStandardItem(QStringLiteral("FancyRow2TextNumber %1").arg(a: i));
550 if (i % 2 == 0)
551 firstItem->setBackground(Qt::red);
552 firstItem->appendRow(aitems: addChild(numChilds: 2,nestingLevel: getRandomNumber(min: 1, max: 4)));
553 QList<QStandardItem*> row;
554 row << firstItem << secondItem;
555 m_sourceModel.appendRow(items: row);
556 list << QStringLiteral("FancyTextNumber %1").arg(a: i);
557 }
558
559 const int numElements = 1000;
560 for (int i = 0; i < numElements; ++i) {
561 QString name = QString("Data %1").arg(a: i);
562 QString pid = QString("%1").arg(a: i);
563 m_listModel.addPair(pid: name, name: pid);
564 }
565}
566
567void TestModelView::setup_models(QRemoteObjectHost &basicServer, QRemoteObjectNode &client, QRemoteObjectRegistryHost &registryServer)
568{
569 static int port = 65211;
570 static const QString url = QStringLiteral("tcp://127.0.0.1:%1");
571
572 // QStandardItem::flags are stored as data with Qt::UserRole - 1
573 static const QVector<int> sourceModelRoles( {Qt::DisplayRole, Qt::BackgroundRole, (Qt::UserRole - 1)} );
574
575 static const QVector<int> listModelRoles( {Qt::UserRole, Qt::UserRole+1} );
576
577 //Setup registry
578 //Registry needs to be created first until we get the retry mechanism implemented
579 basicServer.setHostUrl(hostAddress: QUrl(url.arg(a: port)));
580 registryServer.setRegistryUrl(QUrl(url.arg(a: port+1)));
581 basicServer.setRegistryUrl(QUrl(url.arg(a: port+1)));
582 basicServer.enableRemoting(model: &m_sourceModel, name: "test", roles: sourceModelRoles);
583 basicServer.enableRemoting(model: &m_listModel, name: "testRoleNames", roles: listModelRoles);
584 client.setRegistryUrl(QUrl(url.arg(a: port+1)));
585 port += 2;
586}
587
588#ifdef SLOW_MODELTEST
589#define MODELTEST_WAIT_TIME 25000
590#else
591#define MODELTEST_WAIT_TIME
592#endif
593
594void TestModelView::testEmptyModel()
595{
596 _SETUP_TEST_
597 QVector<int> roles = QVector<int>() << Qt::DisplayRole << Qt::BackgroundRole;
598 QStandardItemModel emptyModel;
599 basicServer.enableRemoting(model: &emptyModel, name: "emptyModel", roles);
600
601 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "emptyModel"));
602 model->setRootCacheSize(1000);
603
604 FetchData f(model.data());
605 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
606
607 compareData(sourceModel: &emptyModel, replica: model.data());
608}
609
610void TestModelView::testInitialData()
611{
612 _SETUP_TEST_
613 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "test"));
614
615 FetchData f(model.data());
616 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
617
618 compareData(sourceModel: &m_sourceModel, replica: model.data());
619}
620
621void TestModelView::testInitialDataTree()
622{
623 _SETUP_TEST_
624 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "test"));
625
626 FetchData f(model.data());
627 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
628
629 compareTreeData(sourceModel: &m_sourceModel, replica: model.data());
630}
631
632void TestModelView::testHeaderData()
633{
634 _SETUP_TEST_
635 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "test"));
636
637 FetchData f(model.data());
638 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
639
640 // ask for all Data members first, so we don't have to wait for update signals
641 QSignalSpy spyHeader(model.data(), &QAbstractItemModelReplica::headerDataChanged);
642 for (int i = 0; i < m_sourceModel.rowCount(); ++i)
643 model->headerData(section: i, orientation: Qt::Vertical, role: Qt::DisplayRole);
644 for (int i = 0; i < m_sourceModel.columnCount(); ++i)
645 model->headerData(section: i, orientation: Qt::Horizontal, role: Qt::DisplayRole);
646 spyHeader.wait();
647
648 for (int i = 0; i < m_sourceModel.rowCount(); ++i)
649 QCOMPARE(model->headerData(i, Qt::Vertical, Qt::DisplayRole), m_sourceModel.headerData(i, Qt::Vertical, Qt::DisplayRole));
650 for (int i = 0; i < m_sourceModel.columnCount(); ++i)
651 QCOMPARE(model->headerData(i, Qt::Horizontal, Qt::DisplayRole), m_sourceModel.headerData(i, Qt::Horizontal, Qt::DisplayRole));
652}
653
654void TestModelView::testHeaderDataChange()
655{
656 _SETUP_TEST_
657 QString newHeader = QStringLiteral("New header name");
658 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "test"));
659
660 FetchData f(model.data());
661 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
662 QVERIFY(model->headerData(0, Qt::Horizontal, Qt::DisplayRole).toString() != newHeader);
663
664 QSignalSpy spyHeader(model.data(), &QAbstractItemModelReplica::headerDataChanged);
665 m_sourceModel.setHeaderData(section: 0, orientation: Qt::Horizontal, value: newHeader, role: Qt::DisplayRole);
666 spyHeader.wait();
667 QTRY_COMPARE(model->headerData(0, Qt::Horizontal, Qt::DisplayRole).toString(), newHeader);
668
669 spyHeader.clear();
670 m_sourceModel.setHeaderData(section: 1, orientation: Qt::Horizontal, value: newHeader, role: Qt::DisplayRole);
671 spyHeader.wait();
672 QTRY_COMPARE(model->headerData(1, Qt::Horizontal, Qt::DisplayRole).toString(), newHeader);
673
674 QString anotherHeader = QStringLiteral("Modified header name");
675 m_sourceModel.setHeaderData(section: 0, orientation: Qt::Horizontal, value: anotherHeader, role: Qt::DisplayRole);
676 spyHeader.wait();
677
678 QTRY_COMPARE(model->headerData(0, Qt::Horizontal, Qt::DisplayRole).toString(), anotherHeader);
679 QCOMPARE(model->headerData(1, Qt::Horizontal, Qt::DisplayRole).toString(), newHeader);
680}
681
682void TestModelView::testDataChangedTree()
683{
684 _SETUP_TEST_
685 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "test"));
686
687 FetchData f(model.data());
688 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
689
690 compareTreeData(sourceModel: &m_sourceModel, replica: model.data());
691 QSignalSpy dataChangedSpy(model.data(), &QAbstractItemModelReplica::dataChanged);
692 QSet<int> expected;
693 for (int i = 10; i < 20; ++i) {
694 const QModelIndex parent = m_sourceModel.index(row: i,column: 0);
695 const int rowCount = m_sourceModel.rowCount(parent);
696 const int colCount = m_sourceModel.columnCount(parent);
697 for (int row = 0; row < rowCount; ++row) {
698 for (int col = 0; col < colCount; ++col) {
699 if (col % 2 == 0)
700 m_sourceModel.setData(index: m_sourceModel.index(row, column: col, parent), value: QColor(Qt::gray), role: Qt::BackgroundRole);
701 else
702 m_sourceModel.setData(index: m_sourceModel.index(row, column: col, parent), value: QColor(Qt::cyan), role: Qt::BackgroundRole);
703 }
704 }
705 m_sourceModel.setData(index: m_sourceModel.index(row: i, column: 1), value: QColor(Qt::magenta), role: Qt::BackgroundRole);
706 expected << i;
707 }
708
709 bool signalsReceived = false;
710 int runs = 0;
711 const int maxRuns = 10;
712 while (runs < maxRuns) {
713 if (dataChangedSpy.wait() &&!dataChangedSpy.isEmpty()) {
714 signalsReceived = true;
715 for (auto args : dataChangedSpy) {
716 int row = args.at(i: 1).value<QModelIndex>().row();
717 if (row && expected.contains(value: row))
718 expected.remove(value: row);
719 }
720 if (expected.size() == 0)
721 break;
722 }
723 ++runs;
724 }
725 QVERIFY(signalsReceived);
726 compareTreeData(sourceModel: &m_sourceModel, replica: model.data());
727}
728
729void TestModelView::testFlags()
730{
731 _SETUP_TEST_
732 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "test"));
733
734 FetchData f(model.data());
735 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
736
737 QSignalSpy dataChangedSpy(model.data(), &QAbstractItemModelReplica::dataChanged);
738 for (int i = 10; i < 20; ++i) {
739 QStandardItem* firstItem = m_sourceModel.item(row: i, column: 0);
740 QStandardItem* secondItem = m_sourceModel.item(row: i, column: 1);
741 firstItem->setFlags(firstItem->flags() | Qt::ItemIsEnabled | Qt::ItemIsAutoTristate);
742 secondItem->setFlags(firstItem->flags() | Qt::ItemIsEnabled);
743 }
744 bool signalsReceived = false;
745 while (dataChangedSpy.wait()) {
746 signalsReceived = true;
747 if (dataChangedSpy.takeLast().at(i: 1).value<QModelIndex>().row() == 19)
748 break;
749 }
750 QVERIFY(signalsReceived);
751 compareFlags(sourceModel: &m_sourceModel, replica: model.data());
752}
753
754void TestModelView::testDataChanged()
755{
756 _SETUP_TEST_
757 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "test"));
758
759 FetchData f(model.data());
760 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
761
762 QSignalSpy dataChangedSpy(model.data(), SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
763 for (int i = 10; i < 20; ++i)
764 m_sourceModel.setData(index: m_sourceModel.index(row: i, column: 1), value: QColor(Qt::blue), role: Qt::BackgroundRole);
765
766 bool signalsReceived = false;
767 while (dataChangedSpy.wait()) {
768 signalsReceived = true;
769 if (dataChangedSpy.takeLast().at(i: 1).value<QModelIndex>().row() == 19)
770 break;
771 }
772 QVERIFY(signalsReceived);
773 compareData(sourceModel: &m_sourceModel, replica: model.data());
774}
775
776void TestModelView::testDataInsertion()
777{
778 _SETUP_TEST_
779 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "test"));
780
781 FetchData f(model.data());
782 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
783
784 const int insertedRowsCount = 9;
785 RowsWatcher watcher(model.data(), insertedRowsCount);
786 QVector<QModelIndex> pending;
787
788 m_sourceModel.insertRows(row: 2, count: insertedRowsCount);
789
790 watcher.scheduleRowsToWatch(index: QModelIndex(), start: 2, end: 2 + insertedRowsCount - 1);
791 QVERIFY(watcher.wait());
792 QCOMPARE(m_sourceModel.rowCount(), model->rowCount());
793
794 pending.append(l: watcher.changedData());
795
796 // change one row to check for inconsistencies
797 m_sourceModel.setData(index: m_sourceModel.index(row: 0, column: 1), value: QColor(Qt::green), role: Qt::BackgroundRole);
798 m_sourceModel.setData(index: m_sourceModel.index(row: 0, column: 1), value: QLatin1String("foo"), role: Qt::DisplayRole);
799 pending.append(t: model->index(row: 0, column: 1));
800 WaitForDataChanged w(model.data(), pending);
801
802 QVERIFY(w.wait());
803 compareData(sourceModel: &m_sourceModel, replica: model.data());
804}
805
806void TestModelView::testDataInsertionTree()
807{
808 _SETUP_TEST_
809 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "test"));
810
811 FetchData f(model.data());
812 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
813
814 const int insertedRowsCount = 9;
815 const int insertedChildRowsCount = 4;
816 RowsWatcher watcher(model.data(), insertedRowsCount + insertedChildRowsCount);
817
818 QVector<QModelIndex> pending;
819
820 for (int i = 0; i < insertedRowsCount; ++i) {
821 watcher.scheduleRowsToWatch(index: QModelIndex(), start: 2 + i, end: 2 + i);
822 m_sourceModel.insertRow(row: 2 + i, items: createInsertionChildren(num: 2, QStringLiteral("insertedintree"), background: Qt::darkRed));
823 const QModelIndex childIndex = m_sourceModel.index(row: 2 + i, column: 0);
824 const QModelIndex childIndex2 = m_sourceModel.index(row: 2 + i, column: 1);
825 pending.append(t: childIndex);
826 pending.append(t: childIndex2);
827 }
828 const QModelIndex parent = m_sourceModel.index(row: 10, column: 0);
829 QStandardItem* parentItem = m_sourceModel.item(row: 10, column: 0);
830 for (int i = 0; i < insertedChildRowsCount; ++i) {
831 watcher.scheduleRowsToWatch(index: parent, start: i, end: i);
832 parentItem->insertRow(row: i, items: createInsertionChildren(num: 2, QStringLiteral("insertedintreedeep"), background: Qt::darkCyan));
833 const QModelIndex childIndex = m_sourceModel.index(row: 0, column: 0, parent);
834 const QModelIndex childIndex2 = m_sourceModel.index(row: 0, column: 1, parent);
835 Q_ASSERT(childIndex.isValid());
836 Q_ASSERT(childIndex2.isValid());
837 pending.append(t: childIndex);
838 pending.append(t: childIndex2);
839 }
840
841 QVERIFY(watcher.wait());
842 QCOMPARE(m_sourceModel.rowCount(), model->rowCount());
843
844 pending.append(l: watcher.changedData());
845
846 // change one row to check for inconsistencies
847
848 pending << m_sourceModel.index(row: 0, column: 0, parent);
849 WaitForDataChanged w(model.data(), pending);
850 m_sourceModel.setData(index: m_sourceModel.index(row: 0, column: 0, parent), value: QColor(Qt::green), role: Qt::BackgroundRole);
851 m_sourceModel.setData(index: m_sourceModel.index(row: 0, column: 0, parent), value: QLatin1String("foo"), role: Qt::DisplayRole);
852
853 QVERIFY(w.wait());
854
855 compareTreeData(sourceModel: &m_sourceModel, replica: model.data());
856}
857
858void TestModelView::testDataRemoval()
859{
860 _SETUP_TEST_
861 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "test"));
862 qputenv(varName: "QTRO_NODES_CACHE_SIZE", value: "1000");
863 model->setRootCacheSize(1000);
864 FetchData f(model.data());
865 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
866
867 const QPersistentModelIndex parent = m_sourceModel.index(row: 10, column: 0);
868 {
869 const int removedRowsCount = 3;
870 RowsWatcher watcher(model.data(), removedRowsCount);
871 m_sourceModel.removeRows(row: 0, count: removedRowsCount, parent);
872 watcher.scheduleRowsToWatch(index: parent, start: 0, end: removedRowsCount - 1);
873 QVERIFY(watcher.wait());
874 QCOMPARE(m_sourceModel.rowCount(parent), model->rowCount(model->index(10, 0)));
875 }
876 {
877 const int removedRowsCount = 8;
878 RowsWatcher watcher(model.data(), removedRowsCount);
879 m_sourceModel.removeRows(row: 2, count: removedRowsCount);
880 watcher.scheduleRowsToWatch(index: QModelIndex(), start: 2, end: 2 + removedRowsCount - 1);
881 QVERIFY(watcher.wait());
882 QCOMPARE(m_sourceModel.rowCount(), model->rowCount());
883 }
884
885 // change one row to check for inconsistencies
886
887 QVector<QModelIndex> pending;
888 pending << m_sourceModel.index(row: 0, column: 0, parent);
889 WaitForDataChanged w(model.data(), pending);
890 m_sourceModel.setData(index: m_sourceModel.index(row: 0, column: 0, parent), value: QColor(Qt::green), role: Qt::BackgroundRole);
891 m_sourceModel.setData(index: m_sourceModel.index(row: 0, column: 0, parent), value: QLatin1String("foo"), role: Qt::DisplayRole);
892
893 QVERIFY(w.wait());
894
895 compareTreeData(sourceModel: &m_sourceModel, replica: model.data());
896}
897
898void TestModelView::testRoleNames()
899{
900 _SETUP_TEST_
901 QScopedPointer<QAbstractItemModelReplica> repModel( client.acquireModel(QStringLiteral("testRoleNames")));
902 // Set a bigger cache enough to keep all the data otherwise the last test will fail
903 repModel->setRootCacheSize(1500);
904 FetchData f(repModel.data());
905 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
906
907 // test custom role names
908 QCOMPARE(repModel.data()->roleNames(), m_listModel.roleNames());
909
910 // test data associated with custom roles
911 compareData(sourceModel: &m_listModel,replica: repModel.data());
912}
913
914void TestModelView::testDataRemovalTree()
915{
916 _SETUP_TEST_
917 m_sourceModel.removeRows(row: 2, count: 4);
918 //Maybe some checks here?
919}
920
921void TestModelView::testServerInsertDataTree()
922{
923 _SETUP_TEST_
924 QVector<int> roles = QVector<int>() << Qt::DisplayRole << Qt::BackgroundRole;
925 QStandardItemModel testTreeModel;
926 basicServer.enableRemoting(model: &testTreeModel, name: "testTreeModel", roles);
927
928 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "testTreeModel"));
929
930 QTRY_COMPARE(testTreeModel.rowCount(), model->rowCount());
931
932 QVERIFY(testTreeModel.insertRow(0));
933 QVERIFY(testTreeModel.insertColumn(0));
934 auto root = testTreeModel.index(row: 0, column: 0);
935 QVERIFY(testTreeModel.setData(root, QLatin1String("Root"), Qt::DisplayRole));
936 QVERIFY(testTreeModel.setData(root, QColor(Qt::green), Qt::BackgroundRole));
937 QVERIFY(testTreeModel.insertRow(0, root));
938 QVERIFY(testTreeModel.insertColumn(0, root));
939 auto child1 = testTreeModel.index(row: 0, column: 0, parent: root);
940 QVERIFY(testTreeModel.setData(child1, QLatin1String("Child1"), Qt::DisplayRole));
941 QVERIFY(testTreeModel.setData(child1, QColor(Qt::red), Qt::BackgroundRole));
942
943 QTRY_COMPARE(testTreeModel.rowCount(), model->rowCount());
944
945 FetchData f(model.data());
946 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
947
948 compareData(sourceModel: &testTreeModel, replica: model.data());
949}
950
951void TestModelView::testModelTest()
952{
953 _SETUP_TEST_
954 QScopedPointer<QAbstractItemModelReplica> repModel( client.acquireModel(QStringLiteral("test")));
955 ModelTest test(repModel.data());
956
957 FetchData f(repModel.data());
958 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
959 Q_UNUSED(test);
960}
961
962void TestModelView::testSortFilterModel()
963{
964 _SETUP_TEST_
965 QScopedPointer<QAbstractItemModelReplica> repModel( client.acquireModel(QStringLiteral("test")));
966
967 FetchData f(repModel.data());
968 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
969
970 QSortFilterProxyModel clientSort;
971 clientSort.setSourceModel(repModel.data());
972 clientSort.setSortRole(Qt::DisplayRole);
973 QSortFilterProxyModel sourceSort;
974 sourceSort.setSourceModel(&m_sourceModel);
975 sourceSort.setSortRole(Qt::DisplayRole);
976
977 compareTreeData(sourceModel: &sourceSort, replica: &clientSort, roles: repModel->availableRoles());
978}
979
980void TestModelView::testSetData()
981{
982 _SETUP_TEST_
983 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "test"));
984
985 FetchData f(model.data());
986 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
987 compareTreeData(sourceModel: &m_sourceModel, replica: model.data(), roles: model->availableRoles());
988
989 //fetched and verified initial state, now setData on the client
990 QVector<QModelIndex> pending;
991 QVector<QModelIndex> pendingReplica;
992 for (int row = 0, numRows = model->rowCount(); row < numRows; ++row) {
993 for (int column = 0, numColumns = model->columnCount(); column != numColumns; ++column) {
994 const QModelIndex index = model->index(row, column);
995 const QString newData = QStringLiteral("This entry was changed with setData");
996 QVERIFY(model->setData(index, newData, Qt::DisplayRole));
997 pending.append(t: m_sourceModel.index(row, column));
998 pendingReplica.append(t: model->index(row, column));
999 }
1000 }
1001 WaitForDataChanged waiter(&m_sourceModel, pending);
1002 QVERIFY(waiter.wait());
1003 WaitForDataChanged waiterReplica(model.data(), pendingReplica);
1004 QVERIFY(waiterReplica.wait());
1005 compareData(sourceModel: &m_sourceModel, replica: model.data());
1006}
1007
1008void TestModelView::testSetDataTree()
1009{
1010 _SETUP_TEST_
1011 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "test"));
1012
1013 FetchData f(model.data());
1014 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
1015 compareTreeData(sourceModel: &m_sourceModel, replica: model.data(), roles: model->availableRoles());
1016
1017 //fetched and verified initial state, now setData on the client
1018 QVector<QModelIndex> pending;
1019 QVector<QModelIndex> pendingReplica;
1020
1021 QVector<QModelIndex> stack;
1022 stack.push_back(t: QModelIndex());
1023 QVector<QModelIndex> sourceStack;
1024 sourceStack.push_back(t: QModelIndex());
1025
1026
1027 const QString newData = QStringLiteral("This entry was changed with setData in a tree %1 %2 %3");
1028 while (!stack.isEmpty()) {
1029 const QModelIndex parent = stack.takeLast();
1030 const QModelIndex parentSource = sourceStack.takeLast();
1031 for (int row = 0; row < model->rowCount(parent); ++row) {
1032 for (int column = 0; column < model->columnCount(parent); ++column) {
1033 const QModelIndex index = model->index(row, column, parent);
1034 const QModelIndex indexSource = m_sourceModel.index(row, column, parent: parentSource);
1035 QVERIFY(model->setData(index, newData.arg(parent.isValid()).arg(row).arg(column), Qt::DisplayRole));
1036 pending.append(t: indexSource);
1037 pendingReplica.append(t: index);
1038 if (column == 0) {
1039 stack.push_back(t: index);
1040 sourceStack.push_back(t: indexSource);
1041 }
1042 }
1043 }
1044 }
1045 WaitForDataChanged waiter(&m_sourceModel, pending);
1046 QVERIFY(waiter.wait());
1047 WaitForDataChanged waiterReplica(model.data(), pendingReplica);
1048 QVERIFY(waiterReplica.wait());
1049 compareData(sourceModel: &m_sourceModel, replica: model.data());
1050}
1051
1052void TestModelView::testSelectionFromReplica()
1053{
1054 _SETUP_TEST_
1055 QVector<int> roles = QVector<int>() << Qt::DisplayRole << Qt::BackgroundRole;
1056 QStandardItemModel simpleModel;
1057 for (int i = 0; i < 4; ++i)
1058 simpleModel.appendRow(aitem: new QStandardItem(QString("item %0").arg(a: i)));
1059 QItemSelectionModel selectionModel(&simpleModel);
1060 basicServer.enableRemoting(model: &simpleModel, name: "simpleModelFromReplica", roles, selectionModel: &selectionModel);
1061
1062 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "simpleModelFromReplica"));
1063 QItemSelectionModel *replicaSelectionModel = model->selectionModel();
1064
1065 FetchData f(model.data());
1066 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
1067
1068 replicaSelectionModel->setCurrentIndex(index: model->index(row: 1,column: 0), command: QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current);
1069 QTRY_COMPARE(selectionModel.currentIndex().row(), 1);
1070}
1071
1072void TestModelView::testSelectionFromSource()
1073{
1074 _SETUP_TEST_
1075 QVector<int> roles = QVector<int>() << Qt::DisplayRole << Qt::BackgroundRole;
1076 QStandardItemModel simpleModel;
1077 for (int i = 0; i < 4; ++i)
1078 simpleModel.appendRow(aitem: new QStandardItem(QString("item %0").arg(a: i)));
1079 QItemSelectionModel selectionModel(&simpleModel);
1080 basicServer.enableRemoting(model: &simpleModel, name: "simpleModelFromSource", roles, selectionModel: &selectionModel);
1081
1082 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "simpleModelFromSource"));
1083 QItemSelectionModel *replicaSelectionModel = model->selectionModel();
1084
1085 FetchData f(model.data());
1086 QVERIFY(f.fetchAndWait(MODELTEST_WAIT_TIME));
1087
1088 selectionModel.setCurrentIndex(index: simpleModel.index(row: 1,column: 0), command: QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current);
1089 QTRY_COMPARE(replicaSelectionModel->currentIndex().row(), 1);
1090}
1091
1092void TestModelView::testCacheData_data()
1093{
1094 QTest::addColumn<QVector<int>>(name: "roles");
1095
1096 QTest::newRow(dataTag: "empty") << QVector<int> {};
1097 QTest::newRow(dataTag: "all") << QVector<int> { Qt::UserRole, Qt::UserRole + 1 };
1098}
1099
1100void TestModelView::testCacheData()
1101{
1102 QFETCH(QVector<int>, roles);
1103
1104 _SETUP_TEST_
1105 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "testRoleNames", action: QtRemoteObjects::PrefetchData, rolesHint: roles));
1106 model->setRootCacheSize(1000);
1107
1108 QEventLoop l;
1109 connect(sender: model.data(), signal: &QAbstractItemModelReplica::initialized, receiver: &l, slot: &QEventLoop::quit);
1110 l.exec();
1111
1112 compareData(sourceModel: &m_listModel, replica: model.data());
1113}
1114
1115void TestModelView::testChildSelection()
1116{
1117 _SETUP_TEST_
1118 QVector<int> roles = {Qt::DisplayRole, Qt::BackgroundRole};
1119 QStandardItemModel simpleModel;
1120 QStandardItem *parentItem = simpleModel.invisibleRootItem();
1121 for (int i = 0; i < 4; ++i) {
1122 QStandardItem *item = new QStandardItem(QString("item %0").arg(a: i));
1123 parentItem->appendRow(aitem: item);
1124 parentItem = item;
1125 }
1126 QItemSelectionModel selectionModel(&simpleModel);
1127 basicServer.enableRemoting(model: &simpleModel, name: "treeModelFromSource", roles, selectionModel: &selectionModel);
1128
1129 QScopedPointer<QAbstractItemModelReplica> model(client.acquireModel(name: "treeModelFromSource", action: QtRemoteObjects::PrefetchData, rolesHint: roles));
1130 QItemSelectionModel *replicaSelectionModel = model->selectionModel();
1131
1132 QTRY_COMPARE(simpleModel.rowCount(), model->rowCount());
1133 QTRY_COMPARE(model->data(model->index(0, 0)), QVariant(QString("item 0")));
1134
1135 // select an item not yet "seen" by the replica
1136 selectionModel.setCurrentIndex(index: simpleModel.index(row: 0, column: 0, parent: simpleModel.index(row: 0,column: 0)), command: QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current);
1137 QTRY_COMPARE(replicaSelectionModel->currentIndex().row(), 0);
1138 QVERIFY(replicaSelectionModel->currentIndex().parent().isValid());
1139}
1140
1141void TestModelView::cleanup()
1142{
1143 // wait for delivery of RemoveObject events to the source
1144 QTest::qWait(ms: 20);
1145}
1146
1147QTEST_MAIN(TestModelView)
1148
1149#include "tst_modelview.moc"
1150

source code of qtremoteobjects/tests/auto/modelview/tst_modelview.cpp