Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qabstractitemmodeltester.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
6
7#include <private/qobject_p.h>
8#include <private/qabstractitemmodel_p.h>
9#include <QtCore/QPointer>
10#include <QtCore/QAbstractItemModel>
11#include <QtCore/QStack>
12#include <QTest>
13#include <QLoggingCategory>
14
16
17Q_LOGGING_CATEGORY(lcModelTest, "qt.modeltest")
18
19#define MODELTESTER_VERIFY(statement) \
20do { \
21 if (!verify(static_cast<bool>(statement), #statement, "", __FILE__, __LINE__)) \
22 return; \
23} while (false)
24
25#define MODELTESTER_COMPARE(actual, expected) \
26do { \
27 if (!compare((actual), (expected), #actual, #expected, __FILE__, __LINE__)) \
28 return; \
29} while (false)
30
32{
33 Q_DECLARE_PUBLIC(QAbstractItemModelTester)
34public:
36
38 void rowAndColumnCount();
39 void hasIndex();
40 void index();
41 void parent();
42 void data();
43
44 void runAllTests();
45
47 void layoutChanged();
48
50 void modelReset();
51
52 void columnsAboutToBeInserted(const QModelIndex &parent, int first, int last);
53 void columnsInserted(const QModelIndex &parent, int first, int last);
54 void columnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd,
55 const QModelIndex &destinationParent, int destinationColumn);
56 void columnsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination,
57 int column);
58 void columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last);
59 void columnsRemoved(const QModelIndex &parent, int first, int last);
60
61 void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end);
62 void rowsInserted(const QModelIndex &parent, int start, int end);
63 void rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd,
64 const QModelIndex &destinationParent, int destinationRow);
65 void rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination,
66 int row);
67 void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
68 void rowsRemoved(const QModelIndex &parent, int start, int end);
69 void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
70 void headerDataChanged(Qt::Orientation orientation, int start, int end);
71
72private:
73 void checkChildren(const QModelIndex &parent, int currentDepth = 0);
74
75 bool verify(bool statement, const char *statementStr, const char *description, const char *file, int line);
76
77 template<typename T1, typename T2>
78 bool compare(const T1 &t1, const T2 &t2,
79 const char *actual, const char *expected,
80 const char *file, int line);
81
82 QPointer<QAbstractItemModel> model;
84
85 struct Changing {
86 QModelIndex parent;
87 int oldSize;
88 QVariant last;
89 QVariant next;
90 };
91 QStack<Changing> insert;
92 QStack<Changing> remove;
93
94 bool useFetchMore = true;
95 bool fetchingMore;
96
97 enum class ChangeInFlight {
98 None,
99 ColumnsInserted,
100 ColumnsMoved,
101 ColumnsRemoved,
102 LayoutChanged,
103 ModelReset,
104 RowsInserted,
105 RowsMoved,
106 RowsRemoved
107 };
108 ChangeInFlight changeInFlight = ChangeInFlight::None;
109
110 QList<QPersistentModelIndex> changing;
111};
112
202
211{
212 if (!model)
213 qFatal("%s: model must not be null", Q_FUNC_INFO);
214
216
217 auto runAllTests = [d] { d->runAllTests(); };
218
220 this, runAllTests);
222 this, runAllTests);
224 this, runAllTests);
226 this, runAllTests);
228 this, runAllTests);
230 this, runAllTests);
232 this, runAllTests);
234 this, runAllTests);
236 this, runAllTests);
238 this, runAllTests);
240 this, runAllTests);
242 this, runAllTests);
244 this, runAllTests);
245
246 // Special checks for changes
248 this, [d]{ d->layoutAboutToBeChanged(); });
250 this, [d]{ d->layoutChanged(); });
251
252 // column operations
254 this, [d](const QModelIndex &parent, int start, int end) { d->columnsAboutToBeInserted(parent, start, end); });
256 this, [d](const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn) {
257 d->columnsAboutToBeMoved(sourceParent, sourceStart, sourceEnd, destinationParent, destinationColumn); });
259 this, [d](const QModelIndex &parent, int start, int end) { d->columnsAboutToBeRemoved(parent, start, end); });
261 this, [d](const QModelIndex &parent, int start, int end) { d->columnsInserted(parent, start, end); });
263 this, [d](const QModelIndex &parent, int start, int end, const QModelIndex &destination, int col) {
266 this, [d](const QModelIndex &parent, int start, int end) { d->columnsRemoved(parent, start, end); });
267
268 // row operations
270 this, [d](const QModelIndex &parent, int start, int end) { d->rowsAboutToBeInserted(parent, start, end); });
272 this, [d](const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) {
273 d->rowsAboutToBeMoved(sourceParent, sourceStart, sourceEnd, destinationParent, destinationRow); });
275 this, [d](const QModelIndex &parent, int start, int end) { d->rowsAboutToBeRemoved(parent, start, end); });
277 this, [d](const QModelIndex &parent, int start, int end) { d->rowsInserted(parent, start, end); });
279 this, [d](const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row) {
282 this, [d](const QModelIndex &parent, int start, int end) { d->rowsRemoved(parent, start, end); });
283
284 // reset
286 this, [d]() { d->modelAboutToBeReset(); });
288 this, [d]() { d->modelReset(); });
289
290 // data
292 this, [d](const QModelIndex &topLeft, const QModelIndex &bottomRight) { d->dataChanged(topLeft, bottomRight); });
294 this, [d](Qt::Orientation orientation, int start, int end) { d->headerDataChanged(orientation, start, end); });
295
296 runAllTests();
297}
298
303{
304 Q_D(const QAbstractItemModelTester);
305 return d->model.data();
306}
307
318
328{
330 d->useFetchMore = value;
331}
332
333bool QAbstractItemModelTester::verify(bool statement, const char *statementStr, const char *description, const char *file, int line)
334{
336 return d->verify(statement, statementStr, description, file, line);
337}
338
340 : model(model),
341 failureReportingMode(failureReportingMode),
342 fetchingMore(false)
343{
344}
345
347{
348 if (fetchingMore)
349 return;
352 hasIndex();
353 index();
354 parent();
355 data();
356}
357
358/*
359 nonDestructiveBasicTest tries to call a number of the basic functions (not all)
360 to make sure the model doesn't outright segfault, testing the functions that makes sense.
361*/
363{
364 MODELTESTER_VERIFY(!model->buddy(QModelIndex()).isValid());
365 model->canFetchMore(QModelIndex());
367 if (useFetchMore) {
368 fetchingMore = true;
369 model->fetchMore(QModelIndex());
370 fetchingMore = false;
371 }
372 Qt::ItemFlags flags = model->flags(QModelIndex());
374 model->hasChildren(QModelIndex());
375 const bool hasRow = model->hasIndex(0, 0);
377 if (hasRow)
378 model->match(model->index(0, 0), -1, cache);
379 model->mimeTypes();
380 MODELTESTER_VERIFY(!model->parent(QModelIndex()).isValid());
381 MODELTESTER_VERIFY(model->rowCount() >= 0);
382 model->span(QModelIndex());
383 model->supportedDropActions();
384 model->roleNames();
385}
386
387/*
388 Tests model's implementation of QAbstractItemModel::rowCount(),
389 columnCount() and hasChildren().
390
391 Models that are dynamically populated are not as fully tested here.
392 */
394{
395 if (!model->hasChildren())
396 return;
397
398 QModelIndex topIndex = model->index(0, 0, QModelIndex());
399
400 // check top row
401 int rows = model->rowCount(topIndex);
402 MODELTESTER_VERIFY(rows >= 0);
403
404 int columns = model->columnCount(topIndex);
405 MODELTESTER_VERIFY(columns >= 0);
406
407 if (rows == 0 || columns == 0)
408 return;
409
410 MODELTESTER_VERIFY(model->hasChildren(topIndex));
411
412 QModelIndex secondLevelIndex = model->index(0, 0, topIndex);
413 MODELTESTER_VERIFY(secondLevelIndex.isValid());
414
415 rows = model->rowCount(secondLevelIndex);
416 MODELTESTER_VERIFY(rows >= 0);
417
418 columns = model->columnCount(secondLevelIndex);
419 MODELTESTER_VERIFY(columns >= 0);
420
421 if (rows == 0 || columns == 0)
422 return;
423
424 MODELTESTER_VERIFY(model->hasChildren(secondLevelIndex));
425
426 // rowCount() / columnCount() are tested more extensively in checkChildren()
427}
428
429/*
430 Tests model's implementation of QAbstractItemModel::hasIndex()
431 */
433{
434 // Make sure that invalid values returns an invalid index
435 MODELTESTER_VERIFY(!model->hasIndex(-2, -2));
436 MODELTESTER_VERIFY(!model->hasIndex(-2, 0));
437 MODELTESTER_VERIFY(!model->hasIndex(0, -2));
438
439 const int rows = model->rowCount();
440 const int columns = model->columnCount();
441
442 // check out of bounds
443 MODELTESTER_VERIFY(!model->hasIndex(rows, columns));
444 MODELTESTER_VERIFY(!model->hasIndex(rows + 1, columns + 1));
445
446 if (rows > 0 && columns > 0)
447 MODELTESTER_VERIFY(model->hasIndex(0, 0));
448
449 // hasIndex() is tested more extensively in checkChildren(),
450 // but this catches the big mistakes
451}
452
453/*
454 Tests model's implementation of QAbstractItemModel::index()
455 */
457{
458 const int rows = model->rowCount();
459 const int columns = model->columnCount();
460
461 for (int row = 0; row < rows; ++row) {
462 for (int column = 0; column < columns; ++column) {
463 // Make sure that the same index is *always* returned
464 QModelIndex a = model->index(row, column);
465 QModelIndex b = model->index(row, column);
466 MODELTESTER_VERIFY(a.isValid());
467 MODELTESTER_VERIFY(b.isValid());
469 }
470 }
471
472 // index() is tested more extensively in checkChildren(),
473 // but this catches the big mistakes
474}
475
476/*
477 Tests model's implementation of QAbstractItemModel::parent()
478 */
480{
481 // Make sure the model won't crash and will return an invalid QModelIndex
482 // when asked for the parent of an invalid index.
483 MODELTESTER_VERIFY(!model->parent(QModelIndex()).isValid());
484
485 if (model->rowCount() == 0 || model->columnCount() == 0)
486 return;
487
488 // Column 0 | Column 1 |
489 // QModelIndex() | |
490 // \- topIndex | topIndex1 |
491 // \- childIndex | childIndex1 |
492
493 // Common error test #1, make sure that a top level index has a parent
494 // that is a invalid QModelIndex.
495 QModelIndex topIndex = model->index(0, 0, QModelIndex());
496 MODELTESTER_VERIFY(topIndex.isValid());
497 MODELTESTER_VERIFY(!model->parent(topIndex).isValid());
498
499 // Common error test #2, make sure that a second level index has a parent
500 // that is the first level index.
501 if (model->rowCount(topIndex) > 0 && model->columnCount(topIndex) > 0) {
502 QModelIndex childIndex = model->index(0, 0, topIndex);
503 MODELTESTER_VERIFY(childIndex.isValid());
504 MODELTESTER_COMPARE(model->parent(childIndex), topIndex);
505 }
506
507 // Common error test #3, the second column should NOT have the same children
508 // as the first column in a row.
509 // Usually the second column shouldn't have children.
510 if (model->hasIndex(0, 1)) {
511 QModelIndex topIndex1 = model->index(0, 1, QModelIndex());
512 MODELTESTER_VERIFY(topIndex1.isValid());
513 if (model->rowCount(topIndex) > 0 && model->rowCount(topIndex1) > 0) {
514 QModelIndex childIndex = model->index(0, 0, topIndex);
515 MODELTESTER_VERIFY(childIndex.isValid());
516 QModelIndex childIndex1 = model->index(0, 0, topIndex1);
517 MODELTESTER_VERIFY(childIndex1.isValid());
518 MODELTESTER_VERIFY(childIndex != childIndex1);
519 }
520 }
521
522 // Full test, walk n levels deep through the model making sure that all
523 // parent's children correctly specify their parent.
524 checkChildren(QModelIndex());
525}
526
527/*
528 Called from the parent() test.
529
530 A model that returns an index of parent X should also return X when asking
531 for the parent of the index.
532
533 This recursive function does pretty extensive testing on the whole model in an
534 effort to catch edge cases.
535
536 This function assumes that rowCount(), columnCount() and index() already work.
537 If they have a bug it will point it out, but the above tests should have already
538 found the basic bugs because it is easier to figure out the problem in
539 those tests then this one.
540 */
541void QAbstractItemModelTesterPrivate::checkChildren(const QModelIndex &parent, int currentDepth)
542{
543 // First just try walking back up the tree.
545 while (p.isValid())
546 p = p.parent();
547
548 // For models that are dynamically populated
549 if (model->canFetchMore(parent) && useFetchMore) {
550 fetchingMore = true;
551 model->fetchMore(parent);
552 fetchingMore = false;
553 }
554
555 const int rows = model->rowCount(parent);
556 const int columns = model->columnCount(parent);
557
558 if (rows > 0)
560
561 // Some further testing against rows(), columns(), and hasChildren()
562 MODELTESTER_VERIFY(rows >= 0);
563 MODELTESTER_VERIFY(columns >= 0);
564 if (rows > 0 && columns > 0)
566
567 const QModelIndex topLeftChild = model->index(0, 0, parent);
568
569 MODELTESTER_VERIFY(!model->hasIndex(rows, 0, parent));
570 MODELTESTER_VERIFY(!model->hasIndex(rows + 1, 0, parent));
571
572 for (int r = 0; r < rows; ++r) {
573 MODELTESTER_VERIFY(!model->hasIndex(r, columns, parent));
574 MODELTESTER_VERIFY(!model->hasIndex(r, columns + 1, parent));
575 for (int c = 0; c < columns; ++c) {
577 QModelIndex index = model->index(r, c, parent);
578 // rowCount() and columnCount() said that it existed...
579 if (!index.isValid())
580 qCWarning(lcModelTest) << "Got invalid index at row=" << r << "col=" << c << "parent=" << parent;
581 MODELTESTER_VERIFY(index.isValid());
582
583 // index() should always return the same index when called twice in a row
584 QModelIndex modifiedIndex = model->index(r, c, parent);
585 MODELTESTER_COMPARE(index, modifiedIndex);
586
587 {
588 const QModelIndex sibling = model->sibling(r, c, topLeftChild);
589 MODELTESTER_COMPARE(index, sibling);
590 }
591 {
592 const QModelIndex sibling = topLeftChild.sibling(r, c);
593 MODELTESTER_COMPARE(index, sibling);
594 }
595
596 // Some basic checking on the index that is returned
597 MODELTESTER_COMPARE(index.model(), model);
599 MODELTESTER_COMPARE(index.column(), c);
600
601 // If the next test fails here is some somewhat useful debug you play with.
602 if (model->parent(index) != parent) {
603 qCWarning(lcModelTest) << "Inconsistent parent() implementation detected:";
604 qCWarning(lcModelTest) << " index=" << index << "exp. parent=" << parent << "act. parent=" << model->parent(index);
605 qCWarning(lcModelTest) << " row=" << r << "col=" << c << "depth=" << currentDepth;
606 qCWarning(lcModelTest) << " data for child" << model->data(index).toString();
607 qCWarning(lcModelTest) << " data for parent" << model->data(parent).toString();
608 }
609
610 // Check that we can get back our real parent.
612
613 QPersistentModelIndex persistentIndex = index;
614
615 // recursively go down the children
616 if (model->hasChildren(index) && currentDepth < 10)
617 checkChildren(index, currentDepth + 1);
618
619 // make sure that after testing the children that the index doesn't change.
620 QModelIndex newerIndex = model->index(r, c, parent);
621 MODELTESTER_COMPARE(persistentIndex, newerIndex);
622 }
623 }
624}
625
626/*
627 Tests model's implementation of QAbstractItemModel::data()
628 */
630{
631 if (model->rowCount() == 0 || model->columnCount() == 0)
632 return;
633
634 MODELTESTER_VERIFY(model->index(0, 0).isValid());
635
636 // General Purpose roles that should return a QString
638 variant = model->data(model->index(0, 0), Qt::DisplayRole);
639 if (variant.isValid())
641 variant = model->data(model->index(0, 0), Qt::ToolTipRole);
642 if (variant.isValid())
644 variant = model->data(model->index(0, 0), Qt::StatusTipRole);
645 if (variant.isValid())
647 variant = model->data(model->index(0, 0), Qt::WhatsThisRole);
648 if (variant.isValid())
650
651 // General Purpose roles that should return a QSize
652 variant = model->data(model->index(0, 0), Qt::SizeHintRole);
653 if (variant.isValid())
655
656 // Check that the alignment is one we know about
657 QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole);
658 if (textAlignmentVariant.isValid()) {
659 Qt::Alignment alignment = QtPrivate::legacyFlagValueFromModelData<Qt::Alignment>(textAlignmentVariant);
661 }
662
663 // Check that the "check state" is one we know about.
664 QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole);
665 if (checkStateVariant.isValid()) {
666 Qt::CheckState state = QtPrivate::legacyEnumValueFromModelData<Qt::CheckState>(checkStateVariant);
669 || state == Qt::Checked);
670 }
671
673
675 return;
676}
677
679 int end)
680{
681 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
682 changeInFlight = ChangeInFlight::ColumnsInserted;
683
684 qCDebug(lcModelTest) << "columnsAboutToBeInserted"
685 << "start=" << start << "end=" << end << "parent=" << parent
686 << "parent data=" << model->data(parent).toString()
687 << "current count of parent=" << model->rowCount(parent)
688 << "last before insertion=" << model->index(start - 1, 0, parent)
689 << model->data(model->index(start - 1, 0, parent));
690}
691
693 int last)
694{
695 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::ColumnsInserted);
696 changeInFlight = ChangeInFlight::None;
697
698 qCDebug(lcModelTest) << "columnsInserted"
699 << "start=" << first << "end=" << last << "parent=" << parent
700 << "parent data=" << model->data(parent).toString()
701 << "current count of parent=" << model->rowCount(parent);
702}
703
705 int sourceStart, int sourceEnd,
706 const QModelIndex &destinationParent,
707 int destinationColumn)
708{
709 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
710 changeInFlight = ChangeInFlight::ColumnsMoved;
711
712 qCDebug(lcModelTest) << "columnsAboutToBeMoved"
713 << "sourceStart=" << sourceStart << "sourceEnd=" << sourceEnd
714 << "sourceParent=" << sourceParent
715 << "sourceParent data=" << model->data(sourceParent).toString()
716 << "destinationParent=" << destinationParent
717 << "destinationColumn=" << destinationColumn;
718}
719
720void QAbstractItemModelTesterPrivate::columnsMoved(const QModelIndex &sourceParent, int sourceStart,
721 int sourceEnd,
722 const QModelIndex &destinationParent,
723 int destinationColumn)
724{
725 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::ColumnsMoved);
726 changeInFlight = ChangeInFlight::None;
727
728 qCDebug(lcModelTest) << "columnsMoved"
729 << "sourceStart=" << sourceStart << "sourceEnd=" << sourceEnd
730 << "sourceParent=" << sourceParent
731 << "sourceParent data=" << model->data(sourceParent).toString()
732 << "destinationParent=" << destinationParent
733 << "destinationColumn=" << destinationColumn;
734}
735
737 int last)
738{
739 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
740 changeInFlight = ChangeInFlight::ColumnsRemoved;
741
742 qCDebug(lcModelTest) << "columnsAboutToBeRemoved"
743 << "start=" << first << "end=" << last << "parent=" << parent
744 << "parent data=" << model->data(parent).toString()
745 << "current count of parent=" << model->rowCount(parent)
746 << "last before removal=" << model->index(first - 1, 0, parent)
747 << model->data(model->index(first - 1, 0, parent));
748}
749
751{
752 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::ColumnsRemoved);
753 changeInFlight = ChangeInFlight::None;
754
755 qCDebug(lcModelTest) << "columnsRemoved"
756 << "start=" << first << "end=" << last << "parent=" << parent
757 << "parent data=" << model->data(parent).toString()
758 << "current count of parent=" << model->rowCount(parent);
759}
760
761/*
762 Store what is about to be inserted to make sure it actually happens
763
764 \sa rowsInserted()
765 */
767{
768 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
769 changeInFlight = ChangeInFlight::RowsInserted;
770
771 qCDebug(lcModelTest) << "rowsAboutToBeInserted"
772 << "start=" << start << "end=" << end << "parent=" << parent
773 << "parent data=" << model->data(parent).toString()
774 << "current count of parent=" << model->rowCount(parent)
775 << "last before insertion=" << model->index(start - 1, 0, parent) << model->data(model->index(start - 1, 0, parent));
776
777 Changing c;
778 c.parent = parent;
779 c.oldSize = model->rowCount(parent);
780 c.last = (start - 1 >= 0) ? model->index(start - 1, 0, parent).data() : QVariant();
781 c.next = (start < c.oldSize) ? model->index(start, 0, parent).data() : QVariant();
782 insert.push(c);
783}
784
785/*
786 Confirm that what was said was going to happen actually did
787
788 \sa rowsAboutToBeInserted()
789 */
791{
792 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::RowsInserted);
793 changeInFlight = ChangeInFlight::None;
794
795 qCDebug(lcModelTest) << "rowsInserted"
796 << "start=" << start << "end=" << end << "parent=" << parent
797 << "parent data=" << model->data(parent).toString()
798 << "current count of parent=" << model->rowCount(parent);
799
800 for (int i = start; i <= end; ++i) {
801 qCDebug(lcModelTest) << " itemWasInserted:" << i
802 << model->index(i, 0, parent).data();
803 }
804
805
806 Changing c = insert.pop();
808
809 MODELTESTER_COMPARE(model->rowCount(parent), c.oldSize + (end - start + 1));
810 if (start - 1 >= 0)
811 MODELTESTER_COMPARE(model->data(model->index(start - 1, 0, c.parent)), c.last);
812
813 if (end + 1 < model->rowCount(c.parent)) {
814 if (c.next != model->data(model->index(end + 1, 0, c.parent))) {
815 qDebug() << start << end;
816 for (int i = 0; i < model->rowCount(); ++i)
817 qDebug() << model->index(i, 0).data().toString();
818 qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent));
819 }
820
821 MODELTESTER_COMPARE(model->data(model->index(end + 1, 0, c.parent)), c.next);
822 }
823}
824
826 int sourceStart, int sourceEnd,
827 const QModelIndex &destinationParent,
828 int destinationRow)
829{
830 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
831 changeInFlight = ChangeInFlight::RowsMoved;
832
833 qCDebug(lcModelTest) << "rowsAboutToBeMoved"
834 << "sourceStart=" << sourceStart << "sourceEnd=" << sourceEnd
835 << "sourceParent=" << sourceParent
836 << "sourceParent data=" << model->data(sourceParent).toString()
837 << "destinationParent=" << destinationParent
838 << "destinationRow=" << destinationRow;
839}
840
841void QAbstractItemModelTesterPrivate::rowsMoved(const QModelIndex &sourceParent, int sourceStart,
842 int sourceEnd, const QModelIndex &destinationParent,
843 int destinationRow)
844{
845 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::RowsMoved);
846 changeInFlight = ChangeInFlight::None;
847
848 qCDebug(lcModelTest) << "rowsMoved"
849 << "sourceStart=" << sourceStart << "sourceEnd=" << sourceEnd
850 << "sourceParent=" << sourceParent
851 << "sourceParent data=" << model->data(sourceParent).toString()
852 << "destinationParent=" << destinationParent
853 << "destinationRow=" << destinationRow;
854}
855
857{
858 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
859 changeInFlight = ChangeInFlight::LayoutChanged;
860
861 for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i)
862 changing.append(QPersistentModelIndex(model->index(i, 0)));
863}
864
866{
867 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::LayoutChanged);
868 changeInFlight = ChangeInFlight::None;
869
870 for (int i = 0; i < changing.size(); ++i) {
871 QPersistentModelIndex p = changing[i];
872 MODELTESTER_COMPARE(model->index(p.row(), p.column(), p.parent()), QModelIndex(p));
873 }
874 changing.clear();
875}
876
878{
879 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
880 changeInFlight = ChangeInFlight::ModelReset;
881}
882
884{
885 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::ModelReset);
886 changeInFlight = ChangeInFlight::None;
887}
888
889/*
890 Store what is about to be inserted to make sure it actually happens
891
892 \sa rowsRemoved()
893 */
895{
896 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
897 changeInFlight = ChangeInFlight::RowsRemoved;
898
899 qCDebug(lcModelTest) << "rowsAboutToBeRemoved"
900 << "start=" << start << "end=" << end << "parent=" << parent
901 << "parent data=" << model->data(parent).toString()
902 << "current count of parent=" << model->rowCount(parent)
903 << "last before removal=" << model->index(start - 1, 0, parent) << model->data(model->index(start - 1, 0, parent));
904
905 Changing c;
906 c.parent = parent;
907 c.oldSize = model->rowCount(parent);
908 if (start > 0 && model->columnCount(parent) > 0) {
909 const QModelIndex startIndex = model->index(start - 1, 0, parent);
910 MODELTESTER_VERIFY(startIndex.isValid());
911 c.last = model->data(startIndex);
912 }
913 if (end < c.oldSize - 1 && model->columnCount(parent) > 0) {
914 const QModelIndex endIndex = model->index(end + 1, 0, parent);
915 MODELTESTER_VERIFY(endIndex.isValid());
916 c.next = model->data(endIndex);
917 }
918
919 remove.push(c);
920}
921
922/*
923 Confirm that what was said was going to happen actually did
924
925 \sa rowsAboutToBeRemoved()
926 */
928{
929 MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::RowsRemoved);
930 changeInFlight = ChangeInFlight::None;
931
932 qCDebug(lcModelTest) << "rowsRemoved"
933 << "start=" << start << "end=" << end << "parent=" << parent
934 << "parent data=" << model->data(parent).toString()
935 << "current count of parent=" << model->rowCount(parent);
936
937 Changing c = remove.pop();
939 MODELTESTER_COMPARE(model->rowCount(parent), c.oldSize - (end - start + 1));
940 if (start > 0)
941 MODELTESTER_COMPARE(model->data(model->index(start - 1, 0, c.parent)), c.last);
942 if (end < c.oldSize - 1)
943 MODELTESTER_COMPARE(model->data(model->index(start, 0, c.parent)), c.next);
944}
945
947{
948 MODELTESTER_VERIFY(topLeft.isValid());
949 MODELTESTER_VERIFY(bottomRight.isValid());
950 QModelIndex commonParent = bottomRight.parent();
951 MODELTESTER_COMPARE(topLeft.parent(), commonParent);
952 MODELTESTER_VERIFY(topLeft.row() <= bottomRight.row());
953 MODELTESTER_VERIFY(topLeft.column() <= bottomRight.column());
954 int rowCount = model->rowCount(commonParent);
955 int columnCount = model->columnCount(commonParent);
956 MODELTESTER_VERIFY(bottomRight.row() < rowCount);
957 MODELTESTER_VERIFY(bottomRight.column() < columnCount);
958}
959
961{
965 int itemCount = orientation == Qt::Vertical ? model->rowCount() : model->columnCount();
966 MODELTESTER_VERIFY(start < itemCount);
967 MODELTESTER_VERIFY(end < itemCount);
968}
969
970bool QAbstractItemModelTesterPrivate::verify(bool statement,
971 const char *statementStr, const char *description,
972 const char *file, int line)
973{
974 static const char formatString[] = "FAIL! %s (%s) returned FALSE (%s:%d)";
975
976 switch (failureReportingMode) {
978 return QTest::qVerify(statement, statementStr, description, file, line);
979 break;
980
982 if (!statement)
983 qCWarning(lcModelTest, formatString, statementStr, description, file, line);
984 break;
985
987 if (!statement)
988 qFatal(formatString, statementStr, description, file, line);
989 break;
990 }
991
992 return statement;
993}
994
995
996template<typename T1, typename T2>
997bool QAbstractItemModelTesterPrivate::compare(const T1 &t1, const T2 &t2,
998 const char *actual, const char *expected,
999 const char *file, int line)
1000{
1001 const bool result = static_cast<bool>(t1 == t2);
1002
1003 static const char formatString[] = "FAIL! Compared values are not the same:\n Actual (%s) %s\n Expected (%s) %s\n (%s:%d)";
1004
1005 switch (failureReportingMode) {
1007 return QTest::qCompare(t1, t2, actual, expected, file, line);
1008 break;
1009
1011 if (!result) {
1012 auto t1string = QTest::toString(t1);
1013 auto t2string = QTest::toString(t2);
1014 qCWarning(lcModelTest, formatString, actual, t1string ? t1string : "(nullptr)",
1015 expected, t2string ? t2string : "(nullptr)",
1016 file, line);
1017 delete [] t1string;
1018 delete [] t2string;
1019 }
1020 break;
1021
1023 if (!result) {
1024 auto t1string = QTest::toString(t1);
1025 auto t2string = QTest::toString(t2);
1026 qFatal(formatString, actual, t1string ? t1string : "(nullptr)",
1027 expected, t2string ? t2string : "(nullptr)",
1028 file, line);
1029 delete [] t1string;
1030 delete [] t2string;
1031 }
1032 break;
1033 }
1034
1035 return result;
1036}
1037
1038
1040
1041#include "moc_qabstractitemmodeltester.cpp"
void columnsAboutToBeInserted(const QModelIndex &parent, int first, int last)
void rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow)
void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
void rowsRemoved(const QModelIndex &parent, int start, int end)
void columnsRemoved(const QModelIndex &parent, int first, int last)
void columnsInserted(const QModelIndex &parent, int first, int last)
void rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
void columnsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column)
void columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
void columnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
void headerDataChanged(Qt::Orientation orientation, int start, int end)
void rowsInserted(const QModelIndex &parent, int start, int end)
void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
QAbstractItemModelTesterPrivate(QAbstractItemModel *model, QAbstractItemModelTester::FailureReportingMode failureReportingMode)
The QAbstractItemModelTester class helps testing QAbstractItemModel subclasses.
QAbstractItemModel * model() const
Returns the model that this instance is testing.
QAbstractItemModelTester(QAbstractItemModel *model, QObject *parent=nullptr)
Creates a model tester instance, with the given parent, that will test the model model.
FailureReportingMode failureReportingMode() const
Returns the mode that this instancing is using to report test failures.
void setUseFetchMore(bool value)
If value is true, enables dynamic population of the tested model, which is the default.
FailureReportingMode
This enumeration specifies how QAbstractItemModelTester should report a failure when it tests a QAbst...
virtual Qt::DropActions supportedDropActions() const
void rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow, QPrivateSignal)
Q_INVOKABLE int const QModelIndex & parent
Returns the parent of the model item with the given index.
void modelAboutToBeReset(QPrivateSignal)
virtual Q_INVOKABLE QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits=1, Qt::MatchFlags flags=Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const
Returns a list of indexes for the items in the column of the start index where data stored under the ...
void columnsRemoved(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted after columns have been removed from the model.
void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted just before rows are inserted into the model.
void columnsAboutToBeInserted(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted just before columns are inserted into the model.
Q_INVOKABLE bool hasIndex(int row, int column, const QModelIndex &parent=QModelIndex()) const
Returns {true} if the model returns a valid QModelIndex for row and column with parent,...
virtual Q_INVOKABLE Qt::ItemFlags flags(const QModelIndex &index) const
Returns the item flags for the given index.
void modelReset(QPrivateSignal)
void layoutAboutToBeChanged(const QList< QPersistentModelIndex > &parents=QList< QPersistentModelIndex >(), QAbstractItemModel::LayoutChangeHint hint=QAbstractItemModel::NoLayoutChangeHint)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles=QList< int >())
This signal is emitted whenever the data in an existing item changes.
virtual Q_INVOKABLE bool hasChildren(const QModelIndex &parent=QModelIndex()) const
Returns {true} if parent has any children; otherwise returns {false}.
virtual Q_INVOKABLE int rowCount(const QModelIndex &parent=QModelIndex()) const =0
Returns the number of rows under the given parent.
void columnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn, QPrivateSignal)
virtual Q_INVOKABLE void fetchMore(const QModelIndex &parent)
Fetches any available data for the items with the parent specified by the parent index.
void columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted just before columns are removed from the model.
void layoutChanged(const QList< QPersistentModelIndex > &parents=QList< QPersistentModelIndex >(), QAbstractItemModel::LayoutChangeHint hint=QAbstractItemModel::NoLayoutChangeHint)
virtual QModelIndex buddy(const QModelIndex &index) const
Returns a model index for the buddy of the item represented by index.
virtual Q_INVOKABLE bool canFetchMore(const QModelIndex &parent) const
Returns {true} if there is more data available for parent; otherwise returns {false}.
virtual Q_INVOKABLE QModelIndex sibling(int row, int column, const QModelIndex &idx) const
Returns the sibling at row and column for the item at index, or an invalid QModelIndex if there is no...
void headerDataChanged(Qt::Orientation orientation, int first, int last)
This signal is emitted whenever a header is changed.
void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted just before rows are removed from the model.
virtual QHash< int, QByteArray > roleNames() const
void rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow, QPrivateSignal)
virtual Q_INVOKABLE int columnCount(const QModelIndex &parent=QModelIndex()) const =0
Returns the number of columns for the children of the given parent.
void rowsInserted(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted after rows have been inserted into the model.
virtual QStringList mimeTypes() const
Returns the list of allowed MIME types.
void columnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn, QPrivateSignal)
void columnsInserted(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted after columns have been inserted into the model.
virtual QSize span(const QModelIndex &index) const
Returns the row and column span of the item represented by index.
virtual Q_INVOKABLE QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const =0
Returns the data stored under the given role for the item referred to by the index.
virtual Q_INVOKABLE QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const =0
Returns the index of the item in the model specified by the given row, column and parent index.
void rowsRemoved(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted after rows have been removed from the model.
qsizetype size() const noexcept
Definition qlist.h:397
void append(parameter_type t)
Definition qlist.h:458
void clear()
Definition qlist.h:434
\inmodule QtCore
constexpr int row() const noexcept
Returns the row this model index refers to.
QModelIndex parent() const
Returns the parent of the model index, or QModelIndex() if it has no parent.
constexpr int column() const noexcept
Returns the column this model index refers to.
constexpr bool isValid() const noexcept
Returns {true} if this model index is valid; otherwise returns {false}.
QModelIndex sibling(int row, int column) const
Returns the sibling at row and column.
\inmodule QtCore
Definition qobject.h:103
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
\inmodule QtCore
Definition qsize.h:25
T pop()
Removes the top item from the stack and returns it.
Definition qstack.h:18
void push(const T &t)
Adds element t to the top of the stack.
Definition qstack.h:17
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
\inmodule QtCore
Definition qvariant.h:65
bool isValid() const
Returns true if the storage type of this variant is not QMetaType::UnknownType; otherwise returns fal...
Definition qvariant.h:714
bool canConvert(QMetaType targetType) const
Definition qvariant.h:345
QJSValue expected
Definition qjsengine.cpp:12
QCache< int, Employee > cache
[0]
uint alignment
else opt state
[0]
Combined button and popup list for selecting options.
bool testDataGuiRoles(QAbstractItemModelTester *tester)
Q_TESTLIB_EXPORT bool qVerify(bool statement, const char *statementStr, const char *description, const char *file, int line)
char * toString(const MyPoint &point)
char * formatString(const char *prefix, const char *suffix, size_t numArguments,...)
bool qCompare(QString const &t1, QLatin1StringView const &t2, const char *actual, const char *expected, const char *file, int line)
Definition qtest.h:31
CheckState
@ Unchecked
@ Checked
@ PartiallyChecked
@ AlignHorizontal_Mask
Definition qnamespace.h:151
@ AlignVertical_Mask
Definition qnamespace.h:161
Orientation
Definition qnamespace.h:98
@ Vertical
Definition qnamespace.h:100
@ WhatsThisRole
@ TextAlignmentRole
@ CheckStateRole
@ StatusTipRole
@ ToolTipRole
@ DisplayRole
@ SizeHintRole
@ ItemIsDropEnabled
#define MODELTESTER_COMPARE(actual, expected)
#define MODELTESTER_VERIFY(statement)
#define Q_FUNC_INFO
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char * destination
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qDebug
[1]
Definition qlogging.h:164
#define qFatal
Definition qlogging.h:168
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
GLboolean GLboolean GLboolean b
GLenum mode
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
[4]
GLbitfield flags
GLuint start
GLint first
GLenum GLenum GLsizei void GLsizei void * column
const GLubyte * c
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLenum GLenum GLsizei void * row
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
#define t2
QSqlQueryModel * model
[16]
QFile file
[0]
QVariant variant
[1]