1/****************************************************************************
2**
3** Copyright (C) 2016 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
29#include <QtTest/QtTest>
30#include <QtCore/QCoreApplication>
31#include <qdebug.h>
32#include "modelstotest.cpp"
33#include <QMetaType>
34
35/*!
36 See modelstotest.cpp for instructions on how to have your model tested with these tests.
37
38 Each test such as rowCount have a _data() function which populate the QTest data with
39 the tests specified by modelstotest.cpp and any extra data needed for that particular test.
40
41 setupWithNoTestData() fills the QTest data with just the tests and is used by most tests.
42 */
43class tst_QItemModel : public QObject
44{
45 Q_OBJECT
46
47public:
48 tst_QItemModel();
49
50public slots:
51 void init();
52 void cleanup();
53
54private slots:
55 void nonDestructiveBasicTest_data();
56 void nonDestructiveBasicTest();
57
58 void rowCount_data();
59 void rowCount();
60 void columnCount_data();
61 void columnCount();
62
63 void hasIndex_data();
64 void hasIndex();
65 void index_data();
66 void index();
67
68 void parent_data();
69 void parent();
70
71 void data_data();
72 void data();
73
74 void setData_data();
75 void setData();
76
77 void setHeaderData_data();
78 void setHeaderData();
79
80 void remove_data();
81 void remove();
82
83 void insert_data();
84 void insert();
85
86 void sort_data();
87 void sort();
88
89protected slots:
90 void slot_rowsAboutToRemove(const QModelIndex &parent);
91 void slot_rowsRemoved(const QModelIndex &parent);
92 void slot_columnsAboutToRemove(const QModelIndex &parent);
93 void slot_columnsRemoved(const QModelIndex &parent);
94
95 void slot_rowsAboutToInserted(const QModelIndex &parent);
96 void slot_rowsInserted(const QModelIndex &parent);
97 void slot_columnsAboutToInserted(const QModelIndex &parent);
98 void slot_columnsInserted(const QModelIndex &parent);
99private:
100 void setupWithNoTestData();
101 QAbstractItemModel *currentModel;
102 ModelsToTest *testModels;
103
104 // used by remove()
105 QPersistentModelIndex parentOfRemoved;
106 int afterAboutToRemoveRowCount;
107 int afterRemoveRowCount;
108 int afterAboutToRemoveColumnCount;
109 int afterRemoveColumnCount;
110
111 // remove() recursive
112 bool removeRecursively;
113
114 // used by insert()
115 QPersistentModelIndex parentOfInserted;
116 int afterAboutToInsertRowCount;
117 int afterInsertRowCount;
118 int afterAboutToInsertColumnCount;
119 int afterInsertColumnCount;
120
121 // insert() recursive
122 bool insertRecursively;
123};
124
125tst_QItemModel::tst_QItemModel()
126{
127 qRegisterMetaType<QAbstractItemModel::LayoutChangeHint>();
128 qRegisterMetaType<QList<QPersistentModelIndex>>();
129}
130
131void tst_QItemModel::init()
132{
133 testModels = new ModelsToTest();
134 removeRecursively = false;
135 insertRecursively = false;
136}
137
138void tst_QItemModel::cleanup()
139{
140 testModels->cleanupTestArea(model: currentModel);
141 delete testModels;
142 delete currentModel;
143 currentModel = 0;
144}
145
146void tst_QItemModel::setupWithNoTestData()
147{
148 ModelsToTest modelsToTest;
149 QTest::addColumn<QString>(name: "modelType");
150 QTest::addColumn<bool>(name: "readOnly");
151 QTest::addColumn<bool>(name: "isEmpty");
152 for (int i = 0; i < modelsToTest.tests.size(); ++i) {
153 ModelsToTest::test t = modelsToTest.tests.at(i);
154 bool readOnly = (t.read == ModelsToTest::ReadOnly);
155 bool isEmpty = (t.contains == ModelsToTest::Empty);
156 QTest::newRow(dataTag: t.modelType.toLatin1().data()) << t.modelType << readOnly << isEmpty;
157 }
158}
159
160void tst_QItemModel::nonDestructiveBasicTest_data()
161{
162 setupWithNoTestData();
163}
164
165/*!
166 nonDestructiveBasicTest tries to call a number of the basic functions (not all)
167 to make sure the model doesn't segfault, testing the functions that makes sense.
168 */
169void tst_QItemModel::nonDestructiveBasicTest()
170{
171 QFETCH(QString, modelType);
172 currentModel = testModels->createModel(modelType);
173 QVERIFY(currentModel);
174
175 QCOMPARE(currentModel->buddy(QModelIndex()), QModelIndex());
176 currentModel->canFetchMore(parent: QModelIndex());
177 QVERIFY(currentModel->columnCount(QModelIndex()) >= 0);
178 QCOMPARE(currentModel->data(QModelIndex()), QVariant());
179 currentModel->fetchMore(parent: QModelIndex());
180 Qt::ItemFlags flags = currentModel->flags(index: QModelIndex());
181 QVERIFY(flags == Qt::ItemIsDropEnabled || flags == 0);
182 currentModel->hasChildren(parent: QModelIndex());
183 currentModel->hasIndex(row: 0, column: 0);
184 currentModel->headerData(section: 0, orientation: Qt::Horizontal);
185 currentModel->index(row: 0,column: 0);
186 currentModel->itemData(index: QModelIndex());
187 QVariant cache;
188 currentModel->match(start: QModelIndex(), role: -1, value: cache);
189 currentModel->mimeTypes();
190 QCOMPARE(currentModel->parent(QModelIndex()), QModelIndex());
191 QVERIFY(currentModel->rowCount() >= 0);
192 QVariant variant;
193 currentModel->setData(index: QModelIndex(), value: variant, role: -1);
194 currentModel->setHeaderData(section: -1, orientation: Qt::Horizontal, value: QVariant());
195 currentModel->setHeaderData(section: 0, orientation: Qt::Horizontal, value: QVariant());
196 currentModel->setHeaderData(section: currentModel->columnCount() + 100, orientation: Qt::Horizontal, value: QVariant());
197 QMap<int, QVariant> roles;
198 currentModel->setItemData(index: QModelIndex(), roles);
199 currentModel->sibling(row: 0,column: 0,idx: QModelIndex());
200 currentModel->span(index: QModelIndex());
201 currentModel->supportedDropActions();
202 currentModel->revert();
203 currentModel->submit();
204}
205
206
207void tst_QItemModel::rowCount_data()
208{
209 setupWithNoTestData();
210}
211
212/*!
213 Tests model's implimentation of QAbstractItemModel::rowCount() and hasChildren()
214 */
215void tst_QItemModel::rowCount()
216{
217 QFETCH(QString, modelType);
218 currentModel = testModels->createModel(modelType);
219 QVERIFY(currentModel);
220
221 QFETCH(bool, isEmpty);
222 if (isEmpty) {
223 QCOMPARE(currentModel->rowCount(), 0);
224 QCOMPARE(currentModel->hasChildren(), false);
225 }
226 else {
227 QVERIFY(currentModel->rowCount() > 0);
228 QCOMPARE(currentModel->hasChildren(), true);
229 }
230
231 // check top row
232 QModelIndex topIndex = currentModel->index(row: 0, column: 0, parent: QModelIndex());
233 int rows = currentModel->rowCount(parent: topIndex);
234 QVERIFY(rows >= 0);
235 if (rows > 0)
236 QCOMPARE(currentModel->hasChildren(topIndex), true);
237 else
238 QCOMPARE(currentModel->hasChildren(topIndex), false);
239
240 QModelIndex secondLevelIndex = currentModel->index(row: 0, column: 0, parent: topIndex);
241 if (secondLevelIndex.isValid()) { // not the top level
242 // check a row count where parent is valid
243 rows = currentModel->rowCount(parent: secondLevelIndex);
244 QVERIFY(rows >= 0);
245 if (rows > 0)
246 QCOMPARE(currentModel->hasChildren(secondLevelIndex), true);
247 else
248 QCOMPARE(currentModel->hasChildren(secondLevelIndex), false);
249 }
250
251 // rowCount is tested more extensivly more later in checkChildren(),
252 // but this catches the big mistakes
253}
254
255void tst_QItemModel::columnCount_data()
256{
257 setupWithNoTestData();
258}
259
260/*!
261 Tests model's implimentation of QAbstractItemModel::columnCount() and hasChildren()
262 */
263void tst_QItemModel::columnCount()
264{
265 QFETCH(QString, modelType);
266 currentModel = testModels->createModel(modelType);
267 QVERIFY(currentModel);
268
269 QFETCH(bool, isEmpty);
270 if (isEmpty) {
271 QCOMPARE(currentModel->hasChildren(), false);
272 }
273 else {
274 QVERIFY(currentModel->columnCount() > 0);
275 QCOMPARE(currentModel->hasChildren(), true);
276 }
277
278 // check top row
279 QModelIndex topIndex = currentModel->index(row: 0, column: 0, parent: QModelIndex());
280 int columns = currentModel->columnCount(parent: topIndex);
281
282 // check a row count where parent is valid
283 columns = currentModel->columnCount(parent: currentModel->index(row: 0, column: 0, parent: topIndex));
284 QVERIFY(columns >= 0);
285
286 // columnCount is tested more extensivly more later in checkChildren(),
287 // but this catches the big mistakes
288}
289
290void tst_QItemModel::hasIndex_data()
291{
292 setupWithNoTestData();
293}
294
295/*!
296 Tests model's implimentation of QAbstractItemModel::hasIndex()
297 */
298void tst_QItemModel::hasIndex()
299{
300 QFETCH(QString, modelType);
301 currentModel = testModels->createModel(modelType);
302 QVERIFY(currentModel);
303
304 // Make sure that invalid values returns an invalid index
305 QCOMPARE(currentModel->hasIndex(-2, -2), false);
306 QCOMPARE(currentModel->hasIndex(-2, 0), false);
307 QCOMPARE(currentModel->hasIndex(0, -2), false);
308
309 int rows = currentModel->rowCount();
310 int columns = currentModel->columnCount();
311
312 QCOMPARE(currentModel->hasIndex(rows, columns), false);
313 QCOMPARE(currentModel->hasIndex(rows+1, columns+1), false);
314
315 QFETCH(bool, isEmpty);
316 if (isEmpty)
317 return;
318
319 QCOMPARE(currentModel->hasIndex(0,0), true);
320
321 // hasIndex is tested more extensivly more later in checkChildren(),
322 // but this catches the big mistakes
323}
324
325void tst_QItemModel::index_data()
326{
327 setupWithNoTestData();
328}
329
330/*!
331 Tests model's implimentation of QAbstractItemModel::index()
332 */
333void tst_QItemModel::index()
334{
335 QFETCH(QString, modelType);
336 currentModel = testModels->createModel(modelType);
337 QVERIFY(currentModel);
338
339 // Make sure that invalid values returns an invalid index
340 QCOMPARE(currentModel->index(-2, -2), QModelIndex());
341 QCOMPARE(currentModel->index(-2, 0), QModelIndex());
342 QCOMPARE(currentModel->index(0, -2), QModelIndex());
343
344 QFETCH(bool, isEmpty);
345 if (isEmpty)
346 return;
347
348 int rows = currentModel->rowCount();
349 int columns = currentModel->columnCount();
350
351 // Catch off by one errors
352 QCOMPARE(currentModel->index(rows,columns), QModelIndex());
353 QCOMPARE(currentModel->index(0,0).isValid(), true);
354
355 // Make sure that the same index is always returned
356 QModelIndex a = currentModel->index(row: 0,column: 0);
357 QModelIndex b = currentModel->index(row: 0,column: 0);
358 QCOMPARE(a, b);
359
360 // index is tested more extensivly more later in checkChildren(),
361 // but this catches the big mistakes
362}
363
364
365void tst_QItemModel::parent_data()
366{
367 setupWithNoTestData();
368}
369
370/*!
371 A model that returns an index of parent X should also return X when asking
372 for the parent of the index.
373
374 This recursive function does pretty extensive testing on the whole model in an
375 effort to catch edge cases.
376
377 This function assumes that rowCount(), columnCount() and index() work. If they have
378 a bug it will point it out, but the above tests should have already found the basic bugs
379 because it is easier to figure out the problem in those tests then this one.
380 */
381void checkChildren(QAbstractItemModel *currentModel, const QModelIndex &parent, int currentDepth=0)
382{
383 QFETCH(bool, readOnly);
384
385 if (currentModel->canFetchMore(parent))
386 currentModel->fetchMore(parent);
387
388 int rows = currentModel->rowCount(parent);
389 int columns = currentModel->columnCount(parent);
390
391 const QModelIndex topLeftChild = currentModel->index( row: 0, column: 0, parent );
392
393 QCOMPARE(rows > 0, (currentModel->hasChildren(parent)));
394
395 // Some reasuring testing against rows(),columns(), and hasChildren()
396 QVERIFY(rows >= 0);
397 QVERIFY(columns >= 0);
398 if (rows > 0 || columns > 0)
399 QCOMPARE(currentModel->hasChildren(parent), true);
400 else
401 QCOMPARE(currentModel->hasChildren(parent), false);
402
403 QCOMPARE(currentModel->hasIndex(rows+1, 0, parent), false);
404 for (int r = 0; r < rows; ++r) {
405 if (currentModel->canFetchMore(parent))
406 currentModel->fetchMore(parent);
407
408 QCOMPARE(currentModel->hasIndex(r, columns+1, parent), false);
409 for (int c = 0; c < columns; ++c) {
410 QCOMPARE(currentModel->hasIndex(r, c, parent), true);
411 QModelIndex index = currentModel->index(row: r, column: c, parent);
412 QCOMPARE(index.isValid(), true);
413
414 if (!readOnly)
415 currentModel->setData(index, value: "I'm a little tea pot short and stout");
416 QModelIndex modifiedIndex = currentModel->index(row: r, column: c, parent);
417 QCOMPARE(index, modifiedIndex);
418
419 // Make sure we get the same index if we request it twice in a row
420 QModelIndex a = currentModel->index(row: r, column: c, parent);
421 QModelIndex b = currentModel->index(row: r, column: c, parent);
422 QCOMPARE(a, b);
423
424 {
425 const QModelIndex sibling = currentModel->sibling( row: r, column: c, idx: topLeftChild );
426 QVERIFY( index == sibling );
427 }
428 {
429 const QModelIndex sibling = topLeftChild.sibling( arow: r, acolumn: c );
430 QVERIFY( index == sibling );
431 }
432 if (r == topLeftChild.row()) {
433 const QModelIndex sibling = topLeftChild.siblingAtColumn( acolumn: c );
434 QVERIFY( index == sibling );
435 }
436 if (c == topLeftChild.column()) {
437 const QModelIndex sibling = topLeftChild.siblingAtRow( arow: r );
438 QVERIFY( index == sibling );
439 }
440
441 // Some basic checking on the index that is returned
442 QCOMPARE(index.model(), currentModel);
443 QCOMPARE(index.row(), r);
444 QCOMPARE(index.column(), c);
445 QCOMPARE(currentModel->data(index, Qt::DisplayRole).isValid(), true);
446
447 // If the next test fails here is some somewhat useful debug you play with.
448 /*
449 if (currentModel->parent(index) != parent) {
450 qDebug() << r << c << currentDepth << currentModel->data(index).toString()
451 << currentModel->data(parent).toString();
452 qDebug() << index << parent << currentModel->parent(index);
453 QTreeView view;
454 view.setModel(currentModel);
455 view.show();
456 QTest::qWait(9000);
457 }*/
458 QCOMPARE(currentModel->parent(index), parent);
459
460 // recursivly go down
461 if (currentModel->hasChildren(parent: index) && currentDepth < 5) {
462 checkChildren(currentModel, parent: index, currentDepth: ++currentDepth);
463 // Because this is recursive we will return at the first failure rather then
464 // reporting it over and over
465 if (QTest::currentTestFailed())
466 return;
467 }
468
469 // make sure that after testing the children that the index pointer doesn't change.
470 QModelIndex newerIndex = currentModel->index(row: r, column: c, parent);
471 QCOMPARE(index, newerIndex);
472 }
473 }
474}
475
476/*!
477 Tests model's implimentation of QAbstractItemModel::parent()
478 */
479void tst_QItemModel::parent()
480{
481 QFETCH(QString, modelType);
482 currentModel = testModels->createModel(modelType);
483 QVERIFY(currentModel);
484
485 // Make sure the model won't crash and will return an invalid QModelIndex
486 // when asked for the parent of an invalid index.
487 QCOMPARE(currentModel->parent(QModelIndex()), QModelIndex());
488
489 QFETCH(bool, isEmpty);
490 if (isEmpty)
491 return;
492
493 // Common error test #1, make sure that a top level index has a parent
494 // that is a invalid QModelIndex. You model
495 QModelIndex topIndex = currentModel->index(row: 0, column: 0, parent: QModelIndex());
496 QCOMPARE(currentModel->parent(topIndex), QModelIndex());
497
498 // Common error test #2, make sure that a second level index has a parent
499 // that is the top level index.
500 if (currentModel->rowCount(parent: topIndex) > 0) {
501 QModelIndex childIndex = currentModel->index(row: 0, column: 0, parent: topIndex);
502 QCOMPARE(currentModel->parent(childIndex), topIndex);
503 }
504
505 // Common error test #3, the second column has the same children
506 // as the first column in a row.
507 QModelIndex topIndex1 = currentModel->index(row: 0, column: 1, parent: QModelIndex());
508 if (currentModel->rowCount(parent: topIndex1) > 0) {
509 QModelIndex childIndex = currentModel->index(row: 0, column: 0, parent: topIndex);
510 QModelIndex childIndex1 = currentModel->index(row: 0, column: 0, parent: topIndex1);
511 QVERIFY(childIndex != childIndex1);
512 }
513
514 // Full test, walk 10 levels deap through the model making sure that all
515 // parents's children correctly specify their parent
516 QModelIndex top = QModelIndex();
517 checkChildren(currentModel, parent: top);
518}
519
520
521void tst_QItemModel::data_data()
522{
523 setupWithNoTestData();
524}
525
526/*!
527 Tests model's implimentation of QAbstractItemModel::data()
528 */
529void tst_QItemModel::data()
530{
531 QFETCH(QString, modelType);
532 currentModel = testModels->createModel(modelType);
533 QVERIFY(currentModel);
534
535 // Invalid index should return an invalid qvariant
536 QVERIFY(!currentModel->data(QModelIndex()).isValid());
537
538 QFETCH(bool, isEmpty);
539 if (isEmpty)
540 return;
541
542 // A valid index should have a valid qvariant data
543 QVERIFY(currentModel->index(0,0).isValid());
544
545 // General Purpose roles
546 QVariant variant = currentModel->data(index: currentModel->index(row: 0,column: 0), role: Qt::ToolTipRole);
547 if (variant.isValid()) {
548 QVERIFY(variant.canConvert<QString>());
549 }
550 variant = currentModel->data(index: currentModel->index(row: 0,column: 0), role: Qt::StatusTipRole);
551 if (variant.isValid()) {
552 QVERIFY(variant.canConvert<QString>());
553 }
554 variant = currentModel->data(index: currentModel->index(row: 0,column: 0), role: Qt::WhatsThisRole);
555 if (variant.isValid()) {
556 QVERIFY(variant.canConvert<QString>());
557 }
558
559 variant = currentModel->data(index: currentModel->index(row: 0,column: 0), role: Qt::SizeHintRole);
560 if (variant.isValid()) {
561 QVERIFY(variant.canConvert<QSize>());
562 }
563
564
565 // Appearance roles
566 QVariant fontVariant = currentModel->data(index: currentModel->index(row: 0,column: 0), role: Qt::FontRole);
567 if (fontVariant.isValid()) {
568 QVERIFY(fontVariant.canConvert<QFont>());
569 }
570
571 QVariant textAlignmentVariant = currentModel->data(index: currentModel->index(row: 0,column: 0), role: Qt::TextAlignmentRole);
572 if (textAlignmentVariant.isValid()) {
573 int alignment = textAlignmentVariant.toInt();
574 QVERIFY(alignment == Qt::AlignLeft ||
575 alignment == Qt::AlignRight ||
576 alignment == Qt::AlignHCenter ||
577 alignment == Qt::AlignJustify);
578 }
579
580 QVariant colorVariant = currentModel->data(index: currentModel->index(row: 0,column: 0), role: Qt::BackgroundRole);
581 if (colorVariant.isValid()) {
582 QVERIFY(colorVariant.canConvert<QColor>());
583 }
584
585 colorVariant = currentModel->data(index: currentModel->index(row: 0,column: 0), role: Qt::ForegroundRole);
586 if (colorVariant.isValid()) {
587 QVERIFY(colorVariant.canConvert<QColor>());
588 }
589
590 QVariant checkStateVariant = currentModel->data(index: currentModel->index(row: 0,column: 0), role: Qt::CheckStateRole);
591 if (checkStateVariant.isValid()) {
592 int state = checkStateVariant.toInt();
593 QVERIFY(state == Qt::Unchecked ||
594 state == Qt::PartiallyChecked ||
595 state == Qt::Checked);
596 }
597}
598
599void tst_QItemModel::setData_data()
600{
601 setupWithNoTestData();
602}
603
604/*!
605 Tests model's implimentation of QAbstractItemModel::setData()
606 */
607void tst_QItemModel::setData()
608{
609 QFETCH(QString, modelType);
610 currentModel = testModels->createModel(modelType);
611 QVERIFY(currentModel);
612 QSignalSpy spy(currentModel, &QAbstractItemModel::dataChanged);
613 QVERIFY(spy.isValid());
614 QCOMPARE(spy.count(), 0);
615
616 QFETCH(bool, isEmpty);
617 if (isEmpty)
618 return;
619
620 QFETCH(bool, readOnly);
621 if (readOnly)
622 return;
623
624 // Populate the test area so we can chage stuff. See: cleanup()
625 QModelIndex topIndex = testModels->populateTestArea(model: currentModel);
626 QVERIFY(currentModel->hasChildren(topIndex));
627 QModelIndex index = currentModel->index(row: 0, column: 0, parent: topIndex);
628 QVERIFY(index.isValid());
629
630 spy.clear();
631 QString text = "Index private pointers should always be the same";
632 QCOMPARE(currentModel->setData(index, text, Qt::EditRole), true);
633 QCOMPARE(index.data(Qt::EditRole).toString(), text);
634
635 // Changing the text shouldn't change the layout, parent, pointer etc.
636 QModelIndex changedIndex = currentModel->index(row: 0, column: 0, parent: topIndex);
637 QCOMPARE(changedIndex, index);
638 QCOMPARE(spy.count(), 1);
639}
640
641void tst_QItemModel::setHeaderData_data()
642{
643 setupWithNoTestData();
644}
645
646/*!
647 Tests model's implimentation of QAbstractItemModel::setHeaderData()
648 */
649void tst_QItemModel::setHeaderData()
650{
651 QFETCH(QString, modelType);
652 currentModel = testModels->createModel(modelType);
653 QVERIFY(currentModel);
654
655 QCOMPARE(currentModel->setHeaderData(-1, Qt::Horizontal, QVariant()), false);
656 QCOMPARE(currentModel->setHeaderData(-1, Qt::Vertical, QVariant()), false);
657
658 QFETCH(bool, isEmpty);
659 if (isEmpty)
660 return;
661
662 QFETCH(bool, readOnly);
663 if (readOnly)
664 return;
665
666 // Populate the test area so we can change stuff. See: cleanup()
667 QModelIndex topIndex = testModels->populateTestArea(model: currentModel);
668 QVERIFY(currentModel->hasChildren(topIndex));
669 QModelIndex index = currentModel->index(row: 0, column: 0, parent: topIndex);
670 QVERIFY(index.isValid());
671
672 qRegisterMetaType<Qt::Orientation>(typeName: "Qt::Orientation");
673 QSignalSpy spy(currentModel, &QAbstractItemModel::headerDataChanged);
674 QVERIFY(spy.isValid());
675
676 QString text = "Index private pointers should always be the same";
677 int signalCount = 0;
678 for (int i = 0; i < 4; ++i){
679 if(currentModel->setHeaderData(section: i, orientation: Qt::Horizontal, value: text)) {
680 QCOMPARE(currentModel->headerData(i, Qt::Horizontal).toString(), text);
681 ++signalCount;
682 }
683 if(currentModel->setHeaderData(section: i, orientation: Qt::Vertical, value: text)) {
684 QCOMPARE(currentModel->headerData(i, Qt::Vertical).toString(), text);
685 ++signalCount;
686 }
687 }
688 QCOMPARE(spy.count(), signalCount);
689}
690
691void tst_QItemModel::sort_data()
692{
693 setupWithNoTestData();
694}
695
696/*!
697 Tests model's implimentation of QAbstractItemModel::sort()
698 */
699void tst_QItemModel::sort()
700{
701 QFETCH(QString, modelType);
702 currentModel = testModels->createModel(modelType);
703 QVERIFY(currentModel);
704
705 QFETCH(bool, isEmpty);
706 if (isEmpty)
707 return;
708
709 // Populate the test area so we can chage stuff. See: cleanup()
710 QPersistentModelIndex topIndex = testModels->populateTestArea(model: currentModel);
711 QVERIFY(currentModel->hasChildren(topIndex));
712 QModelIndex index = currentModel->index(row: 0, column: 0, parent: topIndex);
713 QVERIFY(index.isValid());
714 QSignalSpy spy(currentModel, &QAbstractItemModel::layoutChanged);
715 QVERIFY(spy.isValid());
716 for (int i=-1; i < 10; ++i){
717 currentModel->sort(column: i);
718 if (index != currentModel->index(row: 0, column: 0, parent: topIndex)){
719 QVERIFY(spy.count() > 0);
720 index = currentModel->index(row: 0, column: 0, parent: topIndex);
721 spy.clear();
722 }
723 }
724 currentModel->sort(column: 99999);
725}
726
727/*!
728 Tests model's implimentation of QAbstractItemModel::removeRow() and QAbstractItemModel::removeColumn()
729 */
730#define START 0
731#define MIDDLE 6
732#define END -1
733#define MANY 9
734#define ALL -1
735#define NOSIGNALS 0
736#define DEFAULTCOUNT 1
737#define DNS 1 // DefaultNumberOfSignals
738#define RECURSIVE true
739#define SUCCESS true
740#define FAIL false
741void tst_QItemModel::remove_data()
742{
743 ModelsToTest modelsToTest;
744 QTest::addColumn<QString>(name: "modelType");
745 QTest::addColumn<bool>(name: "readOnly");
746 QTest::addColumn<bool>(name: "isEmpty");
747
748 QTest::addColumn<int>(name: "start");
749 QTest::addColumn<int>(name: "count");
750
751 QTest::addColumn<int>(name: "numberOfRowsAboutToBeRemovedSignals");
752 QTest::addColumn<int>(name: "numberOfColumnsAboutToBeRemovedSignals");
753 QTest::addColumn<int>(name: "numberOfRowsRemovedSignals");
754 QTest::addColumn<int>(name: "numberOfColumnsRemovedSignals");
755
756 QTest::addColumn<bool>(name: "recursive");
757 QTest::addColumn<int>(name: "recursiveRow");
758 QTest::addColumn<int>(name: "recursiveCount");
759
760 QTest::addColumn<bool>(name: "shouldSucceed");
761
762#define makeTestRow(testName, start, count, sar, srr, sac, src, r, rr, rc, s) \
763 QTest::newRow((t.modelType + testName).toLatin1().data()) << t.modelType << readOnly << isEmpty << \
764 start << count << \
765 sar << srr << sac << src << \
766 r << rr << rc << \
767 s;
768
769 for (int i = 0; i < modelsToTest.tests.size(); ++i) {
770 ModelsToTest::test t = modelsToTest.tests.at(i);
771 QString name = t.modelType;
772 bool readOnly = (t.read == ModelsToTest::ReadOnly);
773 bool isEmpty = (t.contains == ModelsToTest::Empty);
774
775 // half these
776 makeTestRow(":one at the start", START, DEFAULTCOUNT, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
777 makeTestRow(":one at the middle", MIDDLE, DEFAULTCOUNT, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
778 makeTestRow(":one at the end", END, DEFAULTCOUNT, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
779
780 makeTestRow(":many at the start", START, MANY, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
781 makeTestRow(":many at the middle", MIDDLE, MANY, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
782 makeTestRow(":many at the end", END, MANY, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
783
784 makeTestRow(":remove all", START, ALL, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
785
786 makeTestRow(":none at the start", START, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
787 makeTestRow(":none at the middle", MIDDLE, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
788 makeTestRow(":none at the end", END, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
789
790 makeTestRow(":invalid start, valid count 1", -99, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
791 makeTestRow(":invalid start, valid count 2", 9999, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
792 makeTestRow(":invalid start, valid count 3", -99, 1, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
793 makeTestRow(":invalid start, valid count 4", 9999, 1, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
794 makeTestRow(":invalid start, valid count 5", -99, MANY, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
795 makeTestRow(":invalid start, valid count 6", 9999, MANY, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
796
797 makeTestRow(":valid start, invalid count 1", START, -2, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
798 makeTestRow(":valid start, invalid count 2", MIDDLE, -2, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
799 makeTestRow(":valid start, invalid count 3", END, -2, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
800 makeTestRow(":valid start, invalid count 4", START, 9999, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
801 makeTestRow(":valid start, invalid count 5", MIDDLE, 9999, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
802 makeTestRow(":valid start, invalid count 6", END, 9999, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
803
804 // Recursive remove's might assert, haven't decided yet...
805 //makeTestRow(":one at the start recursivly", START, DEFAULTCOUNT, 2, DNS, 2, DNS, RECURSIVE, START, DEFAULTCOUNT, FAIL);
806 //makeTestRow(":one at the middle recursivly", MIDDLE, DEFAULTCOUNT, 2, DNS, 2, DNS, RECURSIVE, START, DEFAULTCOUNT, SUCCESS);
807 //makeTestRow(":one at the end recursivly", END, DEFAULTCOUNT, 2, DNS, 2, DNS, RECURSIVE, START, DEFAULTCOUNT, SUCCESS);
808 }
809}
810
811void tst_QItemModel::remove()
812{
813 QFETCH(QString, modelType);
814
815 currentModel = testModels->createModel(modelType);
816 QVERIFY(currentModel);
817
818 QFETCH(bool, readOnly);
819 if (readOnly)
820 return;
821
822 QFETCH(int, start);
823 QFETCH(int, count);
824
825 QFETCH(bool, recursive);
826 removeRecursively = recursive;
827
828/*!
829 Removes count number of rows starting at start
830 if count is -1 it removes all rows
831 if start is -1 then it starts at the last row - count
832 */
833 QFETCH(bool, shouldSucceed);
834
835 // Populate the test area so we can remove something. See: cleanup()
836 // parentOfRemoved is stored so that the slots can make sure parentOfRemoved is the index that is emitted.
837 parentOfRemoved = testModels->populateTestArea(model: currentModel);
838
839 if (count == -1)
840 count = currentModel->rowCount(parent: parentOfRemoved);
841 if (start == -1)
842 start = currentModel->rowCount(parent: parentOfRemoved)-count;
843
844 if (currentModel->rowCount(parent: parentOfRemoved) == 0 ||
845 currentModel->columnCount(parent: parentOfRemoved) == 0) {
846 qWarning() << "model test area doesn't have any rows or columns, can't fully test remove(). Skipping";
847 return;
848 }
849
850 // When a row or column is removed there should be two signals.
851 // Watch to make sure they are emitted and get the row/column count when they do get emitted by connecting them to a slot
852 QSignalSpy columnsAboutToBeRemovedSpy(currentModel, &QAbstractItemModel::columnsAboutToBeRemoved);
853 QSignalSpy rowsAboutToBeRemovedSpy(currentModel, &QAbstractItemModel::rowsAboutToBeRemoved);
854 QSignalSpy columnsRemovedSpy(currentModel, &QAbstractItemModel::columnsRemoved);
855 QSignalSpy rowsRemovedSpy(currentModel, &QAbstractItemModel::rowsRemoved);
856 QSignalSpy modelResetSpy(currentModel, &QAbstractItemModel::modelReset);
857 QSignalSpy modelLayoutChangedSpy(currentModel, &QAbstractItemModel::layoutChanged);
858
859 QVERIFY(columnsAboutToBeRemovedSpy.isValid());
860 QVERIFY(rowsAboutToBeRemovedSpy.isValid());
861 QVERIFY(columnsRemovedSpy.isValid());
862 QVERIFY(rowsRemovedSpy.isValid());
863 QVERIFY(modelResetSpy.isValid());
864 QVERIFY(modelLayoutChangedSpy.isValid());
865
866 QFETCH(int, numberOfRowsAboutToBeRemovedSignals);
867 QFETCH(int, numberOfColumnsAboutToBeRemovedSignals);
868 QFETCH(int, numberOfRowsRemovedSignals);
869 QFETCH(int, numberOfColumnsRemovedSignals);
870
871 //
872 // test removeRow()
873 //
874 connect(sender: currentModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
875 receiver: this, SLOT(slot_rowsAboutToRemove(QModelIndex)));
876 connect(sender: currentModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
877 receiver: this, SLOT(slot_rowsRemoved(QModelIndex)));
878 int beforeRemoveRowCount = currentModel->rowCount(parent: parentOfRemoved);
879 QPersistentModelIndex dyingIndex = currentModel->index(row: start + count + 1, column: 0, parent: parentOfRemoved);
880 QCOMPARE(currentModel->removeRows(start, count, parentOfRemoved), shouldSucceed);
881 currentModel->submit();
882 if (shouldSucceed && dyingIndex.isValid())
883 QCOMPARE(dyingIndex.row(), start + 1);
884
885 if (rowsAboutToBeRemovedSpy.count() > 0){
886 QList<QVariant> arguments = rowsAboutToBeRemovedSpy.at(i: 0);
887 QModelIndex parent = (qvariant_cast<QModelIndex>(v: arguments.at(i: 0)));
888 int first = arguments.at(i: 1).toInt();
889 int last = arguments.at(i: 2).toInt();
890 QCOMPARE(first, start);
891 QCOMPARE(last, start + count - 1);
892 QVERIFY(parentOfRemoved == parent);
893 }
894
895 if (rowsRemovedSpy.count() > 0){
896 QList<QVariant> arguments = rowsRemovedSpy.at(i: 0);
897 QModelIndex parent = (qvariant_cast<QModelIndex>(v: arguments.at(i: 0)));
898 int first = arguments.at(i: 1).toInt();
899 int last = arguments.at(i: 2).toInt();
900 QCOMPARE(first, start);
901 QCOMPARE(last, start + count - 1);
902 QVERIFY(parentOfRemoved == parent);
903 }
904
905 // Only the row signals should have been emitted
906 if (modelResetSpy.count() >= 1 || modelLayoutChangedSpy.count() >=1 ){
907 QCOMPARE(columnsAboutToBeRemovedSpy.count(), 0);
908 QCOMPARE(rowsAboutToBeRemovedSpy.count(), 0);
909 QCOMPARE(columnsRemovedSpy.count(), 0);
910 QCOMPARE(rowsRemovedSpy.count(), 0);
911 }
912 else {
913 QCOMPARE(columnsAboutToBeRemovedSpy.count(), 0);
914 QCOMPARE(rowsAboutToBeRemovedSpy.count(), numberOfRowsAboutToBeRemovedSignals);
915 QCOMPARE(columnsRemovedSpy.count(), 0);
916 QCOMPARE(rowsRemovedSpy.count(), numberOfRowsRemovedSignals);
917 }
918
919 // The row count should only change *after* rowsAboutToBeRemoved has been emitted
920 if (shouldSucceed) {
921 if (modelResetSpy.count() == 0 && modelLayoutChangedSpy.count() == 0){
922 QCOMPARE(afterAboutToRemoveRowCount, beforeRemoveRowCount);
923 QCOMPARE(afterRemoveRowCount, beforeRemoveRowCount-count-(numberOfRowsRemovedSignals-1));
924 }
925 if (modelResetSpy.count() == 0 )
926 QCOMPARE(currentModel->rowCount(parentOfRemoved), beforeRemoveRowCount-count-(numberOfRowsRemovedSignals-1));
927 }
928 else {
929 if (recursive)
930 QCOMPARE(currentModel->rowCount(parentOfRemoved), beforeRemoveRowCount-1);
931 else
932 QCOMPARE(currentModel->rowCount(parentOfRemoved), beforeRemoveRowCount);
933
934 }
935 disconnect(sender: currentModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
936 receiver: this, SLOT(slot_rowsAboutToRemove(QModelIndex)));
937 disconnect(sender: currentModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
938 receiver: this, SLOT(slot_rowsRemoved(QModelIndex)));
939 modelResetSpy.clear();
940 QCOMPARE(modelResetSpy.count(), 0);
941
942 //
943 // Test remove column
944 //
945 connect(sender: currentModel, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
946 receiver: this, SLOT(slot_columnsAboutToRemove(QModelIndex)));
947 connect(sender: currentModel, SIGNAL(columnsRemoved(QModelIndex,int,int)),
948 receiver: this, SLOT(slot_columnsRemoved(QModelIndex)));
949 int beforeRemoveColumnCount = currentModel->columnCount(parent: parentOfRemoved);
950
951 // Some models don't let you remove the column, only row
952 if (currentModel->removeColumns(column: start, count, parent: parentOfRemoved)) {
953 currentModel->submit();
954 // Didn't reset the rows, so they should still be at the same value
955 if (modelResetSpy.count() >= 1 || modelLayoutChangedSpy.count() >= 1){
956 QCOMPARE(columnsAboutToBeRemovedSpy.count(), 0);
957 //QCOMPARE(rowsAboutToBeRemovedSpy.count(), numberOfRowsAboutToBeRemovedSignals);
958 QCOMPARE(columnsRemovedSpy.count(), 0);
959 //QCOMPARE(rowsRemovedSpy.count(), numberOfRowsRemovedSignals);
960 }
961 else {
962 QCOMPARE(columnsAboutToBeRemovedSpy.count(), numberOfColumnsAboutToBeRemovedSignals);
963 QCOMPARE(rowsAboutToBeRemovedSpy.count(), numberOfRowsAboutToBeRemovedSignals);
964 QCOMPARE(columnsRemovedSpy.count(), numberOfColumnsRemovedSignals);
965 QCOMPARE(rowsRemovedSpy.count(), numberOfRowsRemovedSignals);
966 }
967
968 // The column count should only change *after* rowsAboutToBeRemoved has been emitted
969 if (shouldSucceed) {
970 if (modelResetSpy.count() == 0 && modelLayoutChangedSpy.count() == 0){
971 QCOMPARE(afterAboutToRemoveColumnCount, beforeRemoveColumnCount);
972 QCOMPARE(afterRemoveColumnCount, beforeRemoveColumnCount-count-(numberOfColumnsRemovedSignals-1));
973 }
974 if (modelResetSpy.count() == 0)
975 QCOMPARE(currentModel->columnCount(parentOfRemoved), beforeRemoveColumnCount-count-(numberOfColumnsRemovedSignals-1));
976 }
977 else
978 QCOMPARE(currentModel->rowCount(parentOfRemoved), beforeRemoveRowCount);
979 }
980 disconnect(sender: currentModel, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
981 receiver: this, SLOT(slot_columnsAboutToRemove(QModelIndex)));
982 disconnect(sender: currentModel, SIGNAL(columnsRemoved(QModelIndex,int,int)),
983 receiver: this, SLOT(slot_columnsRemoved(QModelIndex)));
984
985 if (columnsAboutToBeRemovedSpy.count() > 0){
986 QList<QVariant> arguments = columnsAboutToBeRemovedSpy.at(i: 0);
987 QModelIndex parent = (qvariant_cast<QModelIndex>(v: arguments.at(i: 0)));
988 int first = arguments.at(i: 1).toInt();
989 int last = arguments.at(i: 2).toInt();
990 QCOMPARE(first, start);
991 QCOMPARE(last, start + count - 1);
992 QVERIFY(parentOfRemoved == parent);
993 }
994
995 if (columnsRemovedSpy.count() > 0){
996 QList<QVariant> arguments = columnsRemovedSpy.at(i: 0);
997 QModelIndex parent = (qvariant_cast<QModelIndex>(v: arguments.at(i: 0)));
998 int first = arguments.at(i: 1).toInt();
999 int last = arguments.at(i: 2).toInt();
1000 QCOMPARE(first, start);
1001 QCOMPARE(last, start + count - 1);
1002 QVERIFY(parentOfRemoved == parent);
1003 }
1004
1005 // Cleanup the test area because remove::() is called multiple times in a test
1006 testModels->cleanupTestArea(model: currentModel);
1007}
1008
1009/*!
1010 Developers like to use the slots to then *do* something on the model so it needs to be
1011 in a working state.
1012 */
1013void verifyState(QAbstractItemModel *currentModel) {
1014 // Make sure the model isn't confused right now and still knows what is root
1015 if (currentModel->hasChildren()) {
1016 QCOMPARE(currentModel->hasIndex(0, 0), true);
1017 QModelIndex index = currentModel->index(row: 0,column: 0);
1018 QCOMPARE(index.isValid(), true);
1019 QCOMPARE(currentModel->parent(index).isValid(), false);
1020 } else {
1021 QModelIndex index = currentModel->index(row: 0,column: 0);
1022 QCOMPARE(index.isValid(), false);
1023 }
1024}
1025
1026void tst_QItemModel::slot_rowsAboutToRemove(const QModelIndex &parent)
1027{
1028 QVERIFY(parentOfRemoved == parent);
1029 afterAboutToRemoveRowCount = currentModel->rowCount(parent);
1030 // hasChildren() should still work
1031 if (afterAboutToRemoveRowCount > 0)
1032 QCOMPARE(currentModel->hasChildren(parent), true);
1033 else
1034 QCOMPARE(currentModel->hasChildren(parent), false);
1035
1036 verifyState(currentModel);
1037
1038 // This does happen
1039 if (removeRecursively) {
1040 QFETCH(int, recursiveRow);
1041 QFETCH(int, recursiveCount);
1042 removeRecursively = false;
1043 QCOMPARE(currentModel->removeRows(recursiveRow, recursiveCount, parent), true);
1044 }
1045}
1046
1047void tst_QItemModel::slot_rowsRemoved(const QModelIndex &parent)
1048{
1049 QVERIFY(parentOfRemoved == parent);
1050 afterRemoveRowCount = currentModel->rowCount(parent);
1051 if (afterRemoveRowCount > 0)
1052 QCOMPARE(currentModel->hasChildren(parent), true);
1053 else
1054 QCOMPARE(currentModel->hasChildren(parent), false);
1055
1056 verifyState(currentModel);
1057}
1058
1059void tst_QItemModel::slot_columnsAboutToRemove(const QModelIndex &parent)
1060{
1061 QVERIFY(parentOfRemoved == parent);
1062 afterAboutToRemoveColumnCount = currentModel->columnCount(parent);
1063 // hasChildren() should still work
1064 if (afterAboutToRemoveColumnCount > 0 && currentModel->rowCount(parent) > 0)
1065 QCOMPARE(currentModel->hasChildren(parent), true);
1066 else
1067 QCOMPARE(currentModel->hasChildren(parent), false);
1068
1069 verifyState(currentModel);
1070}
1071
1072void tst_QItemModel::slot_columnsRemoved(const QModelIndex &parent)
1073{
1074 QVERIFY(parentOfRemoved == parent);
1075 afterRemoveColumnCount = currentModel->columnCount(parent);
1076 if (afterRemoveColumnCount > 0)
1077 QCOMPARE(currentModel->hasChildren(parent), true);
1078 else
1079 QCOMPARE(currentModel->hasChildren(parent), false);
1080
1081 verifyState(currentModel);
1082}
1083
1084/*!
1085 Tests the model's insertRow/Column()
1086 */
1087void tst_QItemModel::insert_data()
1088{
1089 ModelsToTest modelsToTest;
1090 QTest::addColumn<QString>(name: "modelType");
1091 QTest::addColumn<bool>(name: "readOnly");
1092 QTest::addColumn<bool>(name: "isEmpty");
1093
1094 QTest::addColumn<int>(name: "start");
1095 QTest::addColumn<int>(name: "count");
1096
1097 QTest::addColumn<int>(name: "numberOfRowsAboutToBeInsertedSignals");
1098 QTest::addColumn<int>(name: "numberOfColumnsAboutToBeInsertedSignals");
1099 QTest::addColumn<int>(name: "numberOfRowsInsertedSignals");
1100 QTest::addColumn<int>(name: "numberOfColumnsInsertedSignals");
1101
1102 QTest::addColumn<bool>(name: "recursive");
1103 QTest::addColumn<int>(name: "recursiveRow");
1104 QTest::addColumn<int>(name: "recursiveCount");
1105
1106 QTest::addColumn<bool>(name: "shouldSucceed");
1107
1108#define makeTestRow(testName, start, count, sar, srr, sac, src, r, rr, rc, s) \
1109 QTest::newRow((t.modelType + testName).toLatin1().data()) << t.modelType << readOnly << isEmpty << \
1110 start << count << \
1111 sar << srr << sac << src << \
1112 r << rr << rc << \
1113 s;
1114
1115 for (int i = 0; i < modelsToTest.tests.size(); ++i) {
1116 ModelsToTest::test t = modelsToTest.tests.at(i);
1117 QString name = t.modelType;
1118 bool readOnly = (t.read == ModelsToTest::ReadOnly);
1119 bool isEmpty = (t.contains == ModelsToTest::Empty);
1120
1121 // half these
1122 makeTestRow(":one at the start", START, DEFAULTCOUNT, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
1123 makeTestRow(":one at the middle", MIDDLE, DEFAULTCOUNT, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
1124 makeTestRow(":one at the end", END, DEFAULTCOUNT, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
1125
1126 makeTestRow(":many at the start", START, MANY, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
1127 makeTestRow(":many at the middle", MIDDLE, MANY, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
1128 makeTestRow(":many at the end", END, MANY, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
1129
1130 makeTestRow(":add row count", START, ALL, DNS, DNS, DNS, DNS, !RECURSIVE, 0, 0, SUCCESS);
1131
1132 makeTestRow(":none at the start", START, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
1133 makeTestRow(":none at the middle", MIDDLE, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
1134 makeTestRow(":none at the end", END, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
1135
1136 makeTestRow(":invalid start, valid count 1", -99, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
1137 makeTestRow(":invalid start, valid count 2", 9999, 0, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
1138 makeTestRow(":invalid start, valid count 3", -99, 1, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
1139 makeTestRow(":invalid start, valid count 4", 9999, 1, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
1140 makeTestRow(":invalid start, valid count 5", -99, MANY, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
1141 makeTestRow(":invalid start, valid count 6", 9999, MANY, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
1142
1143 makeTestRow(":valid start, invalid count 1", START, -2, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
1144 makeTestRow(":valid start, invalid count 2", MIDDLE, -2, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
1145 makeTestRow(":valid start, invalid count 3", END, -2, NOSIGNALS, NOSIGNALS, NOSIGNALS, NOSIGNALS, !RECURSIVE, 0, 0, FAIL);
1146
1147 // Recursive insert's might assert, haven't decided yet...
1148 //makeTestRow(":one at the start recursivly", START, DEFAULTCOUNT, 2, DNS, 2, DNS, RECURSIVE, START, DEFAULTCOUNT, FAIL);
1149 //makeTestRow(":one at the middle recursivly", MIDDLE, DEFAULTCOUNT, 2, DNS, 2, DNS, RECURSIVE, START, DEFAULTCOUNT, SUCCESS);
1150 //makeTestRow(":one at the end recursivly", END, DEFAULTCOUNT, 2, DNS, 2, DNS, RECURSIVE, START, DEFAULTCOUNT, SUCCESS);
1151 }
1152}
1153
1154void tst_QItemModel::insert()
1155{
1156 QFETCH(QString, modelType);
1157 currentModel = testModels->createModel(modelType);
1158 QVERIFY(currentModel);
1159
1160 QFETCH(bool, readOnly);
1161 if (readOnly)
1162 return;
1163
1164 QFETCH(int, start);
1165 QFETCH(int, count);
1166
1167 QFETCH(bool, recursive);
1168 insertRecursively = recursive;
1169
1170/*!
1171 Inserts count number of rows starting at start
1172 if count is -1 it inserts all rows
1173 if start is -1 then it starts at the last row - count
1174 */
1175 QFETCH(bool, shouldSucceed);
1176
1177 // Populate the test area so we can insert something. See: cleanup()
1178 // parentOfInserted is stored so that the slots can make sure parentOfInserted is the index that is emitted.
1179 parentOfInserted = testModels->populateTestArea(model: currentModel);
1180
1181 if (count == -1)
1182 count = currentModel->rowCount(parent: parentOfInserted);
1183 if (start == -1)
1184 start = currentModel->rowCount(parent: parentOfInserted)-count;
1185
1186 if (currentModel->rowCount(parent: parentOfInserted) == 0 ||
1187 currentModel->columnCount(parent: parentOfInserted) == 0) {
1188 qWarning() << "model test area doesn't have any rows, can't fully test insert(). Skipping";
1189 return;
1190 }
1191
1192 // When a row or column is inserted there should be two signals.
1193 // Watch to make sure they are emitted and get the row/column count when they do get emitted by connecting them to a slot
1194 QSignalSpy columnsAboutToBeInsertedSpy(currentModel, &QAbstractItemModel::columnsAboutToBeInserted);
1195 QSignalSpy rowsAboutToBeInsertedSpy(currentModel, &QAbstractItemModel::rowsAboutToBeInserted);
1196 QSignalSpy columnsInsertedSpy(currentModel, &QAbstractItemModel::columnsInserted);
1197 QSignalSpy rowsInsertedSpy(currentModel, &QAbstractItemModel::rowsInserted);
1198 QSignalSpy modelResetSpy(currentModel, &QAbstractItemModel::modelReset);
1199 QSignalSpy modelLayoutChangedSpy(currentModel, &QAbstractItemModel::layoutChanged);
1200
1201 QVERIFY(columnsAboutToBeInsertedSpy.isValid());
1202 QVERIFY(rowsAboutToBeInsertedSpy.isValid());
1203 QVERIFY(columnsInsertedSpy.isValid());
1204 QVERIFY(rowsInsertedSpy.isValid());
1205 QVERIFY(modelResetSpy.isValid());
1206 QVERIFY(modelLayoutChangedSpy.isValid());
1207
1208 QFETCH(int, numberOfRowsAboutToBeInsertedSignals);
1209 QFETCH(int, numberOfColumnsAboutToBeInsertedSignals);
1210 QFETCH(int, numberOfRowsInsertedSignals);
1211 QFETCH(int, numberOfColumnsInsertedSignals);
1212
1213 //
1214 // test insertRow()
1215 //
1216 connect(sender: currentModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
1217 receiver: this, SLOT(slot_rowsAboutToInserted(QModelIndex)));
1218 connect(sender: currentModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
1219 receiver: this, SLOT(slot_rowsInserted(QModelIndex)));
1220 int beforeInsertRowCount = currentModel->rowCount(parent: parentOfInserted);
1221 QCOMPARE(currentModel->insertRows(start, count, parentOfInserted), shouldSucceed);
1222 currentModel->submit();
1223
1224 if (rowsAboutToBeInsertedSpy.count() > 0){
1225 QList<QVariant> arguments = rowsAboutToBeInsertedSpy.at(i: 0);
1226 QModelIndex parent = (qvariant_cast<QModelIndex>(v: arguments.at(i: 0)));
1227 int first = arguments.at(i: 1).toInt();
1228 int last = arguments.at(i: 2).toInt();
1229 QCOMPARE(first, start);
1230 QCOMPARE(last, start + count - 1);
1231 QVERIFY(parentOfInserted == parent);
1232 }
1233
1234 if (rowsInsertedSpy.count() > 0){
1235 QList<QVariant> arguments = rowsInsertedSpy.at(i: 0);
1236 QModelIndex parent = (qvariant_cast<QModelIndex>(v: arguments.at(i: 0)));
1237 int first = arguments.at(i: 1).toInt();
1238 int last = arguments.at(i: 2).toInt();
1239 QCOMPARE(first, start);
1240 QCOMPARE(last, start + count - 1);
1241 QVERIFY(parentOfInserted == parent);
1242 }
1243
1244 // Only the row signals should have been emitted
1245 if (modelResetSpy.count() >= 1 || modelLayoutChangedSpy.count() >= 1) {
1246 QCOMPARE(columnsAboutToBeInsertedSpy.count(), 0);
1247 QCOMPARE(rowsAboutToBeInsertedSpy.count(), 0);
1248 QCOMPARE(columnsInsertedSpy.count(), 0);
1249 QCOMPARE(rowsInsertedSpy.count(), 0);
1250 }
1251 else {
1252 QCOMPARE(columnsAboutToBeInsertedSpy.count(), 0);
1253 QCOMPARE(rowsAboutToBeInsertedSpy.count(), numberOfRowsAboutToBeInsertedSignals);
1254 QCOMPARE(columnsInsertedSpy.count(), 0);
1255 QCOMPARE(rowsInsertedSpy.count(), numberOfRowsInsertedSignals);
1256 }
1257 // The row count should only change *after* rowsAboutToBeInserted has been emitted
1258 if (shouldSucceed) {
1259 if (modelResetSpy.count() == 0 && modelLayoutChangedSpy.count() == 0) {
1260 QCOMPARE(afterAboutToInsertRowCount, beforeInsertRowCount);
1261 QCOMPARE(afterInsertRowCount, beforeInsertRowCount+count+(numberOfRowsInsertedSignals-1));
1262 }
1263 if (modelResetSpy.count() == 0)
1264 QCOMPARE(currentModel->rowCount(parentOfInserted), beforeInsertRowCount+count+(numberOfRowsInsertedSignals-1));
1265 }
1266 else {
1267 if (recursive)
1268 QCOMPARE(currentModel->rowCount(parentOfInserted), beforeInsertRowCount+1);
1269 else
1270 QCOMPARE(currentModel->rowCount(parentOfInserted), beforeInsertRowCount);
1271
1272 }
1273 disconnect(sender: currentModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
1274 receiver: this, SLOT(slot_rowsAboutToInserted(QModelIndex)));
1275 disconnect(sender: currentModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
1276 receiver: this, SLOT(slot_rowsInserted(QModelIndex)));
1277 modelResetSpy.clear();
1278
1279 //
1280 // Test insertColumn()
1281 //
1282 connect(sender: currentModel, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
1283 receiver: this, SLOT(slot_columnsAboutToInserted(QModelIndex)));
1284 connect(sender: currentModel, SIGNAL(columnsInserted(QModelIndex,int,int)),
1285 receiver: this, SLOT(slot_columnsInserted(QModelIndex)));
1286 int beforeInsertColumnCount = currentModel->columnCount(parent: parentOfInserted);
1287
1288 // Some models don't let you insert the column, only row
1289 if (currentModel->insertColumns(column: start, count, parent: parentOfInserted)) {
1290 currentModel->submit();
1291 if (modelResetSpy.count() >= 1 || modelLayoutChangedSpy.count() >= 1) {
1292 // Didn't reset the rows, so they should still be at the same value
1293 QCOMPARE(columnsAboutToBeInsertedSpy.count(), 0);
1294 //QCOMPARE(rowsAboutToBeInsertedSpy.count(), numberOfRowsAboutToBeInsertedSignals);
1295 QCOMPARE(columnsInsertedSpy.count(), 0);
1296 //QCOMPARE(rowsInsertedSpy.count(), numberOfRowsInsertedSignals);
1297 }
1298 else {
1299 // Didn't reset the rows, so they should still be at the same value
1300 QCOMPARE(columnsAboutToBeInsertedSpy.count(), numberOfColumnsAboutToBeInsertedSignals);
1301 QCOMPARE(rowsAboutToBeInsertedSpy.count(), numberOfRowsAboutToBeInsertedSignals);
1302 QCOMPARE(columnsInsertedSpy.count(), numberOfColumnsInsertedSignals);
1303 QCOMPARE(rowsInsertedSpy.count(), numberOfRowsInsertedSignals);
1304 }
1305 // The column count should only change *after* rowsAboutToBeInserted has been emitted
1306 if (shouldSucceed) {
1307 if (modelResetSpy.count() == 0 && modelLayoutChangedSpy.count() == 0) {
1308 QCOMPARE(afterAboutToInsertColumnCount, beforeInsertColumnCount);
1309 QCOMPARE(afterInsertColumnCount, beforeInsertColumnCount+count+(numberOfColumnsInsertedSignals-1));
1310 }
1311 if (modelResetSpy.count() == 0)
1312 QCOMPARE(currentModel->columnCount(parentOfInserted), beforeInsertColumnCount+count+(numberOfColumnsInsertedSignals-1));
1313 }
1314 else
1315 QCOMPARE(currentModel->rowCount(parentOfInserted), beforeInsertRowCount);
1316 }
1317 disconnect(sender: currentModel, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
1318 receiver: this, SLOT(slot_columnsAboutToInserted(QModelIndex)));
1319 disconnect(sender: currentModel, SIGNAL(columnsInserted(QModelIndex,int,int)),
1320 receiver: this, SLOT(slot_columnsInserted(QModelIndex)));
1321
1322 if (columnsAboutToBeInsertedSpy.count() > 0){
1323 QList<QVariant> arguments = columnsAboutToBeInsertedSpy.at(i: 0);
1324 QModelIndex parent = (qvariant_cast<QModelIndex>(v: arguments.at(i: 0)));
1325 int first = arguments.at(i: 1).toInt();
1326 int last = arguments.at(i: 2).toInt();
1327 QCOMPARE(first, start);
1328 QCOMPARE(last, start + count - 1);
1329 QVERIFY(parentOfInserted == parent);
1330 }
1331
1332 if (columnsInsertedSpy.count() > 0){
1333 QList<QVariant> arguments = columnsInsertedSpy.at(i: 0);
1334 QModelIndex parent = (qvariant_cast<QModelIndex>(v: arguments.at(i: 0)));
1335 int first = arguments.at(i: 1).toInt();
1336 int last = arguments.at(i: 2).toInt();
1337 QCOMPARE(first, start);
1338 QCOMPARE(last, start + count - 1);
1339 QVERIFY(parentOfInserted == parent);
1340 }
1341
1342 // Cleanup the test area because insert::() is called multiple times in a test
1343 testModels->cleanupTestArea(model: currentModel);
1344}
1345
1346void tst_QItemModel::slot_rowsAboutToInserted(const QModelIndex &parent)
1347{
1348 QVERIFY(parentOfInserted == parent);
1349 afterAboutToInsertRowCount = currentModel->rowCount(parent);
1350 bool hasChildren = currentModel->hasChildren(parent);
1351 bool hasDimensions = currentModel->columnCount(parent) > 0 && currentModel->rowCount(parent) > 0;
1352 QCOMPARE(hasChildren, hasDimensions);
1353 verifyState(currentModel);
1354
1355 // This does happen
1356 if (insertRecursively) {
1357 QFETCH(int, recursiveRow);
1358 QFETCH(int, recursiveCount);
1359 insertRecursively = false;
1360 QCOMPARE(currentModel->insertRows(recursiveRow, recursiveCount, parent), true);
1361 }
1362}
1363
1364void tst_QItemModel::slot_rowsInserted(const QModelIndex &parent)
1365{
1366 QVERIFY(parentOfInserted == parent);
1367 afterInsertRowCount = currentModel->rowCount(parent);
1368 bool hasChildren = currentModel->hasChildren(parent);
1369 bool hasDimensions = currentModel->columnCount(parent) > 0 && currentModel->rowCount(parent) > 0;
1370 QCOMPARE(hasChildren, hasDimensions);
1371 verifyState(currentModel);
1372}
1373
1374void tst_QItemModel::slot_columnsAboutToInserted(const QModelIndex &parent)
1375{
1376 QVERIFY(parentOfInserted == parent);
1377 afterAboutToInsertColumnCount = currentModel->columnCount(parent);
1378 bool hasChildren = currentModel->hasChildren(parent);
1379 bool hasDimensions = currentModel->columnCount(parent) > 0 && currentModel->rowCount(parent) > 0;
1380 QCOMPARE(hasChildren, hasDimensions);
1381 verifyState(currentModel);
1382}
1383
1384void tst_QItemModel::slot_columnsInserted(const QModelIndex &parent)
1385{
1386 QVERIFY(parentOfInserted == parent);
1387 afterInsertColumnCount = currentModel->columnCount(parent);
1388 bool hasChildren = currentModel->hasChildren(parent);
1389 bool hasDimensions = currentModel->columnCount(parent) > 0 && currentModel->rowCount(parent) > 0;
1390 QCOMPARE(hasChildren, hasDimensions);
1391 verifyState(currentModel);
1392}
1393
1394QTEST_MAIN(tst_QItemModel)
1395#include "tst_qitemmodel.moc"
1396

source code of qtbase/tests/auto/corelib/itemmodels/qitemmodel/tst_qitemmodel.cpp